Add tog config subcommand (set, get, path, init)#79
Merged
Conversation
Capture the architectural decisions behind the upcoming `tog config` verbs (set/get/path/init), the key × scope matrix, asymmetric token input model, resolved-view pure function, secrets-manager non-resolution stance, module boundaries, and permissions/atomicity. Fixes #70
Establish the `tog config` Cobra group and ship the first read-only verb: `path`. Bare prints the global config path (honours XDG_CONFIG_HOME). `--local` walks up for `.tog.yml`; on a miss, prints "would be created at <path>" using the git toplevel or PWD fallback. Exports `config.Path()` and adds `projectcfg.ResolveWritePath(cwd)` which subsequent slices reuse for the `--local` write path. Fixes #71
Add `internal/configview.Resolve` as a pure function that produces a value + source view for token/workspace/project given a global config, project config, and environment lookup. This is the single source of truth for precedence and is used by `tog config get`. The `get` command supports: - Bare form: prints all three keys with resolved value + source. - Single-key form: just that key's value + source (script-friendly). - `--global` / `--local`: filters to one file rather than the resolved view. - Token redacted to `****<last-4>` by default; `--reveal` prints the full token for the rare migration case. Fixes #72
internal/config gains Save / SaveTo: atomic temp-file-then-rename in the target directory, parent dir created with 0700 if missing, and file perms clamped to 0600 on every write — including over an existing 0644 file. `tog config set workspace <id>` writes through to the global config, mapping the CLI key `workspace` to YAML `default_workspace_id`. The key × scope matrix rejects `set project --global` (no project field in global), unknown keys, and non-numeric workspace values with specific error messages. `set token <value>` is rejected with the security message; bare `set token` returns a not-yet-implemented placeholder pending #75. Fixes #73
internal/projectcfg gains Save: atomic temp-file-then-rename in the target directory and 0644 clamp on every write. `tog config set --local` writes through to the project .tog.yml discovered via ResolveWritePath, preserving any existing fields. The new path is printed to stderr on first creation so the user knows where future invocations from a subdirectory will find the file. `tog config set token --local` is rejected with the security-specific message: the token must not be committed to .tog.yml. Fixes #74
Replace the placeholder rejection with the real token-input path. Bare `set token` opens a huh password prompt (echo mode hidden); `--stdin` reads a single line from stdin for piping from secrets managers (`op read | tog config set token --stdin`). The positional form is still rejected — a bare value would leak the token to shell history, scrollback, and session recordings. The error points at the two safe alternatives. Fixes #75
internal/toggl gains a Workspace struct, ListWorkspaces() against GET /me/workspaces, and an ErrUnauthorized sentinel wrapping 401 responses so init can loop the token prompt on rejection without string-matching the API error message. tog config init drives the bootstrap flow: TTY-only, prompts for overwrite when the global config already has values, asks for the token (huh password input), validates via /me, loops on 401 and offers retry on network errors. With exactly one workspace the choice is auto-selected; otherwise a huh selector picks. The resulting config is written atomically with 0600 perms via the existing config.SaveTo writer. Fixes #76
Detect TOGGL_API_TOKEN at the start of init and offer to keep the token in the vault rather than write it to disk — the workflow for op run / 1Password users who want only the workspace choice to persist. Declining the save-to-disk prompt validates against the env-var token via /me, runs the workspace flow, and writes a config with an empty token field. Accepting falls through to the normal token-prompt path from #76. Fixes #77
Three new/updated documents now describe the shipped tog config surface: - docs/commands/config.md — full reference for init/set/get/path, the key × scope matrix, and worked examples for new-user init, token rotation, per-project defaults, and the get debugging flow. - docs/secrets.md — op run patterns for both native and containerised use, plus brief Bitwarden and pass notes so the page isn't 1Password-only. Documents the wrapper-script env-var passthrough and the bind-mounted set --stdin behaviour. - README — config row added to the Command Reference table; the Configuration section points at docs/secrets.md. Fixes #78
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements all vertical slices for #61.
Slices
Test plan
go test ./...— all packages passgofmt -l .— cleantog config initend-to-end (token entry, workspace selection, file written with 0600)TOGGL_API_TOKEN=… tog config init— workspace-only flow, on-disk token field emptytog config getresolved view matches actual runtime behaviour on a system with both global config and.tog.yml