evateam-cli 0.1.0__py3-none-macosx_13_0_arm64.whl
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.
- evateam_cli/__init__.py +46 -0
- evateam_cli/__main__.py +3 -0
- evateam_cli/bin/eva +0 -0
- evateam_cli-0.1.0.dist-info/METADATA +191 -0
- evateam_cli-0.1.0.dist-info/RECORD +8 -0
- evateam_cli-0.1.0.dist-info/WHEEL +4 -0
- evateam_cli-0.1.0.dist-info/entry_points.txt +2 -0
- evateam_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
evateam_cli/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Launcher for the `eva` binary shipped in this wheel.
|
|
2
|
+
|
|
3
|
+
The binary is compiled Mojo. Its rpath finds Mojo's runtime libraries in the
|
|
4
|
+
mojo-compiler package next to this one in site-packages. For its HTTPS calls
|
|
5
|
+
it embeds a libpython, and this launcher points it at the interpreter that is
|
|
6
|
+
running here.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import sysconfig
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _libpython() -> str | None:
|
|
16
|
+
libdir = sysconfig.get_config_var("LIBDIR") or ""
|
|
17
|
+
names = [
|
|
18
|
+
sysconfig.get_config_var("LDLIBRARY") or "",
|
|
19
|
+
sysconfig.get_config_var("INSTSONAME") or "",
|
|
20
|
+
f"libpython{sysconfig.get_python_version()}.dylib",
|
|
21
|
+
f"libpython{sysconfig.get_python_version()}.so.1.0",
|
|
22
|
+
f"libpython{sysconfig.get_python_version()}.so",
|
|
23
|
+
]
|
|
24
|
+
for name in names:
|
|
25
|
+
if name and not name.endswith(".a"):
|
|
26
|
+
path = Path(libdir) / name
|
|
27
|
+
if path.exists():
|
|
28
|
+
return str(path)
|
|
29
|
+
framework = sysconfig.get_config_var("PYTHONFRAMEWORKPREFIX")
|
|
30
|
+
if framework:
|
|
31
|
+
path = Path(framework) / "Python.framework" / "Versions" / sysconfig.get_python_version() / "Python"
|
|
32
|
+
if path.exists():
|
|
33
|
+
return str(path)
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def main() -> None:
|
|
38
|
+
binary = Path(__file__).parent / "bin" / "eva"
|
|
39
|
+
env = dict(os.environ)
|
|
40
|
+
if "MOJO_PYTHON_LIBRARY" not in env:
|
|
41
|
+
lib = _libpython()
|
|
42
|
+
if lib:
|
|
43
|
+
env["MOJO_PYTHON_LIBRARY"] = lib
|
|
44
|
+
# From a venv the embedded interpreter cannot find its own prefix.
|
|
45
|
+
env.setdefault("PYTHONHOME", sys.base_prefix)
|
|
46
|
+
os.execve(binary, [str(binary), *sys.argv[1:]], env)
|
evateam_cli/__main__.py
ADDED
evateam_cli/bin/eva
ADDED
|
Binary file
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evateam-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: EvaTeam from the command line, written in Mojo
|
|
5
|
+
Author: Ilya Lubenets
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Project-URL: Source, https://github.com/ormeilu/evateam-cli
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Requires-Dist: mojo-compiler==1.2.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# eva — EvaTeam from the command line, in Mojo
|
|
14
|
+
|
|
15
|
+
`eva` is a command-line client for [EvaTeam](https://www.evateam.ru/), the
|
|
16
|
+
Russian task tracker and wiki. It is a sibling of
|
|
17
|
+
[ytcli](https://github.com/ormeilu/yandex-tracker-cli), which does the same for
|
|
18
|
+
Yandex Tracker, and it is written in [Mojo](https://www.modular.com/mojo) for
|
|
19
|
+
fun.
|
|
20
|
+
|
|
21
|
+
Like ytcli, it has two readers in mind: a person at a terminal and an AI agent
|
|
22
|
+
behind a pipe. The terminal gets colour and full descriptions. The pipe gets
|
|
23
|
+
plain text, a capped description and a note saying what was left out.
|
|
24
|
+
|
|
25
|
+
```console
|
|
26
|
+
$ eva task get DEV-1
|
|
27
|
+
DEV-1 Вложения теряются при переносе задачи
|
|
28
|
+
status: В работе stage: IN_PROGRESS
|
|
29
|
+
assignee: ilya@example.ru author: rep@example.ru
|
|
30
|
+
parent: DEV Development
|
|
31
|
+
created: 2026-11-01T10:00:00+03:00 updated: 2026-11-02T10:00:00+03:00
|
|
32
|
+
<untrusted src="DEV-1/text">
|
|
33
|
+
Шаги:
|
|
34
|
+
|
|
35
|
+
- Приложить файл
|
|
36
|
+
- Перенести
|
|
37
|
+
</untrusted>
|
|
38
|
+
|
|
39
|
+
$ eva task list -p DEV -s open -n 2
|
|
40
|
+
DEV-2 Открыто - Retry uploads on 5xx
|
|
41
|
+
DEV-7 Открыто ilya@example.ru Flaky upload test
|
|
42
|
+
shown 2 of 14 — next: --page 2
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Task descriptions and comments are written by other people, so `eva` fences
|
|
46
|
+
them: a `▏` margin in a terminal, an `<untrusted src="…">` tag in a pipe. An
|
|
47
|
+
agent reading the output can tell the tracker's data from instructions.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
eva is built for macOS on Apple silicon and Linux on x86_64 and arm64 (glibc
|
|
52
|
+
2.34 or newer). Pick one way to install it.
|
|
53
|
+
|
|
54
|
+
With the install script. It installs [pixi](https://pixi.sh) if it is missing,
|
|
55
|
+
then eva from the conda channel `prefix.dev/ormeilu`. Run it again to update:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
curl -fsSL https://raw.githubusercontent.com/ormeilu/evateam-cli/main/install.sh | sh
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
With pixi directly, which is what the script does:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pixi global install -c https://prefix.dev/ormeilu -c https://conda.modular.com/max -c conda-forge evateam-cli
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
From PyPI, if you prefer uv or pipx:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
uvx --from evateam-cli eva --help # run without installing
|
|
71
|
+
uv tool install evateam-cli # or put `eva` on PATH
|
|
72
|
+
pipx install evateam-cli
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`eva upgrade` updates eva the same way it was installed.
|
|
76
|
+
|
|
77
|
+
The two routes differ in size. Both carry Mojo's runtime libraries as a
|
|
78
|
+
dependency on Modular's `mojo-compiler` package. On PyPI that package is about
|
|
79
|
+
70 MB. The conda package brings the whole compiler, and the pixi environment
|
|
80
|
+
ends up around 300 MB. The binary itself is under half a megabyte. Either way
|
|
81
|
+
eva makes its HTTPS calls through the Python it is installed with, so there is
|
|
82
|
+
nothing else to set up.
|
|
83
|
+
|
|
84
|
+
From source, with [pixi](https://pixi.sh):
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/ormeilu/evateam-cli
|
|
88
|
+
cd evateam-cli
|
|
89
|
+
pixi run build # → build/eva
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Configure
|
|
93
|
+
|
|
94
|
+
Create a token in EvaTeam: «Моя страница» → «Безопасность» → «Сгенерировать
|
|
95
|
+
API токен». Then set two variables, in the environment, in `./.env` or in
|
|
96
|
+
`~/.config/eva-cli/env`:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
EVA_URL=https://eva.example.ru
|
|
100
|
+
EVA_API_KEY=…
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`EVA_INSECURE=1` skips TLS verification, for self-hosted servers with their own
|
|
104
|
+
certificate. `eva config` shows what was picked up and from where, without
|
|
105
|
+
printing the token.
|
|
106
|
+
|
|
107
|
+
## Commands
|
|
108
|
+
|
|
109
|
+
| Command | What it does |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `eva task get CODE [--fields a,b]` | one task: status, people, parent, description |
|
|
112
|
+
| `eva task list [-p PROJ] [-a LOGIN] [-s STATUS] [-q TEXT]` | search; `-s` takes `open`, `closed`, `in_progress`, `in_review`, `all` or a status code |
|
|
113
|
+
| `eva task create -p PROJ -t TITLE [-d TEXT] [-a LOGIN] [--sprint SPR-1]` | new task |
|
|
114
|
+
| `eva task update CODE [-t] [-d] [-a] [--set JSON]` | change fields; `--set` passes any field through |
|
|
115
|
+
| `eva task statuses CODE` | the statuses the task's workflow lets it move to from here |
|
|
116
|
+
| `eva task move CODE STATUS [-m TEXT] [--set JSON]` | change status through a workflow transition; `--set` fills fields the transition requires |
|
|
117
|
+
| `eva task comment CODE TEXT` / `eva task comments CODE` | write or read comments |
|
|
118
|
+
| `eva task log CODE 1h30m [-m TEXT]` | log time spent |
|
|
119
|
+
| `eva task links CODE` | incoming and outgoing links |
|
|
120
|
+
| `eva project list`, `eva person list`, `eva doc list` | lists, with `-q` to search by name |
|
|
121
|
+
| `eva doc get CODE` | a wiki document as text |
|
|
122
|
+
| `eva call METHOD --kwargs JSON` | any API method; prints the raw reply |
|
|
123
|
+
|
|
124
|
+
Every list takes `-n/--limit` and `--page` and ends with `shown N of M`, so a
|
|
125
|
+
page is never mistaken for the whole answer. `--json` prints the API's own
|
|
126
|
+
JSON, `--full` turns off truncation in a pipe, and `-v` prints each request
|
|
127
|
+
and reply on stderr.
|
|
128
|
+
|
|
129
|
+
`--filter` on `task list` adds a raw BQL condition, for anything the flags do
|
|
130
|
+
not cover:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
eva task list --filter '["cmf_created_at", ">=", "2026-10-01"]'
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## How it is built
|
|
137
|
+
|
|
138
|
+
EvaTeam speaks JSON-RPC 2.2: every call is a `POST /api/` with a method name
|
|
139
|
+
such as `CmfTask.list` and keyword arguments. The pieces:
|
|
140
|
+
|
|
141
|
+
- `src/eva/json.mojo`: a JSON parser and writer. Nodes sit in one arena and
|
|
142
|
+
refer to their children by index, which avoids a recursive type. Numbers keep
|
|
143
|
+
their source text, so ids and timestamps pass through unchanged.
|
|
144
|
+
- `src/eva/client.mojo`: configuration and the RPC call. Mojo's standard
|
|
145
|
+
library has no TLS yet, so the HTTPS request itself goes through Python's
|
|
146
|
+
`urllib` via Mojo's Python interop. Request building and reply parsing are
|
|
147
|
+
Mojo.
|
|
148
|
+
- `src/eva/render.mojo`: terminal and pipe output, HTML to text, colour via
|
|
149
|
+
[mist](https://github.com/thatstoasty/mist) from the
|
|
150
|
+
[modular-community](https://prefix.dev/channels/modular-community) channel.
|
|
151
|
+
- `src/eva/commands.mojo`: the command tree.
|
|
152
|
+
|
|
153
|
+
The JSON parser is home-made because the `emberjson` build in modular-community
|
|
154
|
+
was compiled for Mojo 1.1 and does not load under 1.2.
|
|
155
|
+
|
|
156
|
+
## Develop
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pixi run test # unit tests: JSON, rendering, argument parsing
|
|
160
|
+
pixi run e2e # builds eva and runs it against tests/mock_server.py
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`pixi run conda-package` builds the conda package from `recipe/recipe.yaml`
|
|
164
|
+
into `output/`. `pixi run wheel macosx_13_0_arm64` packs `build/eva` into a wheel in `dist/`,
|
|
165
|
+
with its rpath pointed at `mojo-compiler`'s libraries in the same
|
|
166
|
+
site-packages. A tag `vX.Y.Z` matching `pixi.toml` runs
|
|
167
|
+
`.github/workflows/release.yml`: wheels and conda packages for three
|
|
168
|
+
platforms, the end-to-end suite against each one installed the way a user
|
|
169
|
+
would, then PyPI, prefix.dev and a GitHub release.
|
|
170
|
+
|
|
171
|
+
`tests/mock_server.py` is a small JSON-RPC server with two canned tasks. The
|
|
172
|
+
end-to-end script checks both what `eva` prints and what it sends.
|
|
173
|
+
|
|
174
|
+
## Status
|
|
175
|
+
|
|
176
|
+
Checked against a live EvaTeam instance, reads and writes: every command in the
|
|
177
|
+
table above, paging and counts, and a test task taken from creation to Done.
|
|
178
|
+
|
|
179
|
+
Two things the docs do not say, found that way:
|
|
180
|
+
|
|
181
|
+
- Plain `CmfTask.update` with a new status is accepted and ignored. A status
|
|
182
|
+
change has to run a workflow transition (`CmfTask.task_transition_run`), which
|
|
183
|
+
is what `task move` does: it reads the task's workflow, finds the transition
|
|
184
|
+
from the current status and reports which targets exist when the one asked for
|
|
185
|
+
does not.
|
|
186
|
+
- A refused call comes back as HTTP 200 with an `abort` message ("Необходимо
|
|
187
|
+
заполнить Вид услуги") and no `error`. `eva` treats it as an error.
|
|
188
|
+
|
|
189
|
+
## Licence
|
|
190
|
+
|
|
191
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
evateam_cli/__init__.py,sha256=2F7qCr62iEkmzTUakBosjg84MjY32oT7-Yl6zfmlCvA,1621
|
|
2
|
+
evateam_cli/__main__.py,sha256=q93TOIsNvR12G0fVRFOhytbgkRlp9yrULjSzADJkkjQ,37
|
|
3
|
+
evateam_cli/bin/eva,sha256=ZfGgnSoPxlNM9S7JxpEuhljK0_rNjxpG9vYplXItolo,471296
|
|
4
|
+
evateam_cli-0.1.0.dist-info/licenses/LICENSE,sha256=RooB9VjlLCKnSgdSsnReIFpZNaTLoFqdRdC1tjMPKlE,1070
|
|
5
|
+
evateam_cli-0.1.0.dist-info/METADATA,sha256=eBpUCb9J3-7EF00cZ__cdCWIgYadz55ZvW0zlDPOWS4,7732
|
|
6
|
+
evateam_cli-0.1.0.dist-info/WHEEL,sha256=jbuqoWejtGFyBB65Kb0ZrMoo8Gc6egtaqIHMy9AGS_A,112
|
|
7
|
+
evateam_cli-0.1.0.dist-info/entry_points.txt,sha256=UCDJ-pfUgLpIIImK-MvSyLU1T-sid9CMKC2pQ1MERuw,41
|
|
8
|
+
evateam_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ilya Lubenets
|
|
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.
|