feat(accounts): an account names its forge and server, and the command line takes --forge and --server #205

Merged
james.maes merged 4 commits from feat/204-gitea-accounts-and-cli into main 2026-11-11 22:22:17 +00:00
4 Commits
Author SHA1 Message Date
james.maesandClaude Fable 5.2 edf2d5c6a6 docs(cli): a name two accounts share is refused with exit code 2, and the ADR says what happens to a record without a forge
From the third review round of #205, which found no defect in the code of
9b82c15. docs/CLI.md gave the wrong exit code; the test now states it. ADR
0010 says that a record without a forge in the file for other forges is
written back with an empty forge name.

Refs #204

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 16:22:07 -06:00
james.maesandClaude Fable 5.2 2b6e11abf8 fix(accounts): a change writes one file, a workspace's token is the one of the place it was opened for, and a name two accounts share is refused
From the review of 8d855cd (#205, second round).

- A change could write both account files (one to put a missing file back or
  to move an account that was in the wrong file, one for the change), and a
  failure between the two was not undone: a token put back for an account
  that was gone from disk, or an account on disk whose token was taken back.
  Whatever else has to be written is now written before the change, before
  the vault is touched; when that fails the change fails with nothing
  changed. The change itself writes one file, as it always did.
- A workspace made its listers from the record it was opened with and took
  its token from the record as the store has it now. A file changed in
  between could bring the token of one place to the listers of another. The
  token is now resolved for the place the workspace was opened for.
- What a later version put on an account is kept whatever record is saved:
  the wizard builds its record anew and dropped it.
- GitHub is the account that names no forge, and only that one. A record in
  the file for other forges that names none is not read as a GitHub account
  (it would get GitHub's listers and the default token): it stays there and
  cannot be used.
- Two accounts of one name (an older gclo can add a GitHub account with the
  name of one it does not see): sync --account, accounts edit and accounts
  remove refuse the name instead of acting on the first of the two.
- Tests for each, and for the order of the two writes. docs/CLI.md, ADR 0010,
  T13.

Refs #204

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 16:22:07 -06:00
james.maesandClaude Fable 5.2 f2e35881a4 fix(accounts): accounts on another forge are kept where an older gclo does not rewrite them, and a token is found only at the place it was saved for
From the review of #205.

- A gclo from before forges reads every account in accounts.json as a GitHub
  account and writes the file back with the fields it knows. It dropped the
  forge and the server of a Gitea account, which then read as a GitHub account
  in every version: its token would be asked for again and sent to GitHub.
  Accounts on another forge are now kept in accounts-forges.json, which such a
  version neither reads nor writes. Both files are changed under the one lock;
  a change writes only the file of the account it changes.
- The kind of forge is stored as text. A name this version does not know (a
  forge of a later version) makes that one account unusable, not the file
  unreadable, and is written back as it was. Fields this version does not know
  are written back as they were read.
- The vault entry of an account on another forge is made from its id, its
  forge and its server address as the file states them. An account whose
  address was changed in the file finds no token for the new address, and a
  record that names another account's id finds none of that account's.
- An account whose stored address is spelled another way can be edited; what
  the file says about the place stays as it is.
- accounts list --json gives the address a token is sent to, and nothing for
  an address that cannot be used; --forge does not repeat what it was given.
- The wizard refuses the default token for an account on another forge in the
  view model, not only by hiding the choice.
- Tests: an older version's rewrite (as it does it), the two files, the
  unknown forge and fields, the binding of a token to its place, the vault
  being put back under the account's entry, a workspace's own listers.
- docs/CLI.md, ADR 0010, threat model T13 and T16, CLAUDE.md.

Refs #204

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 16:22:06 -06:00
james.maesandClaude Fable 5.2 29375ee61d feat(accounts): an account names its forge and server, and the command line takes --forge and --server
Step 2 of Gitea support (#201): accounts and the command line can use a Gitea
server. The desktop app syncs such an account; its wizard adds GitHub accounts
only, for now.

- Account.Forge and Account.Server, left out of accounts.json when they are
  the default. They are set when an account is added and cannot be changed by
  an edit: a token that was saved for one server is not sent to another
  because an address was edited.
- The token of an account that is not on GitHub is kept under another entry
  than the account's id. A gclo from before forges reads every account as a
  GitHub account; it finds no token there, instead of sending a Gitea token to
  GitHub.
- The default token is a GitHub token: it is never handed to an account on
  another forge, and such an account cannot be saved as using it.
- ForgeEndpoint (engine) and AccountListers (view models) are the one place
  that pairs a forge, or an account, with its listers. An account whose stored
  forge or server cannot be used asks no server anything.
- Command line: --forge github|gitea and --server on sync, repos, orgs and
  accounts add; accounts list shows where an account lives; accounts edit and
  sync --account take no other place. The default token variable is per forge
  (GITHUB_TOKEN, GITEA_TOKEN), and one is never used for the other. Exit codes
  are by the kind of failure, whatever the forge.
- The app builds a workspace's listers from its account, and checks an edited
  account's token against the place the account is on.
- A refused server address is not repeated in a message.
- docs/CLI.md, README, ADR 0010, threat model T16.

Closes #204

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Signed-off-by: James Maes <james@kof22.com>
2026-11-11 16:22:06 -06:00