.gitbay/wiki/Parity.org

e9566eed86ebcd185c4b85f63d667b5e671fe787
gitbay/.gitbay/wiki/Parity.org rendered · source · history · blame · raw

462 lines · 23513 bytes

  1#+title: Parity
  2
  3Which surface can do what. The CLI is meant to be the complete
  4interface: every capability exists over SSH, and the other surfaces
  5dispatch the same control commands rather than reimplementing them, so
  6they cannot drift. This page is updated in the merge request that
  7changes a row.
  8
  9Three surfaces now: the CLI over SSH, the web, and the iOS client
 10(krz/gitbay-ios). A =no= in the cli column is a defect, not a
 11preference — it means some surface reached around the registry and
 12stranded a capability where only it can reach.
 13
 14* Rule
 15
 16A capability lands over SSH first. If it belongs to the
 17triage/review/respond loop, it lands on the web in the same merge
 18request. Nothing is held back from a surface any more (#234): the
 19registry has no flag for it, and what a caller may do is the account's
 20rights narrowed by the scope of the key or token it arrived with,
 21decided in one place. A =no= in the web or ios column is a page nobody
 22has built yet, not a refusal.
 23
 24Rows are one page or one action each. Grouped rows hide gaps, twice
 25now: "browse, log, blame, search" read as covered while blame had no
 26command at all, and "build list, log" read as covered while the job
 27list a trigger can name had none (krz/gitbay#50), leaving the picker
 28browser-only and the iOS build screen unable to say more than the log.
 29
 30* Merge requests
 31
 32| capability             | cli | web | ios |
 33|------------------------+-----+-----+-----|
 34| read, diff, commits    | yes | yes | yes |
 35| review and check times | yes | yes | yes |
 36| who resolved it, when  | yes | yes | yes |
 37| comment                | yes | yes | yes |
 38| edit title and body    | yes | yes | yes |
 39| review (approve etc.)  | yes | yes | yes |
 40| resolve a thread       | yes | yes | yes |
 41| comment on a diff line | yes | yes | yes |
 42| merge (all strategies) | yes | yes | yes |
 43| close                  | yes | yes | yes |
 44| close in favour of another | yes | yes | yes |
 45| create                 | yes | yes | yes |
 46| draft, ready           | yes | yes | yes |
 47| search title and body  | yes | yes | yes |
 48| create from a fork     | yes | yes | yes |
 49| retarget               | yes | yes | yes |
 50| milestone              | yes | yes | yes |
 51| labels                 | yes | yes | no  |
 52| filter by label        | yes | yes | no  |
 53| request a review       | yes | yes | yes |
 54| choose body markup     | yes | yes | yes |
 55| preview body markup    | n/a | yes | no  |
 56| stacked merge requests | yes | yes | yes |
 57| revisions              | yes | yes | yes |
 58| range-diff             | yes | no  | yes |
 59| merge gates            | yes | yes | yes |
 60
 61Reviews and checks carry the time they last said something, and a
 62=ci/<job>= check carries how long its build ran, so a merge request
 63reads without opening the build. Statuses posted through =status set=
 64have no build and report only the time. A merged or closed merge
 65request names who resolved it and when; a row without that stamp — a
 66=migrate= import, or one merged before krz/gitbay!137 with no
 67=mr.merged= event to backfill from — says neither rather than
 68inventing a time.
 69
 70A merge request whose target is another open merge request's source
 71branch is stacked on it: =mr show= and the page say so both ways, and
 72when the lower one merges, everything stacked on it is retargeted onto
 73what it merged into with its reviews kept. A squash or rebase merge is
 74refused while anything is stacked on the merge request, since it would
 75rewrite the commits the stack builds on. All three surfaces show the
 76stack both ways.
 77
 78A draft merge request is open but not asking: it does not merge, and it
 79does not appear in anyone's review queue. Draft is a flag rather than a
 80fifth state, so every =state = 'open'= rule still means what it did.
 81=mr revisions= lists the heads a merge request has had; the web page
 82lists them beside the reviews they staled, and the iOS client has a
 83Revisions section. =mr range-diff= compares two heads: the iOS client
 84shows it from a revision to the one before, as text; the web has no
 85view. Batched review is not built.
 86
 87=mr review request --add <user>= asks a *particular* person, who then
 88carries the merge request in their queue and is notified; =--remove=
 89withdraws the ask. Without one the queue is computed from involvement —
 90what you own, are granted, or reach through an org or team — so a
 91collaborator with write access who has not touched a thread hears
 92nothing until they do (krz/gitbay#145).
 93
 94Creating from a fork works anywhere the source can be typed as
 95=owner/name:branch=. The web's source picker offers the branches of
 96every fork the viewer can push to in that form, and the repository
 97header has the fork control, so the whole path — fork, edit a file,
 98propose — runs in a browser. Retargeting moves an open
 99merge request onto another branch of the same repository and stales the
100reviews, since an approval was of the diff against the old branch.
101
102* Issues
103
104| capability          | cli | web | ios |
105|---------------------+-----+-----+-----|
106| read, list, filter  | yes | yes | yes |
107| filter by label, assignee, author, milestone | yes | yes | yes |
108| search title and body | yes | yes | yes |
109| create              | yes | yes | yes |
110| comment             | yes | yes | yes |
111| edit title and body | yes | yes | yes |
112| close and reopen    | yes | yes | yes |
113| labels, assignees   | yes | yes | yes |
114| milestone           | yes | yes | yes |
115| milestone list      | yes | yes | yes |
116| labels: list, colour | yes | yes | yes |
117| choose body markup  | yes | yes | yes |
118| preview body markup | n/a | yes | no  |
119| issue templates     | yes | yes | yes |
120| milestone create, close, reopen | yes | no | yes |
121| org labels: set, list, remove | yes | list | yes |
122| org milestones: create, list, close, reopen | yes | list | yes |
123| closes across repositories | yes | yes | yes |
124
125Labels are created on the fly by =issue label --add= and =mr label
126--add=, and managed by =label list=, =label set <label> --color rrggbb=
127and =label remove=, which takes the label off every issue and merge
128request. One set serves both. The web paints the stored colour on every
129chip and derives one from the name when none is set. The set itself is
130at =/<owner>/<repo>/labels=, linked from the issue list: create,
131recolour and remove, dispatching the same commands.
132
133Org labels and milestones are managed on the CLI, the API and the iOS
134client's org screen; =/<org>/-/labels= and =/<org>/-/milestones= show
135them on the web. The repository
136label page's form exists for colour alone, and three org forms nobody
137asked for were not worth their handlers.
138
139Issue, MR and release bodies, and their comments, carry the markup they
140were written in — =--format md|org= on create, comment and edit, stored
141alongside the text so changing a preference later cannot reinterpret
142prose that already exists. Every surface *renders* the stored format, and
143every surface now offers the choice: the web's create forms, and the iOS
144composer on create, edit and comment. An edit starts on the format its
145body was stored in, since starting elsewhere would silently reinterpret
146it on the next save. Diff-line comments have no format column and are
147always markdown.
148
149Every web form that takes markup has a Preview button beside its own
150submit: issue and merge request create, their edit and comment boxes,
151release create and edit, and the file editor on
152a path the forge renders. It posts to the form's own action, which
153renders the draft and hands the page back without writing, so what you
154see is the rendering the thread will show, autolinks included. It is a
155round trip rather than a live preview because the instance serves no
156JavaScript, the same way the blob page's rendered/source toggle works.
157The row is =n/a= on the CLI: a terminal has no form to preview, and
158=issue show= already renders. Diff-line comments are left out — they
159sit inside the diff, where a page-level preview has nowhere to go.
160
161* Repositories
162
163| capability                  | cli | web | ios |
164|-----------------------------+-----+-----+-----|
165| browse files                | yes | yes | yes |
166| read a file                 | yes | yes | yes |
167| commit log                  | yes | yes | yes |
168| commit log at a ref         | yes | yes | yes |
169| one commit with its patch   | yes | yes | yes |
170| blame                       | yes | yes | yes |
171| search file contents        | yes | yes | yes |
172| compare two refs            | yes | yes | yes |
173| branches and tags           | yes | yes | yes |
174| wiki (read)                 | yes | yes | yes |
175| download an archive         | yes | yes | n/a |
176| edit a file                 | yes | yes | yes |
177| preview an edited markup file | n/a | yes | no  |
178| create                      | yes | yes | yes |
179| fork                        | yes | yes | yes |
180| pin                         | yes | yes | yes |
181| bookmark                    | yes | yes | yes |
182| bookmark list               | yes | yes | yes |
183| watch, unwatch              | yes | yes | yes |
184| mute                        | yes | no  | yes |
185| settings, protection        | yes | yes | yes |
186| default branch              | yes | yes | yes |
187| merge requests only         | yes | yes | yes |
188| protected tags              | yes | yes | yes |
189| require codeowners          | yes | yes | yes |
190| access grants               | yes | no  | yes |
191| effective access            | yes | no  | yes |
192| webhooks                    | yes | no  | yes |
193| runners attach, list, detach | yes | yes | n/a |
194| import from a remote        | yes | no  | yes |
195| topics, website             | yes | yes | yes |
196| visibility                  | yes | yes | yes |
197| archive (read-only flag)    | yes | yes | yes |
198| release list, show          | yes | yes | yes |
199| atom feeds                  | n/a | yes | n/a |
200| release create, edit        | yes | yes | yes |
201| preview release notes       | n/a | yes | no  |
202| build list                  | yes | yes | yes |
203| build list paging (limit, cursor) | yes | yes | yes |
204| build list filters (ref, status, job) | yes | yes | yes |
205| build show (one build)      | yes | yes | yes |
206| build log                   | yes | yes | yes |
207| build jobs                  | yes | yes | yes |
208| build trigger               | yes | yes | yes |
209| build cancel                | yes | yes | yes |
210| job image (ci.yml)          | yes | n/a | n/a |
211| dependency checks on/off    | yes | yes | yes |
212| dependency status           | yes | yes | yes |
213| delete, transfer            | yes | no  | no  |
214| rename                      | yes | no  | yes |
215| release delete              | yes | yes | yes |
216| release asset add           | yes | no  | n/a |
217| release asset remove        | yes | no  | yes |
218| snippet create, edit, delete  | yes | yes | yes |
219| snippet show, list            | yes | yes | yes |
220| snippet file set, get, remove | yes | yes | yes |
221
222No row is web-only any more. Blame, file editing, log at a ref,
223archive, the public listing and the wiki were all in that state — a
224handler reading git or the store directly instead of dispatching a
225command — until krz/gitbay!99, !101 and !102 gave them commands.
226
227=build cancel= withdraws a queued build, or ends a running one: the
228server closes the runner's log session and the runner kills the step
229within a couple of seconds. Cancelling a duplicate of a commit that
230already passed the job puts that result back on the commit. The build
231page carries the button while a build is still cancellable.
232
233A pin and a bookmark are different things and are stored separately. A
234pin is private quick access to what you are working on, and drives the
235rail; a bookmark is public, says a repository is worth coming back to,
236and its count is the only popularity signal on the instance. Bookmarking
237needs read access only — it is something you do to someone else's
238repository — and a repository bookmarked while public and since made
239private drops out of the listing rather than leaking that it exists
240(krz/gitbay#146).
241
242The web's watch and pin controls write the store directly instead of
243dispatching =repo watch= and =repo pin=. That is why the web cannot
244mute: its toggle knows watching and default only.
245
246Dependency checks are off until a repository's admin turns them on: the
247check tells a public registry what the repository depends on. =repo deps
248status= lists what is behind; the repository's settings page renders the
249same report under the toggle — last check, last error, the tracking
250issue, and the rows. The issue the worker opens, rewrites and closes is
251still the copy every other surface reads.
252
253The wiki row covers reading. A wiki lives at =.gitbay/wiki/= on the
254default branch, so editing one is editing a file in the repository —
255a push, or =repo commit-file= — and there is no wiki-specific edit row
256to have parity on. The web editor reaches it on any repository that
257permits server-authored commits; one requiring verified signatures
258does not, because the server signs nothing on a user's behalf, so
259those wikis are push-only.
260
261=n/a= marks a capability deliberately absent from a surface rather than
262missing from it. Archive download is =n/a= on iOS: the read API carries
263a command's stdout as a JSON string, so a gzip stream cannot survive it,
264and the app has nowhere useful to put a tarball — the brief rules out
265local git, so there is no clone, checkout or build to feed. The web's
266route stays the way to get one. =release asset add= is =n/a= on iOS for
267the same reason from the other direction: the file arrives on stdin,
268which the API carries as a JSON string. Attaching a runner is =n/a= on
269iOS for a plainer reason: the key is generated by =gitbay-runner init=
270on the machine that will run builds, and that machine's terminal (the
271command =init= prints) or the settings page is where the paste happens.
272A phone has neither the key nor the runner.
273
274A README or wiki page's org renders per surface: the web through
275go-org, the iOS client through the shared OrgSwift package
276(=krz/org-swift=). OrgSwift is held to orgo's output by the
277=krz/org-conformance= corpus — golden renderings from orgo, itself
278diffed against Emacs =ox-html= — so constructs the iOS client used to
279approximate (tables, footnotes, timestamps, heading tags, nested lists)
280now render the way the reference does. go-org is not yet on the corpus.
281
282* Discovery
283
284| capability                       | cli | web | ios |
285|----------------------------------+-----+-----+-----|
286| search repositories              | yes | yes | yes |
287| search issues and merge requests | yes | yes | yes |
288| browse all public repositories   | yes | yes | yes |
289| profile page                     | yes | yes | yes |
290| profile sections as tabs         | n/a | yes | n/a |
291| profile about and links          | yes | yes | yes |
292| profile about as a file          | yes | yes | yes |
293| activity feed                    | yes | yes | yes |
294| command reference                | yes | no  | n/a |
295
296=explore= is the listing without a query; =repo search= is the one with.
297=search= is both plus the title and body of every issue and merge
298request the caller can read, over FTS5; the web serves it at =/search=
299with a field in the rail, and an anonymous visitor gets the public rows
300from the same query. =issue list --search= and =mr list --search= narrow
301one repository. What someone types is quoted term by term, so an FTS5
302operator — =c++=, =AND=, a lone quote — is a word to match and never a
303syntax error. =repo grep= remains the per-repository file-contents
304search.
305
306The about text is =profile/README.{md,org,markdown}= on the default
307branch of =<owner>/.gitbay=, resolved in that order, so the extension
308picks the renderer rather than a stored format. =profile show= reports
309it as =about=, =about_format= and =about_path=. It reads with the
310repository's own access, so a private =.gitbay= is a profile with no
311about text to anyone but its owner and the admins. A repository whose
312name starts with a dot stays out of =explore= and off the profile's
313repository list. On the web the about text is its own tab: =/<owner>=
314is the repository list, =/<owner>/-/about= the text, =/<owner>/-/activity=
315the year of squares, and =/<owner>/-/people= an organization's members
316and teams. A tab with nothing on it is not offered and its URL is a
317404. The CLI's =profile show= is unchanged and still returns all of it
318at once. The iOS client decodes and renders both formats,
319through the same OrgSwift path a README takes.
320
321=help= lists the command registry. Bare it is an index, one line per
322command, sorted. With a prefix (=help mr=) it adds each command's
323argument syntax, which is the only place flags are written down;
324=gitbay <cmd> --help= asks the server for the same thing, and =--json=
325returns ={path, summary, usage}=. No web page renders it, and a native
326client has no use for one (krz/gitbay#57).
327
328* Accounts
329
330| capability                  | cli | web | ios |
331|-----------------------------+-----+-----+-----|
332| SSH keys: list, add, remove | yes | yes | yes |
333| SSH key label               | yes | yes | yes |
334| PGP keys: list, add, remove | yes | yes | yes |
335| email add and verify        | yes | yes | yes |
336| email list, remove, primary | yes | yes | yes |
337| dashboard aggregate         | yes | yes | yes |
338| notification inbox          | yes | yes | yes |
339| activity mail on, off       | yes | yes | yes |
340| watch writable repos        | yes | yes | yes |
341| push device add             | yes | yes | yes |
342| push device list            | yes | yes | yes |
343| push device remove          | yes | yes | yes |
344| activity push on, off       | yes | yes | yes |
345| web colour scheme           | yes | yes | n/a |
346| API token mint              | yes | no  | no  |
347| account export bundle       | yes | yes | n/a |
348| profile set                 | yes | yes | yes |
349| write the profile about     | yes | yes | yes |
350| request a login link        | n/a | yes | n/a |
351| account import bundle       | yes | no  | n/a |
352
353Notifications land in an inbox row per recipient whether or not the
354instance sends mail, and mail is the second half when SMTP is
355configured. =notifications list= reads it, unread by default;
356=notifications read <id>... | --all= clears it; the dashboard and the
357web rail carry the unread count. =repo watch= adds you to a
358repository's notifications, =repo mute= silences it, and =repo unwatch=
359returns you to the default from either — told about work you are part
360of, nothing more. =notifications settings watch on= widens that default
361to every repository you can write to, without a row per repository. A
362mute wins over owning the repository, having written the thread, or the
363preference.
364
365Push is the third leg beside inbox and mail, delivered to Apple devices
366an account has registered. =notifications device add= runs on every
367surface like every other command, but no web page offers a form for
368it, because only the iOS app can produce an APNs device token — a
369browser has no way to ask Apple for one. =device list= and =device
370remove= have no such limit and are yes on the web like the rest.
371
372A login link is requested from the login page by username or verified
373address, and arrives by mail: it works once and expires in fifteen
374minutes. The row is =n/a= for the CLI because a terminal with a
375registered key runs =web login=, which mints a link directly and needs
376no mail. It exists because an account with no SSH key had no way into
377the web at all (krz/gitbay#155). The page offers the form only when the
378instance has SMTP configured; there is no separate switch. The response
379never says whether the account exists. It is =n/a= on iOS for the same
380reason it is on the CLI, from the other end: the app authenticates with
381a bearer token pasted at sign-in, so a one-time link into the web
382unlocks nothing it can use.
383
384The account export bundle is =n/a= on iOS on the archive-download
385argument above — a JSON bundle has nowhere useful to land on a phone,
386and the web route stays the way to get one.
387
388=profile set= carries description, website and links; the JSON API runs
389it like any other write. The account settings page has the form, and so
390does the iOS client: =--link= replaces the whole set rather than
391appending, so a client sends every link it keeps on every save, and
392=--link ''= is how they are cleared.
393
394The about text is not among those flags. It is a file, written by a push
395or =repo commit-file= like any other file, which is why writing it is
396yes on every surface: anything that can commit a file can write it. The
397settings page points at the file and offers to create =<owner>/.gitbay=
398when there is none.
399
400* Administration
401
402| capability                      | cli | web | ios |
403|---------------------------------+-----+-----+-----|
404| account list, filter by state   | yes | yes | no  |
405| account show                    | yes | no  | no  |
406| promote, demote                 | yes | yes | no  |
407| disable, enable                 | yes | yes | no  |
408| account create, delete          | yes | no  | no  |
409| invite                          | yes | no  | no  |
410| worker queues                   | yes | yes | no  |
411| runners                         | yes | no  | no  |
412| repository list, archive, visibility | yes | no | no |
413| audit log                       | yes | no  | no  |
414| instance statistics             | yes | no  | no  |
415
416=/admin/users= carries the account list with the command's state
417filter and cursor, and promote, demote, disable and enable per row;
418demote and disable ask for the username to be typed. The rest is a
419page nobody has built yet rather than a refusal — see below. Account
420creation and deletion stay on the command line on purpose: one takes a
421key, the other cannot be undone.
422
423* Organizations
424
425| capability                | cli | web | ios |
426|---------------------------+-----+-----+-----|
427| read members and teams    | yes | yes | yes |
428| members add, remove, role | yes | yes | yes |
429| teams create, delete      | yes | yes | yes |
430| team members              | yes | yes | yes |
431| team repo grants          | yes | yes | yes |
432| create, rename            | yes | yes | yes |
433| delete                    | yes | n/a | n/a |
434| profile                   | yes | no  | yes |
435
436* Pagination
437
438=issue list=, =mr list=, =repo list=, =feed= and =build list= take
439=--limit <n>= and =--cursor <c>=. Cursors are opaque; each page carries
440the next one. Without the flags a list stays as it was, so existing
441scripts are unchanged — for =build list= that means the newest fifty
442matching builds, which is what the cursor now reaches past. The web
443pages the issue and merge request lists at fifty and the builds list at
444thirty with the same cursors; iOS pages with them too.
445
446* CLI only, for now
447
448Build secrets, mirror configuration and tokens, custom domain claims,
449API token minting, web session listing and revocation, deploy keys,
450and instance administration have no page yet. Until #234 these were
451refused outright on the other surfaces; the refusal is gone, so each is
452now a page waiting to be built rather than a rule. A credential still
453travels on stdin wherever it is set, since argv is world-readable in
454/proc and the audit log keeps flag values.
455
456Deleting, transferring or renaming a repository stays CLI-only on
457purpose, as does deleting an organization and pruning merge request
458heads (=admin mr prune=): each removes or moves what clone URLs point
459at, and wants a typed command rather than a button.
460
461=n/a= means a surface cannot usefully carry the capability at all —
462see the archive note above.