runem 0.5.0__tar.gz → 0.7.0__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.
- {runem-0.5.0 → runem-0.7.0}/HISTORY.md +119 -0
- runem-0.7.0/PKG-INFO +162 -0
- runem-0.7.0/README.md +117 -0
- {runem-0.5.0 → runem-0.7.0}/pyproject.toml +8 -6
- runem-0.7.0/runem/VERSION +1 -0
- {runem-0.5.0 → runem-0.7.0}/runem/blocking_print.py +10 -2
- {runem-0.5.0 → runem-0.7.0}/runem/cli/initialise_options.py +0 -1
- {runem-0.5.0 → runem-0.7.0}/runem/command_line.py +4 -7
- {runem-0.5.0 → runem-0.7.0}/runem/config.py +8 -4
- {runem-0.5.0 → runem-0.7.0}/runem/config_parse.py +2 -1
- runem-0.7.0/runem/config_validate.py +47 -0
- {runem-0.5.0 → runem-0.7.0}/runem/informative_dict.py +7 -2
- {runem-0.5.0 → runem-0.7.0}/runem/job.py +1 -3
- {runem-0.5.0 → runem-0.7.0}/runem/job_execute.py +12 -9
- {runem-0.5.0 → runem-0.7.0}/runem/job_filter.py +5 -5
- {runem-0.5.0 → runem-0.7.0}/runem/job_wrapper_python.py +3 -4
- runem-0.7.0/runem/log.py +46 -0
- {runem-0.5.0 → runem-0.7.0}/runem/report.py +8 -4
- {runem-0.5.0 → runem-0.7.0}/runem/run_command.py +62 -28
- {runem-0.5.0 → runem-0.7.0}/runem/runem.py +46 -64
- runem-0.7.0/runem/schema.yml +137 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/__init__.py +2 -1
- runem-0.7.0/runem/types/errors.py +14 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/hooks.py +1 -1
- {runem-0.5.0 → runem-0.7.0}/runem/types/types_jobs.py +21 -24
- runem-0.7.0/runem/utils.py +18 -0
- runem-0.7.0/runem/yaml_utils.py +19 -0
- runem-0.7.0/runem/yaml_validation.py +28 -0
- runem-0.7.0/runem.egg-info/PKG-INFO +162 -0
- {runem-0.5.0 → runem-0.7.0}/runem.egg-info/SOURCES.txt +6 -0
- {runem-0.5.0 → runem-0.7.0}/runem.egg-info/requires.txt +6 -6
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/py.py +63 -1
- runem-0.7.0/tests/intentional_test_error.py +8 -0
- runem-0.7.0/tests/test_config_validate.py +128 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_files.py +3 -3
- {runem-0.5.0 → runem-0.7.0}/tests/test_hook_manager.py +5 -4
- {runem-0.5.0 → runem-0.7.0}/tests/test_informative_dict.py +21 -20
- {runem-0.5.0 → runem-0.7.0}/tests/test_job_filter.py +0 -2
- {runem-0.5.0 → runem-0.7.0}/tests/test_job_runner_simple_command.py +2 -1
- {runem-0.5.0 → runem-0.7.0}/tests/test_job_wrapper.py +2 -3
- {runem-0.5.0 → runem-0.7.0}/tests/test_run_command.py +19 -20
- {runem-0.5.0 → runem-0.7.0}/tests/test_runem.py +18 -16
- runem-0.7.0/tests/test_yaml_validation.py +66 -0
- runem-0.5.0/PKG-INFO +0 -164
- runem-0.5.0/README.md +0 -120
- runem-0.5.0/runem/VERSION +0 -1
- runem-0.5.0/runem/log.py +0 -24
- runem-0.5.0/runem/types/errors.py +0 -4
- runem-0.5.0/runem/utils.py +0 -6
- runem-0.5.0/runem.egg-info/PKG-INFO +0 -164
- runem-0.5.0/tests/intentional_test_error.py +0 -5
- {runem-0.5.0 → runem-0.7.0}/Containerfile +0 -0
- {runem-0.5.0 → runem-0.7.0}/LICENSE +0 -0
- {runem-0.5.0 → runem-0.7.0}/MANIFEST.in +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/__init__.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/__main__.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/base.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/cli.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/config_metadata.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/files.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/hook_manager.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/job_runner_simple_command.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/job_wrapper.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/py.typed +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/runem_version.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/common.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/filters.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/options.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem/types/runem_config.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem.egg-info/dependency_links.txt +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem.egg-info/entry_points.txt +0 -0
- {runem-0.5.0 → runem-0.7.0}/runem.egg-info/top_level.txt +0 -0
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/__init__.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/json_validators.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/py.typed +0 -0
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/runem_hooks.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/scripts/test_hooks/yarn.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/setup.cfg +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/__init__.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/cli/test_initialise_options.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/conftest.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/data/help_output.3.10.txt +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/data/help_output.3.11.txt +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/sanitise_reports_footer.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_base.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_blocking_print.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_cli.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_config.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_config_parse.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_job.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_job_execute.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_job_wrapper_python.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_report.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_types/__init__.py +0 -0
- {runem-0.5.0 → runem-0.7.0}/tests/test_types/test_public_api.py +0 -0
@@ -4,6 +4,125 @@ Changelog
|
|
4
4
|
|
5
5
|
(unreleased)
|
6
6
|
------------
|
7
|
+
- Merge pull request #83 from
|
8
|
+
lursight/feat/no_runem_traceback_on_job_fail. [Frank Harrison]
|
9
|
+
|
10
|
+
Feat/no runem traceback on job fail
|
11
|
+
- Feat(error-only): only shows the error not the runem error tracback.
|
12
|
+
[Frank Harrison]
|
13
|
+
|
14
|
+
The runem error traceback is irelevant if the sub-task fails. So we just
|
15
|
+
show that task's output instead of the traceback for where we handle the
|
16
|
+
error.
|
17
|
+
- Feat(error-ctx): shows the failed job label as we show the causing
|
18
|
+
error. [Frank Harrison]
|
19
|
+
- Feat(remove-failed): always remove the failed job from the list of
|
20
|
+
running jobs. [Frank Harrison]
|
21
|
+
- Merge pull request #82 from lursight/feat/schema_validation. [Frank
|
22
|
+
Harrison]
|
23
|
+
|
24
|
+
feat(validation): validates the .runem.yml file against the schema
|
25
|
+
- Feat(validation): validates the .runem.yml file against the schema.
|
26
|
+
[Frank Harrison]
|
27
|
+
- Merge pull request #81 from lursight/chore/ruff. [Frank Harrison]
|
28
|
+
|
29
|
+
Chore/ruff
|
30
|
+
- Chore(ruff): some formatting change made whilst configuring ruff.
|
31
|
+
[Frank Harrison]
|
32
|
+
- Chore(ruff): use ruff as its faster/better. [Frank Harrison]
|
33
|
+
- Merge pull request #80 from lursight/chore/update_deps. [Frank
|
34
|
+
Harrison]
|
35
|
+
|
36
|
+
Chore/update deps
|
37
|
+
- Chore(deps): updates pylint 3.1.0 -> 3.3.6. [Frank Harrison]
|
38
|
+
- Chore(deps): updates pytest 8.3.3 -> 8.3.5 and pytest-cov to latest.
|
39
|
+
[Frank Harrison]
|
40
|
+
- Merge pull request #79 from
|
41
|
+
lursight/feat/removes_dectorate_param_from_log. [Frank Harrison]
|
42
|
+
|
43
|
+
feat(log): changes the semantics of log's 'decorate' to allow overriding
|
44
|
+
- Feat(log): changes the semantics of log's 'decorate' to allow
|
45
|
+
overriding. [Frank Harrison]
|
46
|
+
|
47
|
+
Also renames the log API's param decorate -> prefix.
|
48
|
+
|
49
|
+
This better represents the intent of the param as decorate was adding a
|
50
|
+
default prefix.
|
51
|
+
|
52
|
+
|
53
|
+
0.6.0 (2025-02-03)
|
54
|
+
------------------
|
55
|
+
- Release: version 0.6.0 🚀 [Frank Harrison]
|
56
|
+
- Merge pull request #78 from lursight/feat/better_error_display. [Frank
|
57
|
+
Harrison]
|
58
|
+
|
59
|
+
Feat/better error display
|
60
|
+
- Fix(spinner): fixes the Spinner show it only shows for --show-spinner.
|
61
|
+
[Frank Harrison]
|
62
|
+
|
63
|
+
Also, fixes it so that it shows only a single spinner
|
64
|
+
- Feat(colours): adds better colours to the terminal output. [Frank
|
65
|
+
Harrison]
|
66
|
+
|
67
|
+
This helps to reinfoce time-saved and other aspects of the tool.
|
68
|
+
- Feat(better-errors): makes finding the stderr in the list of errors
|
69
|
+
easier. [Frank Harrison]
|
70
|
+
|
71
|
+
We do this by colouring the text for the commands and wrapping the
|
72
|
+
errors with a red-box.
|
73
|
+
|
74
|
+
We colour:
|
75
|
+
- command-lines -> yellow
|
76
|
+
- job-labels -> blue
|
77
|
+
- errors -> red
|
78
|
+
- in-progress -> green box
|
79
|
+
- Merge pull request #77 from lursight/fix/log-verbosity. [Frank
|
80
|
+
Harrison]
|
81
|
+
|
82
|
+
fix(log-verbosity): fixes verbosity bug when not showing the spinner
|
83
|
+
- Fix(log-verbosity): fixes verbosity bug when not showing the spinner.
|
84
|
+
[Frank Harrison]
|
85
|
+
|
86
|
+
We were showing the running procs on every tick, instead of just the
|
87
|
+
changes to the running procs, if any.
|
88
|
+
- Merge pull request #76 from lursight/chore/types/job-return-type.
|
89
|
+
[Frank Harrison]
|
90
|
+
|
91
|
+
chore(types) fixes exports for JobReturn type
|
92
|
+
- Chore(types): exports the JobReturn type from the types submodule.
|
93
|
+
[Frank Harrison]
|
94
|
+
- Merge pull request #75 from lursight/chore/todos. [Frank Harrison]
|
95
|
+
|
96
|
+
chore(todos): adds TODOD.txt to track ideas for runem
|
97
|
+
- Chore(todos): adds TODOD.txt to track ideas for runem. [Frank
|
98
|
+
Harrison]
|
99
|
+
- Merge pull request #74 from lursight/chore/help_docs. [Frank Harrison]
|
100
|
+
|
101
|
+
chore(docs): removes help output from a details block
|
102
|
+
- Chore(docs): removes help output from a details block. [Frank
|
103
|
+
Harrison]
|
104
|
+
|
105
|
+
The details block broke the pre-formatted styling of the code-block.
|
106
|
+
- Merge pull request #73 from lursight/chore/update_contrib. [Frank
|
107
|
+
Harrison]
|
108
|
+
|
109
|
+
chore(docs): trying to add line-breaks to non-bulletpointed list
|
110
|
+
- Chore(docs): trying to add line-breaks to non-bulletpointed list.
|
111
|
+
[Frank Harrison]
|
112
|
+
- Merge pull request #72 from lursight/chore/update_contrib. [Frank
|
113
|
+
Harrison]
|
114
|
+
|
115
|
+
Chore/update CONTRIBUTING.md and README.md
|
116
|
+
- Chore(spell): adds 'pyenv' to dictionary. [Frank Harrison]
|
117
|
+
- Chore(docs): improves the README. [Frank Harrison]
|
118
|
+
- Docs(contrib): updates the contributing docs. [Frank Harrison]
|
119
|
+
|
120
|
+
We add some of the basics as well as some more recent changes
|
121
|
+
|
122
|
+
|
123
|
+
0.5.0 (2024-12-12)
|
124
|
+
------------------
|
125
|
+
- Release: version 0.5.0 🚀 [Frank Harrison]
|
7
126
|
- Merge pull request #71 from lursight/feat/pyproject. [Frank Harrison]
|
8
127
|
|
9
128
|
Feat/pyproject
|
runem-0.7.0/PKG-INFO
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: runem
|
3
|
+
Version: 0.7.0
|
4
|
+
Summary: Awesome runem created by lursight
|
5
|
+
Author: lursight
|
6
|
+
License: Specify your license here
|
7
|
+
Project-URL: Homepage, https://github.com/lursight/runem/
|
8
|
+
Keywords: example,runem
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Requires-Python: >=3.7
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
License-File: LICENSE
|
17
|
+
Requires-Dist: packaging>=22.0
|
18
|
+
Requires-Dist: PyYAML>=5.0.0
|
19
|
+
Requires-Dist: jsonschema>=4.22
|
20
|
+
Requires-Dist: rich>10.0.0
|
21
|
+
Requires-Dist: typing_extensions>3.0.0
|
22
|
+
Provides-Extra: tests
|
23
|
+
Requires-Dist: coverage==7.5; extra == "tests"
|
24
|
+
Requires-Dist: flake8-bugbear==24.2.6; extra == "tests"
|
25
|
+
Requires-Dist: flake8==7.0.0; extra == "tests"
|
26
|
+
Requires-Dist: gitchangelog==3.0.4; extra == "tests"
|
27
|
+
Requires-Dist: mkdocs==1.5.3; extra == "tests"
|
28
|
+
Requires-Dist: mypy==1.9.0; extra == "tests"
|
29
|
+
Requires-Dist: pydocstyle==6.3.0; extra == "tests"
|
30
|
+
Requires-Dist: pylint==3.3.6; extra == "tests"
|
31
|
+
Requires-Dist: pylama==8.4.1; extra == "tests"
|
32
|
+
Requires-Dist: pytest-cov==6.1.1; extra == "tests"
|
33
|
+
Requires-Dist: pytest-profiling==1.7.0; extra == "tests"
|
34
|
+
Requires-Dist: pytest-xdist==3.6.1; extra == "tests"
|
35
|
+
Requires-Dist: pytest==8.3.5; extra == "tests"
|
36
|
+
Requires-Dist: ruff==0.11.6; extra == "tests"
|
37
|
+
Requires-Dist: setuptools; extra == "tests"
|
38
|
+
Requires-Dist: termplotlib==0.3.9; extra == "tests"
|
39
|
+
Requires-Dist: tox; extra == "tests"
|
40
|
+
Requires-Dist: types-PyYAML==6.0.12.20240311; extra == "tests"
|
41
|
+
Requires-Dist: requests-mock==1.11.0; extra == "tests"
|
42
|
+
Requires-Dist: types-jsonschema; extra == "tests"
|
43
|
+
Requires-Dist: types-setuptools; extra == "tests"
|
44
|
+
Dynamic: license-file
|
45
|
+
|
46
|
+
<!-- [](https://codecov.io/gh/lursight/runem) -->
|
47
|
+
[](https://github.com/lursight/runem/actions/workflows/main.yml)
|
48
|
+
[](https://lursight.github.io/runem/)
|
49
|
+
|
50
|
+
# Run’em
|
51
|
+
|
52
|
+
**Your Blueprint of Commands. Your Engine of Parallel Execution.**
|
53
|
+
Run’em is your definitive blueprint of tasks and commands—instantly discoverable, effortlessly parallel, and elegantly extensible.
|
54
|
+
|
55
|
+
## Core Strengths
|
56
|
+
|
57
|
+
**Blueprint** - discover tasks and onboard smoothly\
|
58
|
+
**Parallel** - get results quicker\
|
59
|
+
**Simple** - define task easily\
|
60
|
+
**Extensible** - add tasks quickly\
|
61
|
+
**Filters** - powerful task selection\
|
62
|
+
**Reports** - see metrics on tasks
|
63
|
+
|
64
|
+
## Why Run’em?
|
65
|
+
- **Command Blueprint:** Instantly see and run all your tasks. No guesswork, no rummaging.
|
66
|
+
- **Effortless Parallelism:** Execute tasks side-by-side to obliterate downtime.
|
67
|
+
- **Simple YAML Declarations:** Define everything in one `.runem.yml`.
|
68
|
+
- **Extensible & Smart:** Adapt to monorepos, complex workflows, and evolving needs.
|
69
|
+
- **Discoverable by Design:** `runem --help` guides your team, new hires, or contributors to every defined command.
|
70
|
+
|
71
|
+
## Contents
|
72
|
+
- [Run’em](#runem)
|
73
|
+
- [Core Strengths](#core-strengths)
|
74
|
+
- [Why Run’em?](#why-runem)
|
75
|
+
- [Contents](#contents)
|
76
|
+
- [Highlights](#highlights)
|
77
|
+
- [Quick Start](#quick-start)
|
78
|
+
- [Basic Use](#basic-use)
|
79
|
+
- [Advanced Use](#advanced-use)
|
80
|
+
- [Help & Discovery](#help--discovery)
|
81
|
+
- [Troubleshooting](#troubleshooting)
|
82
|
+
- [Contribute & Support](#contribute--support)
|
83
|
+
- [About Run’em](#about-runem)
|
84
|
+
|
85
|
+
# Highlights
|
86
|
+
## Blueprint of Commands:
|
87
|
+
The blueprint (available via `--help`) gives you a manifest of all jobs and tasks in a
|
88
|
+
project. A single source of truth for all tasks.
|
89
|
+
## Parallel Execution:
|
90
|
+
Maximise speed with automatic concurrency. Runem tries to run all tasks as quickly as
|
91
|
+
possible, looking at resources, with dependencies. It is not yet a full
|
92
|
+
dependency-execution graph, but by version 1.0.0 it will be.
|
93
|
+
## Filtering:
|
94
|
+
Use powerful and flexible filtering. Select or excluded tasks by `tags`, `name` and
|
95
|
+
`phase`. Chose the task to be run based on your needs, right now.
|
96
|
+
|
97
|
+
You can also customise filtering by adding your own command `options`.
|
98
|
+
|
99
|
+
See `--tags`, `--not-tags`, `--jobs`, `--not-jobs`, `--phases` and `--not-phases`.
|
100
|
+
## Powerful Insights:** Understand what ran, how fast, and what failed.
|
101
|
+
**Quiet by Default:** Focus on what matters, and reveal detail only when needed.
|
102
|
+
|
103
|
+
# Quick Start
|
104
|
+
**Install:**
|
105
|
+
```bash
|
106
|
+
pip install runem
|
107
|
+
```
|
108
|
+
**Define a task:**
|
109
|
+
|
110
|
+
```yaml
|
111
|
+
`# .runem.yml
|
112
|
+
- job:
|
113
|
+
command: echo "hello world!"
|
114
|
+
```
|
115
|
+
|
116
|
+
**Run:**
|
117
|
+
|
118
|
+
```bash
|
119
|
+
runem
|
120
|
+
```
|
121
|
+
|
122
|
+
Run multiple commands in parallel, see timing, and keep output minimal. Need detail?
|
123
|
+
|
124
|
+
```bash
|
125
|
+
runem --verbose
|
126
|
+
```
|
127
|
+
|
128
|
+
[Quick Start Docs](https://lursight.github.io/runem/docs/quick_start.html)
|
129
|
+
|
130
|
+
# Basic Use
|
131
|
+
|
132
|
+
Get comfortable with typical workflows:
|
133
|
+
[Basic Use Docs](https://lursight.github.io/runem/docs/basic_use.html)
|
134
|
+
|
135
|
+
# Advanced Use
|
136
|
+
|
137
|
+
Scale up with multi-phase configs, filtered execution, and custom reporting:
|
138
|
+
[Advanced Configuration](https://lursight.github.io/runem/docs/configuration.html)
|
139
|
+
[Custom Reporting](https://lursight.github.io/runem/docs/reports.html)
|
140
|
+
|
141
|
+
# Help & Discovery
|
142
|
+
|
143
|
+
`runem --help` is your radar—instantly mapping out every available task:
|
144
|
+
[Help & Job Discovery](https://lursight.github.io/runem/docs/help_and_job_discovery.html)
|
145
|
+
|
146
|
+
# Troubleshooting
|
147
|
+
|
148
|
+
Swift solutions to common issues:
|
149
|
+
[Troubleshooting & Known Issues](https://lursight.github.io/runem/docs/troubleshooting_known_issues.html)
|
150
|
+
|
151
|
+
---
|
152
|
+
|
153
|
+
# Contribute & Support
|
154
|
+
|
155
|
+
Brought to you by [Lursight Ltd.](https://lursight.com) and an open community.
|
156
|
+
[CONTRIBUTING.md](CONTRIBUTING.md)
|
157
|
+
[❤️ Sponsor](https://github.com/sponsors/lursight/)
|
158
|
+
|
159
|
+
# About Run’em
|
160
|
+
|
161
|
+
Run’em exists to accelerate your team’s delivery and reduce complexity. Learn about our [Mission](https://lursight.github.io/runem/docs/mission.html).
|
162
|
+
|
runem-0.7.0/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
<!-- [](https://codecov.io/gh/lursight/runem) -->
|
2
|
+
[](https://github.com/lursight/runem/actions/workflows/main.yml)
|
3
|
+
[](https://lursight.github.io/runem/)
|
4
|
+
|
5
|
+
# Run’em
|
6
|
+
|
7
|
+
**Your Blueprint of Commands. Your Engine of Parallel Execution.**
|
8
|
+
Run’em is your definitive blueprint of tasks and commands—instantly discoverable, effortlessly parallel, and elegantly extensible.
|
9
|
+
|
10
|
+
## Core Strengths
|
11
|
+
|
12
|
+
**Blueprint** - discover tasks and onboard smoothly\
|
13
|
+
**Parallel** - get results quicker\
|
14
|
+
**Simple** - define task easily\
|
15
|
+
**Extensible** - add tasks quickly\
|
16
|
+
**Filters** - powerful task selection\
|
17
|
+
**Reports** - see metrics on tasks
|
18
|
+
|
19
|
+
## Why Run’em?
|
20
|
+
- **Command Blueprint:** Instantly see and run all your tasks. No guesswork, no rummaging.
|
21
|
+
- **Effortless Parallelism:** Execute tasks side-by-side to obliterate downtime.
|
22
|
+
- **Simple YAML Declarations:** Define everything in one `.runem.yml`.
|
23
|
+
- **Extensible & Smart:** Adapt to monorepos, complex workflows, and evolving needs.
|
24
|
+
- **Discoverable by Design:** `runem --help` guides your team, new hires, or contributors to every defined command.
|
25
|
+
|
26
|
+
## Contents
|
27
|
+
- [Run’em](#runem)
|
28
|
+
- [Core Strengths](#core-strengths)
|
29
|
+
- [Why Run’em?](#why-runem)
|
30
|
+
- [Contents](#contents)
|
31
|
+
- [Highlights](#highlights)
|
32
|
+
- [Quick Start](#quick-start)
|
33
|
+
- [Basic Use](#basic-use)
|
34
|
+
- [Advanced Use](#advanced-use)
|
35
|
+
- [Help & Discovery](#help--discovery)
|
36
|
+
- [Troubleshooting](#troubleshooting)
|
37
|
+
- [Contribute & Support](#contribute--support)
|
38
|
+
- [About Run’em](#about-runem)
|
39
|
+
|
40
|
+
# Highlights
|
41
|
+
## Blueprint of Commands:
|
42
|
+
The blueprint (available via `--help`) gives you a manifest of all jobs and tasks in a
|
43
|
+
project. A single source of truth for all tasks.
|
44
|
+
## Parallel Execution:
|
45
|
+
Maximise speed with automatic concurrency. Runem tries to run all tasks as quickly as
|
46
|
+
possible, looking at resources, with dependencies. It is not yet a full
|
47
|
+
dependency-execution graph, but by version 1.0.0 it will be.
|
48
|
+
## Filtering:
|
49
|
+
Use powerful and flexible filtering. Select or excluded tasks by `tags`, `name` and
|
50
|
+
`phase`. Chose the task to be run based on your needs, right now.
|
51
|
+
|
52
|
+
You can also customise filtering by adding your own command `options`.
|
53
|
+
|
54
|
+
See `--tags`, `--not-tags`, `--jobs`, `--not-jobs`, `--phases` and `--not-phases`.
|
55
|
+
## Powerful Insights:** Understand what ran, how fast, and what failed.
|
56
|
+
**Quiet by Default:** Focus on what matters, and reveal detail only when needed.
|
57
|
+
|
58
|
+
# Quick Start
|
59
|
+
**Install:**
|
60
|
+
```bash
|
61
|
+
pip install runem
|
62
|
+
```
|
63
|
+
**Define a task:**
|
64
|
+
|
65
|
+
```yaml
|
66
|
+
`# .runem.yml
|
67
|
+
- job:
|
68
|
+
command: echo "hello world!"
|
69
|
+
```
|
70
|
+
|
71
|
+
**Run:**
|
72
|
+
|
73
|
+
```bash
|
74
|
+
runem
|
75
|
+
```
|
76
|
+
|
77
|
+
Run multiple commands in parallel, see timing, and keep output minimal. Need detail?
|
78
|
+
|
79
|
+
```bash
|
80
|
+
runem --verbose
|
81
|
+
```
|
82
|
+
|
83
|
+
[Quick Start Docs](https://lursight.github.io/runem/docs/quick_start.html)
|
84
|
+
|
85
|
+
# Basic Use
|
86
|
+
|
87
|
+
Get comfortable with typical workflows:
|
88
|
+
[Basic Use Docs](https://lursight.github.io/runem/docs/basic_use.html)
|
89
|
+
|
90
|
+
# Advanced Use
|
91
|
+
|
92
|
+
Scale up with multi-phase configs, filtered execution, and custom reporting:
|
93
|
+
[Advanced Configuration](https://lursight.github.io/runem/docs/configuration.html)
|
94
|
+
[Custom Reporting](https://lursight.github.io/runem/docs/reports.html)
|
95
|
+
|
96
|
+
# Help & Discovery
|
97
|
+
|
98
|
+
`runem --help` is your radar—instantly mapping out every available task:
|
99
|
+
[Help & Job Discovery](https://lursight.github.io/runem/docs/help_and_job_discovery.html)
|
100
|
+
|
101
|
+
# Troubleshooting
|
102
|
+
|
103
|
+
Swift solutions to common issues:
|
104
|
+
[Troubleshooting & Known Issues](https://lursight.github.io/runem/docs/troubleshooting_known_issues.html)
|
105
|
+
|
106
|
+
---
|
107
|
+
|
108
|
+
# Contribute & Support
|
109
|
+
|
110
|
+
Brought to you by [Lursight Ltd.](https://lursight.com) and an open community.
|
111
|
+
[CONTRIBUTING.md](CONTRIBUTING.md)
|
112
|
+
[❤️ Sponsor](https://github.com/sponsors/lursight/)
|
113
|
+
|
114
|
+
# About Run’em
|
115
|
+
|
116
|
+
Run’em exists to accelerate your team’s delivery and reduce complexity. Learn about our [Mission](https://lursight.github.io/runem/docs/mission.html).
|
117
|
+
|
@@ -23,7 +23,10 @@ classifiers = [
|
|
23
23
|
dependencies = [
|
24
24
|
# `packaging` is probably not needed after moving to pyproject.toml
|
25
25
|
"packaging>=22.0",
|
26
|
+
|
27
|
+
# For yml passingg and validation
|
26
28
|
"PyYAML>=5.0.0",
|
29
|
+
"jsonschema>=4.22",
|
27
30
|
|
28
31
|
# For UI Elements
|
29
32
|
"rich>10.0.0",
|
@@ -49,26 +52,25 @@ runem = ["VERSION"]
|
|
49
52
|
[project.optional-dependencies]
|
50
53
|
tests = [
|
51
54
|
# This requirements are for development and testing only, not for production.
|
52
|
-
"black==24.10.0",
|
53
55
|
"coverage==7.5",
|
54
|
-
"docformatter==1.7.5",
|
55
56
|
"flake8-bugbear==24.2.6",
|
56
57
|
"flake8==7.0.0",
|
57
58
|
"gitchangelog==3.0.4",
|
58
|
-
"isort==5.13.2",
|
59
59
|
"mkdocs==1.5.3",
|
60
60
|
"mypy==1.9.0",
|
61
61
|
"pydocstyle==6.3.0",
|
62
|
-
"pylint==3.
|
62
|
+
"pylint==3.3.6",
|
63
63
|
"pylama==8.4.1",
|
64
|
-
"pytest-cov==6.
|
64
|
+
"pytest-cov==6.1.1",
|
65
65
|
"pytest-profiling==1.7.0",
|
66
66
|
"pytest-xdist==3.6.1",
|
67
|
-
"pytest==8.3.
|
67
|
+
"pytest==8.3.5",
|
68
|
+
"ruff==0.11.6",
|
68
69
|
"setuptools",
|
69
70
|
"termplotlib==0.3.9",
|
70
71
|
"tox",
|
71
72
|
"types-PyYAML==6.0.12.20240311",
|
72
73
|
"requests-mock==1.11.0",
|
74
|
+
"types-jsonschema",
|
73
75
|
"types-setuptools",
|
74
76
|
]
|
@@ -0,0 +1 @@
|
|
1
|
+
0.7.0
|
@@ -13,7 +13,14 @@ def _reset_console() -> Console:
|
|
13
13
|
|
14
14
|
RICH_CONSOLE = Console(
|
15
15
|
log_path=False, # Do NOT print the source path.
|
16
|
-
markup
|
16
|
+
# We allow markup here, BUT stdout/stderr from other procs should have
|
17
|
+
# `escape()` called on them so they don't error here.
|
18
|
+
# This means 'rich' effects/colors can be judiciously applied:
|
19
|
+
# e.g. `[blink]Don't Panic![/blink]`.
|
20
|
+
markup=True,
|
21
|
+
# `highlight` is what colourises string and number in print() calls.
|
22
|
+
# We do not want this to be auto-magic.
|
23
|
+
highlight=False,
|
17
24
|
)
|
18
25
|
return RICH_CONSOLE
|
19
26
|
|
@@ -31,7 +38,8 @@ def _reset_console_for_tests() -> None:
|
|
31
38
|
RICH_CONSOLE = Console(
|
32
39
|
log_path=False, # Do NOT print the source path.
|
33
40
|
log_time=False, # Do not prefix with log time e.g. `[time] log message`.
|
34
|
-
markup=
|
41
|
+
markup=True, # Allow some markup e.g. `[blink]Don't Panic![/blink]`.
|
42
|
+
highlight=False,
|
35
43
|
width=999, # A very wide width.
|
36
44
|
)
|
37
45
|
|
@@ -43,10 +43,8 @@ def _get_argparse_help_formatter() -> typing.Any:
|
|
43
43
|
|
44
44
|
if use_fixed_width:
|
45
45
|
# Use custom formatter with the width specified in the environment variable
|
46
|
-
return (
|
47
|
-
|
48
|
-
prog
|
49
|
-
)
|
46
|
+
return lambda prog: HelpFormatterFixedWidth( # pylint: disable=unnecessary-lambda
|
47
|
+
prog
|
50
48
|
)
|
51
49
|
|
52
50
|
# Use default formatter
|
@@ -294,12 +292,12 @@ def parse_args(
|
|
294
292
|
error_on_log_logic(args.verbose, args.silent)
|
295
293
|
|
296
294
|
if args.show_root_path_and_exit:
|
297
|
-
log(str(config_metadata.cfg_filepath.parent),
|
295
|
+
log(str(config_metadata.cfg_filepath.parent), prefix=False)
|
298
296
|
# cleanly exit
|
299
297
|
sys.exit(0)
|
300
298
|
|
301
299
|
if args.show_version_and_exit:
|
302
|
-
log(str(get_runem_version()),
|
300
|
+
log(str(get_runem_version()), prefix=False)
|
303
301
|
# cleanly exit
|
304
302
|
sys.exit(0)
|
305
303
|
|
@@ -383,7 +381,6 @@ def initialise_options(
|
|
383
381
|
|
384
382
|
Returns the options dictionary
|
385
383
|
"""
|
386
|
-
|
387
384
|
options: OptionsWritable = InformativeDict(
|
388
385
|
{option["name"]: option["default"] for option in config_metadata.options_config}
|
389
386
|
)
|
@@ -2,9 +2,9 @@ import pathlib
|
|
2
2
|
import sys
|
3
3
|
import typing
|
4
4
|
|
5
|
-
import yaml
|
6
5
|
from packaging.version import Version
|
7
6
|
|
7
|
+
from runem.config_validate import validate_runem_file
|
8
8
|
from runem.log import error, log
|
9
9
|
from runem.runem_version import get_runem_version
|
10
10
|
from runem.types.runem_config import (
|
@@ -13,6 +13,7 @@ from runem.types.runem_config import (
|
|
13
13
|
GlobalSerialisedConfig,
|
14
14
|
UserConfigMetadata,
|
15
15
|
)
|
16
|
+
from runem.yaml_utils import load_yaml_object
|
16
17
|
|
17
18
|
CFG_FILE_YAML = pathlib.Path(".runem.yml")
|
18
19
|
|
@@ -46,7 +47,7 @@ def _search_up_multiple_dirs_for_file(
|
|
46
47
|
|
47
48
|
|
48
49
|
def _find_config_file(
|
49
|
-
config_filename: typing.Union[str, pathlib.Path]
|
50
|
+
config_filename: typing.Union[str, pathlib.Path],
|
50
51
|
) -> typing.Tuple[typing.Optional[pathlib.Path], typing.Tuple[pathlib.Path, ...]]:
|
51
52
|
"""Searches up from the cwd for the given config file-name."""
|
52
53
|
start_dirs = (pathlib.Path(".").absolute(),)
|
@@ -117,8 +118,11 @@ def _conform_global_config_types(
|
|
117
118
|
|
118
119
|
def load_and_parse_config(cfg_filepath: pathlib.Path) -> Config:
|
119
120
|
"""For the given config file pass, project or user, load it & parse/conform it."""
|
120
|
-
|
121
|
-
|
121
|
+
all_config = load_yaml_object(cfg_filepath)
|
122
|
+
validate_runem_file(
|
123
|
+
cfg_filepath,
|
124
|
+
all_config,
|
125
|
+
)
|
122
126
|
|
123
127
|
conformed_config: Config
|
124
128
|
global_config: typing.Optional[GlobalConfig]
|
@@ -162,7 +162,8 @@ def parse_job_config(
|
|
162
162
|
("cwd" in job["ctx"]) and (job["ctx"]["cwd"] is not None)
|
163
163
|
)
|
164
164
|
if (not have_ctw_cwd) or isinstance(
|
165
|
-
job["ctx"]["cwd"],
|
165
|
+
job["ctx"]["cwd"], # type: ignore # handled above
|
166
|
+
str,
|
166
167
|
):
|
167
168
|
# if
|
168
169
|
# - we don't have a cwd, ctx
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import pathlib
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from runem.log import error, log
|
5
|
+
from runem.types.errors import SystemExitBad
|
6
|
+
from runem.yaml_utils import load_yaml_object
|
7
|
+
from runem.yaml_validation import ValidationErrors, validate_yaml
|
8
|
+
|
9
|
+
|
10
|
+
def _load_runem_schema() -> typing.Any:
|
11
|
+
"""Loads and returns the yaml schema for runem.
|
12
|
+
|
13
|
+
Returns:
|
14
|
+
Any: the Draft202012Validator conformant schema.
|
15
|
+
"""
|
16
|
+
schema_path: pathlib.Path = pathlib.Path(__file__).with_name("schema.yml")
|
17
|
+
if not schema_path.exists():
|
18
|
+
error(
|
19
|
+
(
|
20
|
+
"runem schema file not found, cannot continue! "
|
21
|
+
f"Is the install corrupt? {schema_path}"
|
22
|
+
)
|
23
|
+
)
|
24
|
+
raise SystemExitBad(1)
|
25
|
+
schema: typing.Any = load_yaml_object(schema_path)
|
26
|
+
return schema
|
27
|
+
|
28
|
+
|
29
|
+
def validate_runem_file(
|
30
|
+
cfg_filepath: pathlib.Path,
|
31
|
+
all_config: typing.Any,
|
32
|
+
) -> None:
|
33
|
+
"""Validates the config Loader object against the runem schema.
|
34
|
+
|
35
|
+
Exits if the files does not validate.
|
36
|
+
"""
|
37
|
+
schema: typing.Any = _load_runem_schema()
|
38
|
+
errors: ValidationErrors = validate_yaml(all_config, schema)
|
39
|
+
if not errors:
|
40
|
+
# aok
|
41
|
+
return
|
42
|
+
|
43
|
+
error(f"failed to validate runem config [yellow]{cfg_filepath}[/yellow]")
|
44
|
+
for err in errors:
|
45
|
+
path = ".".join(map(str, err.path)) or "<root>"
|
46
|
+
log(f" [yellow]{path}[/yellow]: {err.message}")
|
47
|
+
raise SystemExit("Config validation failed.")
|
@@ -9,8 +9,7 @@ class InformativeDict(typing.Dict[K, V], typing.Generic[K, V]):
|
|
9
9
|
"""A dictionary type that prints out the available keys."""
|
10
10
|
|
11
11
|
def __getitem__(self, key: K) -> V:
|
12
|
-
"""Attempt to
|
13
|
-
found."""
|
12
|
+
"""Attempt to get item, raising a detailed exception if the key is not found."""
|
14
13
|
try:
|
15
14
|
return super().__getitem__(key)
|
16
15
|
except KeyError:
|
@@ -24,19 +23,25 @@ class ReadOnlyInformativeDict(InformativeDict[K, V], typing.Generic[K, V]):
|
|
24
23
|
"""A read-only variant of the above."""
|
25
24
|
|
26
25
|
def __setitem__(self, key: K, value: V) -> None:
|
26
|
+
"""Readonly object, setitem disallowed."""
|
27
27
|
raise NotImplementedError("This dictionary is read-only")
|
28
28
|
|
29
29
|
def __delitem__(self, key: K) -> None:
|
30
|
+
"""Readonly object, delitem disallowed."""
|
30
31
|
raise NotImplementedError("This dictionary is read-only")
|
31
32
|
|
32
33
|
def pop(self, *args: typing.Any, **kwargs: typing.Any) -> V:
|
34
|
+
"""Readonly object, pop disallowed."""
|
33
35
|
raise NotImplementedError("This dictionary is read-only")
|
34
36
|
|
35
37
|
def popitem(self) -> typing.Tuple[K, V]:
|
38
|
+
"""Readonly object, popitem disallowed."""
|
36
39
|
raise NotImplementedError("This dictionary is read-only")
|
37
40
|
|
38
41
|
def clear(self) -> None:
|
42
|
+
"""Readonly object, clear disallowed."""
|
39
43
|
raise NotImplementedError("This dictionary is read-only")
|
40
44
|
|
41
45
|
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
46
|
+
"""Readonly object, update disallowed."""
|
42
47
|
raise NotImplementedError("This dictionary is read-only")
|
@@ -71,7 +71,6 @@ class Job:
|
|
71
71
|
|
72
72
|
TODO: make a non-static member function
|
73
73
|
"""
|
74
|
-
|
75
74
|
# default to all file-tags
|
76
75
|
tags_for_files: typing.Iterable[str] = file_lists.keys()
|
77
76
|
use_default_tags: bool = job_tags is None
|
@@ -91,7 +90,6 @@ class Job:
|
|
91
90
|
|
92
91
|
TODO: make a non-static member function
|
93
92
|
"""
|
94
|
-
|
95
93
|
# First try one of the following keys.
|
96
94
|
valid_name_keys = ("label", "command")
|
97
95
|
for candidate in valid_name_keys:
|
@@ -101,6 +99,6 @@ class Job:
|
|
101
99
|
|
102
100
|
# The try the python-wrapper address
|
103
101
|
try:
|
104
|
-
return f
|
102
|
+
return f"{job['addr']['file']}.{job['addr']['function']}"
|
105
103
|
except KeyError:
|
106
104
|
raise NoJobName() # pylint: disable=raise-missing-from
|