Skip to main content

ytcli/cli/
guidance.rs

1//! Where to get credentials.
2//!
3//! Printed when someone is stuck rather than on every run: a first login, a
4//! rejected token, an organisation that answers 403. Getting a Tracker token
5//! means creating an OAuth application and hand-assembling an authorize URL,
6//! which is not something anyone guesses, and the organisation id lives behind a
7//! page most people have never opened.
8//!
9//! The blocks are written as markdown and rendered for a terminal, for the same
10//! reason issue descriptions are: a numbered procedure with a table in it reads
11//! as a procedure, not as punctuation. Where nobody is watching — a pipe, a log,
12//! a CI run — the markdown source goes out unrendered, because reflowed text
13//! with escape codes in it is worse to read in a log than the source was.
14
15use std::io::IsTerminal;
16
17/// How to obtain an OAuth token.
18pub const TOKEN: &str = "\
19## Get an OAuth token
20
21The quickest way needs none of this: in a terminal, `ytcli auth login` offers to
22sign you in through the browser with a short code, and `--device` does the same
23where there is no terminal. The steps below are for pasting a token yourself —
24CI, or an organisation that does not allow third-party applications.
25
261. Create an application at https://oauth.yandex.ru/client/new — pick
27   **For API access or debugging**, and grant `tracker:write` for full access
28   or `tracker:read` to stay read-only. Add `wiki:read` (and `wiki:write`) for
29   the `ytcli wiki` commands: a token without it is refused by the Wiki.
302. Copy the application's **ClientID** from its page.
313. Open `https://oauth.yandex.ru/authorize?response_type=token&client_id=<ClientID>`
32   and sign in. The token comes back in the address bar you land on.
33
34**Stay on one domain for both steps.** `oauth.yandex.com` works too, but it is a
35separate origin with its own cookies, so your browser may be signed in there as
36somebody else — or as nobody. An application created under one account and
37authorised under another produces a token for the wrong person, and the first
38sign of it is `auth status` naming a stranger.
39
40It looks like `y0__xAbc...`, roughly 60-90 characters.
41
42Docs: https://yandex.ru/support/tracker/en/api-ref/access
43";
44
45/// How to find the organisation id, and which flavour it is.
46pub const ORG: &str = "\
47## Find your organisation id
48
49Open https://tracker.yandex.ru/admin/orgs — it lists every organisation you are
50in, with its id, and says which kind each one is.
51
52| Kind | Id looks like | Flag |
53|:-|:-|:-|
54| Yandex 360 for Business | `1234567` | `--org-kind yandex360` |
55| Yandex Cloud Organization | `bpfaidqca8vd0m5jl3fp` | `--org-kind cloud` |
56
57Not sure which you have? Omit `--org-kind`: login tries both and reports which
58one answered. The two use different headers, and the wrong one returns **403** —
59which reads like a rights problem rather than a configuration mistake.
60
61Docs: https://yandex.ru/support/tracker/en/api-ref/access
62";
63
64/// What `auth login` is, above the two blocks.
65const INTRO: &str = "\
66Store a token for an account, and set up a profile to use it with.
67
68```
69ytcli auth login
70ytcli auth login --account work --org-id 1234567 --profile work
71ytcli auth login --account work --org-id 1234567 --dry-run
72```
73
74Run it with no arguments in a terminal and it walks you through each step,
75taking the token as a hidden password. Pass whatever you already know as flags
76and only the rest is asked for. Outside a terminal the flags are all there is,
77and the token is read from stdin.
78";
79
80/// Render one block for whoever is reading it.
81///
82/// Rendered only for a terminal, and to the terminal's own width: markdown
83/// wrapped to 80 columns in a 200-column window looks like a mistake, and
84/// escape codes in a captured log are one.
85#[must_use]
86pub fn block(markdown: &str) -> String {
87    if !std::io::stderr().is_terminal() {
88        return markdown.to_owned();
89    }
90    crate::render::markdown::render(markdown, width())
91}
92
93/// Both blocks, for a first run.
94#[must_use]
95pub fn full() -> String {
96    block(&format!("{TOKEN}\n{ORG}"))
97}
98
99/// The long help of `auth login`: what the command is, then the same two
100/// blocks, so `--help` answers the question without anyone having to fail first.
101#[must_use]
102pub fn login_help() -> String {
103    crate::cli::help::md(&format!("{INTRO}\n{TOKEN}\n{ORG}"))
104}
105
106/// The window, narrowed to a width prose is readable at.
107///
108/// A procedure wrapped to 200 columns is one long line with a number in front
109/// of it; nobody reads that as steps.
110fn width() -> usize {
111    crate::cli::terminal_width().clamp(40, 92)
112}