avr-cli 0.4.0__tar.gz → 0.5.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.
- {avr_cli-0.4.0 → avr_cli-0.5.0}/PKG-INFO +82 -5
- {avr_cli-0.4.0 → avr_cli-0.5.0}/README.md +80 -4
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/PKG-INFO +82 -5
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/SOURCES.txt +10 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/requires.txt +1 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/_build_info.py +1 -1
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/api_client.py +27 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/auth.py +5 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/click_ext.py +21 -0
- avr_cli-0.5.0/avrea_cli/commands/artifact.py +272 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/auth_cmd.py +101 -7
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/config_cmd.py +20 -2
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/job.py +3 -1
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/repo.py +309 -40
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/run.py +5 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/sbom.py +266 -56
- avr_cli-0.5.0/avrea_cli/commands/token.py +553 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/config.py +11 -5
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/display.py +25 -3
- avr_cli-0.5.0/avrea_cli/git_mirror.py +207 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/helpers.py +125 -15
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/json_output.py +16 -9
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/log_display.py +3 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/main.py +3 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/repo_context.py +140 -56
- avr_cli-0.5.0/avrea_cli/spawn_script.py +89 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/transport.py +9 -2
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/vm.py +226 -32
- {avr_cli-0.4.0 → avr_cli-0.5.0}/pyproject.toml +5 -1
- avr_cli-0.5.0/tests/test_artifact.py +164 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_env_conventions.py +57 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_helpers.py +59 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_job_list.py +11 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_job_metrics_cmd.py +4 -3
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_log_display.py +11 -0
- avr_cli-0.5.0/tests/test_repo_clone.py +775 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_repo_context.py +48 -15
- avr_cli-0.5.0/tests/test_repo_mirror.py +612 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_view.py +11 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_sbom.py +556 -9
- avr_cli-0.5.0/tests/test_scoped_token.py +519 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_settings.py +12 -12
- avr_cli-0.5.0/tests/test_token.py +774 -0
- avr_cli-0.5.0/tests/test_token_output.py +534 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_vm.py +6 -3
- avr_cli-0.5.0/tests/test_vm_spawn_script.py +190 -0
- avr_cli-0.4.0/tests/test_repo_mirror.py +0 -310
- {avr_cli-0.4.0 → avr_cli-0.5.0}/LICENSE +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/dependency_links.txt +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/entry_points.txt +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avr_cli.egg-info/top_level.txt +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/__init__.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/assets/logo_mark.svg +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/audit.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/billing.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/__init__.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/cache.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/health.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/internal.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/log.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/org.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/pr.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/settings.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/status.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/commands/workflow.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/docs_gen.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/firewall.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/metrics_display.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/output.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/paths.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/run_diagnostics.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/run_diagnostics_display.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/run_refs.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/avrea_cli/version.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/setup.cfg +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_api_client.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_auth.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_auth_login.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_auth_sso.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_cache.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_config.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_config_cmd.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_display.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_docs_gen.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_firewall.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_global_flags.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_job_view_logs.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_json_output.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_lazy_registry.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_metrics_display.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_org_email_domain.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_org_saml.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_output.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_piped_output.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_pr_list.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_public_mirrors.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_attempts.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_command_refs.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_diagnose.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_list_flags.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_logs.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_run_refs.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_transport.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_web_flag.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_workflow_actions.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_workflow_run.py +0 -0
- {avr_cli-0.4.0 → avr_cli-0.5.0}/tests/test_workflow_view.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avr-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Avrea command-line client
|
|
5
5
|
Author-email: Avrea <support@avrea.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -15,6 +15,7 @@ Requires-Python: >=3.14
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: anyio>=4.14.2
|
|
18
|
+
Requires-Dist: certifi>=2026.10.19
|
|
18
19
|
Requires-Dist: click>=8.3.1
|
|
19
20
|
Requires-Dist: httpx>=0.28.1
|
|
20
21
|
Requires-Dist: platformdirs>=4.0
|
|
@@ -117,6 +118,61 @@ Auto-detection means most commands "just work" without `--repo`. Outside a git t
|
|
|
117
118
|
|
|
118
119
|
## Common workflows
|
|
119
120
|
|
|
121
|
+
**Clone from a nearby Git mirror**
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
avr repo clone foo/faa
|
|
125
|
+
avr repo clone foo/faa my-checkout --depth 1 --branch main
|
|
126
|
+
avr repo clone foo/faa --placement https://git.fi.a.avrea.io
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Avrea discovers the repository's placements and picks the first mirror that
|
|
130
|
+
responds successfully from your machine. `--placement` accepts an advertised
|
|
131
|
+
mirror URL or hostname to select one explicitly. Give the `owner/repository` name
|
|
132
|
+
or an Avrea repository ID (`rep-...`).
|
|
133
|
+
The repository must already have Git mirroring enabled and a usable placement.
|
|
134
|
+
|
|
135
|
+
`origin` stays on the selected read-only mirror. The clone installs a
|
|
136
|
+
repository-local credential helper so ordinary `git fetch` and `git pull`
|
|
137
|
+
obtain fresh Git tokens through your Avrea login. Push changes to the source
|
|
138
|
+
repository using a separate remote, for example:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
git remote add upstream git@github.com:foo/faa.git
|
|
142
|
+
git push upstream HEAD
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Tokens normally last one hour; scoped API keys are limited to ten minutes or
|
|
146
|
+
their remaining lifetime. Git tokens are never saved in the remote URL or
|
|
147
|
+
repository configuration. Keep `avr` installed and logged in for later Git
|
|
148
|
+
operations; the helper continues to use the API host used for the clone.
|
|
149
|
+
|
|
150
|
+
**Create a mirror at a location**
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
avr repo mirror locations
|
|
154
|
+
avr repo mirror create foo/faa --location gsc-fi
|
|
155
|
+
avr repo mirror create group/subgroup/project --source gitlab --location gsc-fi
|
|
156
|
+
avr repo mirror status --repo group/subgroup/project --source gitlab
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`--source` defaults to `github`; `gitlab` supports nested group names. The
|
|
160
|
+
repository must already be connected to your Avrea organization. Use `--org`
|
|
161
|
+
to select an organization. Mirror management requires the organization's
|
|
162
|
+
Git-mirror feature and an admin role.
|
|
163
|
+
|
|
164
|
+
`--location` accepts a cluster ID, datacenter ID, or unique name from `locations`.
|
|
165
|
+
The command enables mirroring and adds the placement, preserving existing
|
|
166
|
+
placements. Sync runs asynchronously; use `status` to check it. Repeating
|
|
167
|
+
`create` is safe, including when enabling succeeded but placement failed.
|
|
168
|
+
`locations` and `create` also support `--json` and `--jq`.
|
|
169
|
+
|
|
170
|
+
GitLab repositories cannot be mirrored until GitLab mirroring is enabled
|
|
171
|
+
for your organization. `--source gitlab` is available for deployments that
|
|
172
|
+
support it, including repositories in nested groups. If a GitLab repository
|
|
173
|
+
returns “not found,” check its connection to Avrea, your access, and whether
|
|
174
|
+
GitLab mirroring is enabled.
|
|
175
|
+
|
|
120
176
|
**Watch a run live**
|
|
121
177
|
|
|
122
178
|
```sh
|
|
@@ -239,11 +295,32 @@ case $? in
|
|
|
239
295
|
esac
|
|
240
296
|
```
|
|
241
297
|
|
|
242
|
-
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error.
|
|
298
|
+
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error. `130` means Ctrl-C
|
|
299
|
+
ended the command, including a watch, a log follow or a tunnel that is stopped that way.
|
|
300
|
+
|
|
301
|
+
**Scoped tokens for scripts and agents**
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
avr token create --name ci-read --org <org-id> --repo acme/api --ttl 8h # prints the credential once
|
|
305
|
+
avr token create --name agent --org <org-id> --vm <vm-id> --allow-vm-create --vm-create-limit 3
|
|
306
|
+
avr token list
|
|
307
|
+
avr token view <token-id>
|
|
308
|
+
avr token revoke <token-id>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
A scoped token is a short-lived credential (60 seconds to 7 days, 8 hours by default) bound to one organization. Repository grants are read-only and VM grants are admin: it reads only the repositories it names, has admin access to only the VMs it names, and cannot create further tokens. `create` prints the `export AVR_TOKEN=...` and `export AVR_ORG=...` lines for whatever will use the token. Scoped tokens require the `org-...` ID in `AVR_ORG` or `--org`; they ignore the login's stored organization and cannot resolve a slug. `avr auth status` reports this configuration without checking whether the token is still live. A 403 or 404 comes with a reminder that the token reaches only what it names.
|
|
312
|
+
|
|
313
|
+
`avr repo clone acme/api` uses the `owner/repository` name directly.
|
|
314
|
+
|
|
315
|
+
With a scoped token, a repository name (`--repo acme/api`, `AVR_REPO` or the checkout's remote) is matched against the repositories the token reaches; a `rep-...` ID works as well. For runs, use the run ID rather than an Avrea console URL, whose organization slug cannot be resolved with a scoped token. Token IDs for `view` and `revoke` are `key-` followed by 32 lowercase hexadecimal characters.
|
|
316
|
+
|
|
317
|
+
Creation returns the credential only once. If `--jq` fails or removes it, the command falls back to the requested JSON without filtering and exits nonzero. Save the credential from that JSON. Output failures also report a revoke command on stderr without exposing the credential there. If the creation request is interrupted, its response is lost, or the answer is a server error (5xx), a token may already exist: check `avr token list` before retrying. Only a 503 that carries the API's own reason means that no token was created.
|
|
318
|
+
|
|
319
|
+
If `avr token create` returns a 404, check the API's reason, the organization and the API host. If scoped-token access is unavailable, contact support@avrea.com to request it. VM grants and `--allow-vm-create` also require customer VMs to be enabled for the organization. `list`, `view` and `revoke` remain available when token creation is disabled.
|
|
243
320
|
|
|
244
321
|
**Pipe-aware output**
|
|
245
322
|
|
|
246
|
-
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
323
|
+
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. Control characters, Unicode line separators and bidirectional controls inside cells are escaped (`\t`, `\x1b`, `\u2028`), keeping each item on one row with stable columns. The escaping is not reversible: a literal backslash is left as it is, so a cell that reads `a\tb` may hold either a tab or a backslash and a `t`. Use `--json` when the exact value matters; JSON retains the original values. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
247
324
|
|
|
248
325
|
## Configuration
|
|
249
326
|
|
|
@@ -252,8 +329,8 @@ When stdout isn't a TTY, list commands switch to tab-separated rows (no color, n
|
|
|
252
329
|
| Variable | Purpose |
|
|
253
330
|
| ---------------- | ------------------------------------------------------------------------------------------------ |
|
|
254
331
|
| `AVR_HOST` | Avrea API URL. Defaults to the active host in `hosts.json`, then `https://api.avrea.com`. |
|
|
255
|
-
| `AVR_TOKEN` | API key. Overrides whatever's stored for the active host.
|
|
256
|
-
| `AVR_ORG` | Default organization ID. Overrides the stored default.
|
|
332
|
+
| `AVR_TOKEN` | API key or scoped token. Overrides whatever's stored for the active host. |
|
|
333
|
+
| `AVR_ORG` | Default organization ID. Overrides the stored default. A scoped token needs it, or `--org`, except for `repo clone`. |
|
|
257
334
|
| `AVR_REPO` | Default repository (`org/name` or `rep-xxx`). Overrides git auto-detect. |
|
|
258
335
|
| `AVR_BROWSER` | Browser to launch for `--web` and OAuth login. Falls back to `BROWSER`, then system default. |
|
|
259
336
|
| `AVR_PAGER` | Pager for long output. Overrides `PAGER`. Set to empty string to disable paging. |
|
|
@@ -85,6 +85,61 @@ Auto-detection means most commands "just work" without `--repo`. Outside a git t
|
|
|
85
85
|
|
|
86
86
|
## Common workflows
|
|
87
87
|
|
|
88
|
+
**Clone from a nearby Git mirror**
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
avr repo clone foo/faa
|
|
92
|
+
avr repo clone foo/faa my-checkout --depth 1 --branch main
|
|
93
|
+
avr repo clone foo/faa --placement https://git.fi.a.avrea.io
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Avrea discovers the repository's placements and picks the first mirror that
|
|
97
|
+
responds successfully from your machine. `--placement` accepts an advertised
|
|
98
|
+
mirror URL or hostname to select one explicitly. Give the `owner/repository` name
|
|
99
|
+
or an Avrea repository ID (`rep-...`).
|
|
100
|
+
The repository must already have Git mirroring enabled and a usable placement.
|
|
101
|
+
|
|
102
|
+
`origin` stays on the selected read-only mirror. The clone installs a
|
|
103
|
+
repository-local credential helper so ordinary `git fetch` and `git pull`
|
|
104
|
+
obtain fresh Git tokens through your Avrea login. Push changes to the source
|
|
105
|
+
repository using a separate remote, for example:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
git remote add upstream git@github.com:foo/faa.git
|
|
109
|
+
git push upstream HEAD
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Tokens normally last one hour; scoped API keys are limited to ten minutes or
|
|
113
|
+
their remaining lifetime. Git tokens are never saved in the remote URL or
|
|
114
|
+
repository configuration. Keep `avr` installed and logged in for later Git
|
|
115
|
+
operations; the helper continues to use the API host used for the clone.
|
|
116
|
+
|
|
117
|
+
**Create a mirror at a location**
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
avr repo mirror locations
|
|
121
|
+
avr repo mirror create foo/faa --location gsc-fi
|
|
122
|
+
avr repo mirror create group/subgroup/project --source gitlab --location gsc-fi
|
|
123
|
+
avr repo mirror status --repo group/subgroup/project --source gitlab
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`--source` defaults to `github`; `gitlab` supports nested group names. The
|
|
127
|
+
repository must already be connected to your Avrea organization. Use `--org`
|
|
128
|
+
to select an organization. Mirror management requires the organization's
|
|
129
|
+
Git-mirror feature and an admin role.
|
|
130
|
+
|
|
131
|
+
`--location` accepts a cluster ID, datacenter ID, or unique name from `locations`.
|
|
132
|
+
The command enables mirroring and adds the placement, preserving existing
|
|
133
|
+
placements. Sync runs asynchronously; use `status` to check it. Repeating
|
|
134
|
+
`create` is safe, including when enabling succeeded but placement failed.
|
|
135
|
+
`locations` and `create` also support `--json` and `--jq`.
|
|
136
|
+
|
|
137
|
+
GitLab repositories cannot be mirrored until GitLab mirroring is enabled
|
|
138
|
+
for your organization. `--source gitlab` is available for deployments that
|
|
139
|
+
support it, including repositories in nested groups. If a GitLab repository
|
|
140
|
+
returns “not found,” check its connection to Avrea, your access, and whether
|
|
141
|
+
GitLab mirroring is enabled.
|
|
142
|
+
|
|
88
143
|
**Watch a run live**
|
|
89
144
|
|
|
90
145
|
```sh
|
|
@@ -207,11 +262,32 @@ case $? in
|
|
|
207
262
|
esac
|
|
208
263
|
```
|
|
209
264
|
|
|
210
|
-
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error.
|
|
265
|
+
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error. `130` means Ctrl-C
|
|
266
|
+
ended the command, including a watch, a log follow or a tunnel that is stopped that way.
|
|
267
|
+
|
|
268
|
+
**Scoped tokens for scripts and agents**
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
avr token create --name ci-read --org <org-id> --repo acme/api --ttl 8h # prints the credential once
|
|
272
|
+
avr token create --name agent --org <org-id> --vm <vm-id> --allow-vm-create --vm-create-limit 3
|
|
273
|
+
avr token list
|
|
274
|
+
avr token view <token-id>
|
|
275
|
+
avr token revoke <token-id>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
A scoped token is a short-lived credential (60 seconds to 7 days, 8 hours by default) bound to one organization. Repository grants are read-only and VM grants are admin: it reads only the repositories it names, has admin access to only the VMs it names, and cannot create further tokens. `create` prints the `export AVR_TOKEN=...` and `export AVR_ORG=...` lines for whatever will use the token. Scoped tokens require the `org-...` ID in `AVR_ORG` or `--org`; they ignore the login's stored organization and cannot resolve a slug. `avr auth status` reports this configuration without checking whether the token is still live. A 403 or 404 comes with a reminder that the token reaches only what it names.
|
|
279
|
+
|
|
280
|
+
`avr repo clone acme/api` uses the `owner/repository` name directly.
|
|
281
|
+
|
|
282
|
+
With a scoped token, a repository name (`--repo acme/api`, `AVR_REPO` or the checkout's remote) is matched against the repositories the token reaches; a `rep-...` ID works as well. For runs, use the run ID rather than an Avrea console URL, whose organization slug cannot be resolved with a scoped token. Token IDs for `view` and `revoke` are `key-` followed by 32 lowercase hexadecimal characters.
|
|
283
|
+
|
|
284
|
+
Creation returns the credential only once. If `--jq` fails or removes it, the command falls back to the requested JSON without filtering and exits nonzero. Save the credential from that JSON. Output failures also report a revoke command on stderr without exposing the credential there. If the creation request is interrupted, its response is lost, or the answer is a server error (5xx), a token may already exist: check `avr token list` before retrying. Only a 503 that carries the API's own reason means that no token was created.
|
|
285
|
+
|
|
286
|
+
If `avr token create` returns a 404, check the API's reason, the organization and the API host. If scoped-token access is unavailable, contact support@avrea.com to request it. VM grants and `--allow-vm-create` also require customer VMs to be enabled for the organization. `list`, `view` and `revoke` remain available when token creation is disabled.
|
|
211
287
|
|
|
212
288
|
**Pipe-aware output**
|
|
213
289
|
|
|
214
|
-
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
290
|
+
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. Control characters, Unicode line separators and bidirectional controls inside cells are escaped (`\t`, `\x1b`, `\u2028`), keeping each item on one row with stable columns. The escaping is not reversible: a literal backslash is left as it is, so a cell that reads `a\tb` may hold either a tab or a backslash and a `t`. Use `--json` when the exact value matters; JSON retains the original values. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
215
291
|
|
|
216
292
|
## Configuration
|
|
217
293
|
|
|
@@ -220,8 +296,8 @@ When stdout isn't a TTY, list commands switch to tab-separated rows (no color, n
|
|
|
220
296
|
| Variable | Purpose |
|
|
221
297
|
| ---------------- | ------------------------------------------------------------------------------------------------ |
|
|
222
298
|
| `AVR_HOST` | Avrea API URL. Defaults to the active host in `hosts.json`, then `https://api.avrea.com`. |
|
|
223
|
-
| `AVR_TOKEN` | API key. Overrides whatever's stored for the active host.
|
|
224
|
-
| `AVR_ORG` | Default organization ID. Overrides the stored default.
|
|
299
|
+
| `AVR_TOKEN` | API key or scoped token. Overrides whatever's stored for the active host. |
|
|
300
|
+
| `AVR_ORG` | Default organization ID. Overrides the stored default. A scoped token needs it, or `--org`, except for `repo clone`. |
|
|
225
301
|
| `AVR_REPO` | Default repository (`org/name` or `rep-xxx`). Overrides git auto-detect. |
|
|
226
302
|
| `AVR_BROWSER` | Browser to launch for `--web` and OAuth login. Falls back to `BROWSER`, then system default. |
|
|
227
303
|
| `AVR_PAGER` | Pager for long output. Overrides `PAGER`. Set to empty string to disable paging. |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avr-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Avrea command-line client
|
|
5
5
|
Author-email: Avrea <support@avrea.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -15,6 +15,7 @@ Requires-Python: >=3.14
|
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: anyio>=4.14.2
|
|
18
|
+
Requires-Dist: certifi>=2026.10.19
|
|
18
19
|
Requires-Dist: click>=8.3.1
|
|
19
20
|
Requires-Dist: httpx>=0.28.1
|
|
20
21
|
Requires-Dist: platformdirs>=4.0
|
|
@@ -117,6 +118,61 @@ Auto-detection means most commands "just work" without `--repo`. Outside a git t
|
|
|
117
118
|
|
|
118
119
|
## Common workflows
|
|
119
120
|
|
|
121
|
+
**Clone from a nearby Git mirror**
|
|
122
|
+
|
|
123
|
+
```sh
|
|
124
|
+
avr repo clone foo/faa
|
|
125
|
+
avr repo clone foo/faa my-checkout --depth 1 --branch main
|
|
126
|
+
avr repo clone foo/faa --placement https://git.fi.a.avrea.io
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Avrea discovers the repository's placements and picks the first mirror that
|
|
130
|
+
responds successfully from your machine. `--placement` accepts an advertised
|
|
131
|
+
mirror URL or hostname to select one explicitly. Give the `owner/repository` name
|
|
132
|
+
or an Avrea repository ID (`rep-...`).
|
|
133
|
+
The repository must already have Git mirroring enabled and a usable placement.
|
|
134
|
+
|
|
135
|
+
`origin` stays on the selected read-only mirror. The clone installs a
|
|
136
|
+
repository-local credential helper so ordinary `git fetch` and `git pull`
|
|
137
|
+
obtain fresh Git tokens through your Avrea login. Push changes to the source
|
|
138
|
+
repository using a separate remote, for example:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
git remote add upstream git@github.com:foo/faa.git
|
|
142
|
+
git push upstream HEAD
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Tokens normally last one hour; scoped API keys are limited to ten minutes or
|
|
146
|
+
their remaining lifetime. Git tokens are never saved in the remote URL or
|
|
147
|
+
repository configuration. Keep `avr` installed and logged in for later Git
|
|
148
|
+
operations; the helper continues to use the API host used for the clone.
|
|
149
|
+
|
|
150
|
+
**Create a mirror at a location**
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
avr repo mirror locations
|
|
154
|
+
avr repo mirror create foo/faa --location gsc-fi
|
|
155
|
+
avr repo mirror create group/subgroup/project --source gitlab --location gsc-fi
|
|
156
|
+
avr repo mirror status --repo group/subgroup/project --source gitlab
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`--source` defaults to `github`; `gitlab` supports nested group names. The
|
|
160
|
+
repository must already be connected to your Avrea organization. Use `--org`
|
|
161
|
+
to select an organization. Mirror management requires the organization's
|
|
162
|
+
Git-mirror feature and an admin role.
|
|
163
|
+
|
|
164
|
+
`--location` accepts a cluster ID, datacenter ID, or unique name from `locations`.
|
|
165
|
+
The command enables mirroring and adds the placement, preserving existing
|
|
166
|
+
placements. Sync runs asynchronously; use `status` to check it. Repeating
|
|
167
|
+
`create` is safe, including when enabling succeeded but placement failed.
|
|
168
|
+
`locations` and `create` also support `--json` and `--jq`.
|
|
169
|
+
|
|
170
|
+
GitLab repositories cannot be mirrored until GitLab mirroring is enabled
|
|
171
|
+
for your organization. `--source gitlab` is available for deployments that
|
|
172
|
+
support it, including repositories in nested groups. If a GitLab repository
|
|
173
|
+
returns “not found,” check its connection to Avrea, your access, and whether
|
|
174
|
+
GitLab mirroring is enabled.
|
|
175
|
+
|
|
120
176
|
**Watch a run live**
|
|
121
177
|
|
|
122
178
|
```sh
|
|
@@ -239,11 +295,32 @@ case $? in
|
|
|
239
295
|
esac
|
|
240
296
|
```
|
|
241
297
|
|
|
242
|
-
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error.
|
|
298
|
+
Exit code `4` is reserved for "auth required". `1` is general failure; `2` is a usage error. `130` means Ctrl-C
|
|
299
|
+
ended the command, including a watch, a log follow or a tunnel that is stopped that way.
|
|
300
|
+
|
|
301
|
+
**Scoped tokens for scripts and agents**
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
avr token create --name ci-read --org <org-id> --repo acme/api --ttl 8h # prints the credential once
|
|
305
|
+
avr token create --name agent --org <org-id> --vm <vm-id> --allow-vm-create --vm-create-limit 3
|
|
306
|
+
avr token list
|
|
307
|
+
avr token view <token-id>
|
|
308
|
+
avr token revoke <token-id>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
A scoped token is a short-lived credential (60 seconds to 7 days, 8 hours by default) bound to one organization. Repository grants are read-only and VM grants are admin: it reads only the repositories it names, has admin access to only the VMs it names, and cannot create further tokens. `create` prints the `export AVR_TOKEN=...` and `export AVR_ORG=...` lines for whatever will use the token. Scoped tokens require the `org-...` ID in `AVR_ORG` or `--org`; they ignore the login's stored organization and cannot resolve a slug. `avr auth status` reports this configuration without checking whether the token is still live. A 403 or 404 comes with a reminder that the token reaches only what it names.
|
|
312
|
+
|
|
313
|
+
`avr repo clone acme/api` uses the `owner/repository` name directly.
|
|
314
|
+
|
|
315
|
+
With a scoped token, a repository name (`--repo acme/api`, `AVR_REPO` or the checkout's remote) is matched against the repositories the token reaches; a `rep-...` ID works as well. For runs, use the run ID rather than an Avrea console URL, whose organization slug cannot be resolved with a scoped token. Token IDs for `view` and `revoke` are `key-` followed by 32 lowercase hexadecimal characters.
|
|
316
|
+
|
|
317
|
+
Creation returns the credential only once. If `--jq` fails or removes it, the command falls back to the requested JSON without filtering and exits nonzero. Save the credential from that JSON. Output failures also report a revoke command on stderr without exposing the credential there. If the creation request is interrupted, its response is lost, or the answer is a server error (5xx), a token may already exist: check `avr token list` before retrying. Only a 503 that carries the API's own reason means that no token was created.
|
|
318
|
+
|
|
319
|
+
If `avr token create` returns a 404, check the API's reason, the organization and the API host. If scoped-token access is unavailable, contact support@avrea.com to request it. VM grants and `--allow-vm-create` also require customer VMs to be enabled for the organization. `list`, `view` and `revoke` remain available when token creation is disabled.
|
|
243
320
|
|
|
244
321
|
**Pipe-aware output**
|
|
245
322
|
|
|
246
|
-
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
323
|
+
When stdout isn't a TTY, list commands switch to tab-separated rows (no color, no truncation, ISO timestamps), so `avr run list | awk` works without flags. Control characters, Unicode line separators and bidirectional controls inside cells are escaped (`\t`, `\x1b`, `\u2028`), keeping each item on one row with stable columns. The escaping is not reversible: a literal backslash is left as it is, so a cell that reads `a\tb` may hold either a tab or a backslash and a `t`. Use `--json` when the exact value matters; JSON retains the original values. `avr run watch | jq -c .` automatically switches to NDJSON event mode.
|
|
247
324
|
|
|
248
325
|
## Configuration
|
|
249
326
|
|
|
@@ -252,8 +329,8 @@ When stdout isn't a TTY, list commands switch to tab-separated rows (no color, n
|
|
|
252
329
|
| Variable | Purpose |
|
|
253
330
|
| ---------------- | ------------------------------------------------------------------------------------------------ |
|
|
254
331
|
| `AVR_HOST` | Avrea API URL. Defaults to the active host in `hosts.json`, then `https://api.avrea.com`. |
|
|
255
|
-
| `AVR_TOKEN` | API key. Overrides whatever's stored for the active host.
|
|
256
|
-
| `AVR_ORG` | Default organization ID. Overrides the stored default.
|
|
332
|
+
| `AVR_TOKEN` | API key or scoped token. Overrides whatever's stored for the active host. |
|
|
333
|
+
| `AVR_ORG` | Default organization ID. Overrides the stored default. A scoped token needs it, or `--org`, except for `repo clone`. |
|
|
257
334
|
| `AVR_REPO` | Default repository (`org/name` or `rep-xxx`). Overrides git auto-detect. |
|
|
258
335
|
| `AVR_BROWSER` | Browser to launch for `--web` and OAuth login. Falls back to `BROWSER`, then system default. |
|
|
259
336
|
| `AVR_PAGER` | Pager for long output. Overrides `PAGER`. Set to empty string to disable paging. |
|
|
@@ -18,6 +18,7 @@ avrea_cli/config.py
|
|
|
18
18
|
avrea_cli/display.py
|
|
19
19
|
avrea_cli/docs_gen.py
|
|
20
20
|
avrea_cli/firewall.py
|
|
21
|
+
avrea_cli/git_mirror.py
|
|
21
22
|
avrea_cli/helpers.py
|
|
22
23
|
avrea_cli/json_output.py
|
|
23
24
|
avrea_cli/log_display.py
|
|
@@ -29,11 +30,13 @@ avrea_cli/repo_context.py
|
|
|
29
30
|
avrea_cli/run_diagnostics.py
|
|
30
31
|
avrea_cli/run_diagnostics_display.py
|
|
31
32
|
avrea_cli/run_refs.py
|
|
33
|
+
avrea_cli/spawn_script.py
|
|
32
34
|
avrea_cli/transport.py
|
|
33
35
|
avrea_cli/version.py
|
|
34
36
|
avrea_cli/vm.py
|
|
35
37
|
avrea_cli/assets/logo_mark.svg
|
|
36
38
|
avrea_cli/commands/__init__.py
|
|
39
|
+
avrea_cli/commands/artifact.py
|
|
37
40
|
avrea_cli/commands/auth_cmd.py
|
|
38
41
|
avrea_cli/commands/cache.py
|
|
39
42
|
avrea_cli/commands/config_cmd.py
|
|
@@ -48,8 +51,10 @@ avrea_cli/commands/run.py
|
|
|
48
51
|
avrea_cli/commands/sbom.py
|
|
49
52
|
avrea_cli/commands/settings.py
|
|
50
53
|
avrea_cli/commands/status.py
|
|
54
|
+
avrea_cli/commands/token.py
|
|
51
55
|
avrea_cli/commands/workflow.py
|
|
52
56
|
tests/test_api_client.py
|
|
57
|
+
tests/test_artifact.py
|
|
53
58
|
tests/test_auth.py
|
|
54
59
|
tests/test_auth_login.py
|
|
55
60
|
tests/test_auth_sso.py
|
|
@@ -75,6 +80,7 @@ tests/test_output.py
|
|
|
75
80
|
tests/test_piped_output.py
|
|
76
81
|
tests/test_pr_list.py
|
|
77
82
|
tests/test_public_mirrors.py
|
|
83
|
+
tests/test_repo_clone.py
|
|
78
84
|
tests/test_repo_context.py
|
|
79
85
|
tests/test_repo_mirror.py
|
|
80
86
|
tests/test_run_attempts.py
|
|
@@ -85,9 +91,13 @@ tests/test_run_logs.py
|
|
|
85
91
|
tests/test_run_refs.py
|
|
86
92
|
tests/test_run_view.py
|
|
87
93
|
tests/test_sbom.py
|
|
94
|
+
tests/test_scoped_token.py
|
|
88
95
|
tests/test_settings.py
|
|
96
|
+
tests/test_token.py
|
|
97
|
+
tests/test_token_output.py
|
|
89
98
|
tests/test_transport.py
|
|
90
99
|
tests/test_vm.py
|
|
100
|
+
tests/test_vm_spawn_script.py
|
|
91
101
|
tests/test_web_flag.py
|
|
92
102
|
tests/test_workflow_actions.py
|
|
93
103
|
tests/test_workflow_run.py
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Auto-generated by .github/workflows/release.yml; do not edit.
|
|
2
|
-
GIT_COMMIT = "
|
|
2
|
+
GIT_COMMIT = "620a47d50c11e7e1905980829c01fe2d974fe837"
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from avrea_cli.config import CliConfig
|
|
4
4
|
from avrea_cli.transport import CompressingTransport
|
|
5
|
+
from collections.abc import Iterator
|
|
5
6
|
from typing import Any
|
|
6
7
|
import click
|
|
7
8
|
import httpx
|
|
@@ -76,6 +77,32 @@ class ApiClient:
|
|
|
76
77
|
response.raise_for_status()
|
|
77
78
|
return response.content
|
|
78
79
|
|
|
80
|
+
def public_download(self, path: str) -> Iterator[bytes]:
|
|
81
|
+
"""Stream an authenticated response without following credential redirects."""
|
|
82
|
+
url = f"{self.config.public_api_url}{path}"
|
|
83
|
+
with self._http.stream(
|
|
84
|
+
"GET",
|
|
85
|
+
url,
|
|
86
|
+
headers={**self.config.get_api_headers(), "Accept-Encoding": "identity"},
|
|
87
|
+
timeout=self.timeout,
|
|
88
|
+
follow_redirects=False,
|
|
89
|
+
) as response:
|
|
90
|
+
self._log("GET", str(response.request.url), response.status_code)
|
|
91
|
+
if not response.is_success:
|
|
92
|
+
# Error formatting needs a buffered body, but a failed download
|
|
93
|
+
# must not buffer an arbitrarily large response.
|
|
94
|
+
body = bytearray()
|
|
95
|
+
for chunk in response.iter_bytes(chunk_size=4096):
|
|
96
|
+
body.extend(chunk)
|
|
97
|
+
if len(body) > 64 * 1024:
|
|
98
|
+
body.clear()
|
|
99
|
+
break
|
|
100
|
+
headers = {"Retry-After": response.headers["Retry-After"]} if "Retry-After" in response.headers else {}
|
|
101
|
+
httpx.Response(
|
|
102
|
+
response.status_code, headers=headers, content=bytes(body), request=response.request
|
|
103
|
+
).raise_for_status()
|
|
104
|
+
yield from response.iter_bytes(chunk_size=1024 * 1024)
|
|
105
|
+
|
|
79
106
|
def public_post(
|
|
80
107
|
self,
|
|
81
108
|
path: str,
|
|
@@ -42,6 +42,11 @@ _LOGO_SVG = (Path(__file__).parent / "assets" / "logo_mark.svg").read_text(encod
|
|
|
42
42
|
HOSTS_FILE = PATHS.hosts_file
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def is_scoped_token(token: str | None) -> bool:
|
|
46
|
+
"""Identify organization-bound credentials without making a request."""
|
|
47
|
+
return token is not None and token.startswith("avs_")
|
|
48
|
+
|
|
49
|
+
|
|
45
50
|
def _read() -> dict[str, Any]:
|
|
46
51
|
"""Load the hosts file. Returns an empty wrapper if missing or malformed
|
|
47
52
|
— callers treat absence as 'no credentials yet'."""
|
|
@@ -6,6 +6,9 @@ import importlib
|
|
|
6
6
|
|
|
7
7
|
SECTION_ORDER = ["Core Commands", "Setup & Config", "Additional Commands"]
|
|
8
8
|
|
|
9
|
+
# 128 + SIGINT, the shell convention for a command ended by Ctrl-C.
|
|
10
|
+
EXIT_INTERRUPTED = 130
|
|
11
|
+
|
|
9
12
|
LEARN_MORE = 'Use "avr <command> <subcommand> --help" for more information about a command.\nRead the docs at https://docs.avrea.com/cli'
|
|
10
13
|
|
|
11
14
|
|
|
@@ -188,6 +191,24 @@ class AliasGroup(GhHelpMixin, LazyGroup):
|
|
|
188
191
|
finally:
|
|
189
192
|
self._current_section = prev
|
|
190
193
|
|
|
194
|
+
def invoke(self, ctx: click.Context):
|
|
195
|
+
"""Exit 130 when Ctrl-C ends the command.
|
|
196
|
+
|
|
197
|
+
Click reports an interrupt as exit 1, the same as a general failure.
|
|
198
|
+
A prompt turns the interrupt into ``click.Abort`` before it gets here,
|
|
199
|
+
so that case is recognised by the exception it replaced. An ``Abort``
|
|
200
|
+
raised for any other reason, end of input included, keeps exit 1.
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
return super().invoke(ctx)
|
|
204
|
+
except KeyboardInterrupt:
|
|
205
|
+
click.echo(err=True)
|
|
206
|
+
except click.Abort as exc:
|
|
207
|
+
if not isinstance(exc.__context__, KeyboardInterrupt):
|
|
208
|
+
raise
|
|
209
|
+
click.echo("Aborted!", err=True)
|
|
210
|
+
raise click.exceptions.Exit(EXIT_INTERRUPTED)
|
|
211
|
+
|
|
191
212
|
def add_command(self, cmd, name=None):
|
|
192
213
|
super().add_command(cmd, name)
|
|
193
214
|
if self._current_section is not None:
|