akshitkrnagpal / akshitkrnagpal/revcat
LoadLocal walk-up collides with ~/.revcat/config.json, causing false 'profile block is empty' error
- Ngôn ngữ chính
- Go
- Star
- 3
- Fork
- 0
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Mô tả
## Summary
`revcat auth login` succeeds and writes a valid `~/.revcat/config.json`, but every subsequent command (`auth status`, `doctor`, `projects list`, etc.) fails with:
```
/Users//.revcat/config.json: profile block is empty; rerun `revcat init`
```
This happens even immediately after a fresh login, with a config file that is valid JSON and contains a fully populated `profiles.default` block with non-expired tokens. Re-running `auth logout --all` + `auth login` reproduces the exact same error every time — it is not a stale-credential issue.
## Root cause
In `internal/auth/local_config.go`, `LoadLocal(startDir)` walks up from `cwd` looking for a file at the **relative** path `.revcat/config.json`, with no boundary check other than reaching filesystem root:
```go
func LoadLocal(startDir string) (*LocalConfig, error) {
dir, err := filepath.Abs(startDir)
...
for {
path := filepath.Join(dir, LocalConfigPath) // ".revcat/config.json"
if info, err := os.Stat(path); err == nil && !info.IsDir() {
return readLocalFile(path)
}
parent := filepath.Dir(dir)
if parent == dir {
return nil, ErrNoLocalConfig
}
dir = parent
}
}
```
Because the walk-up is not bounded at `$HOME`, for any working directory that is a descendant of the user's home directory (i.e. virtually everyone), the loop eventually reaches `$HOME` itself and checks for `$HOME/.revcat/config.json` — which **is** the global config file (see `globalFileName = ".revcat/config.json"` in `global_file.go`, joined with home in `resolve.go`'s fallback path construction).
`LoadLocal` finds this file and hands it to `readLocalFile`, which unmarshals it into `LocalConfig{ProjectID, Apps, Profile}` — a struct expecting a **singular** `"profile"` key. But the global file's actual shape (from `global_file.go`) is a **plural** `"profiles"` map:
```json
{
"profiles": {
"default": { "name": "default", "access_token": "...", ... }
}
}
```
Since the key doesn't match, `cfg.Profile` stays zero-valued, and `readLocalFile` hits:
```go
if cfg.Profile.AccessToken == "" && cfg.Profile.RefreshToken == "" {
return nil, fmt.Errorf("%s: profile block is empty; rerun `revcat init`", path)
}
```
...which is misleading, since the *global* profile block is not empty at all — it's just being parsed with the wrong schema entirely, and the code never reaches the correct `OpenGlobal()` branch in `Resolve()` (`resolve.go:98`) because the error from `LoadLocal` isn't `ErrNoLocalConfig`, so it's propagated as a hard failure (`resolve.go:94-96`).
## Repro
```sh
cd ~/some/project/nested/deep/enough # any dir under $HOME with no .revcat/ in the tree
revcat auth login # succeeds, writes ~/.revcat/config.json
revcat auth status # fails: "profile block is empty; rerun `revcat init`"
```
Confirmed the walk-up path collision directly:
```
/Users/aaa/Documents/Projects/InstaMail/.revcat/config.json
/Users/aaa/Documents/Projects/.revcat/config.json
/Users/aaa/Documents/.revcat/config.json
/Users/aaa/.revcat/config.json <-- $HOME, matches globalFileName, misparsed here
/Users/.revcat/config.json
/.revcat/config.json
```
Also confirmed the credential itself is valid: setting `REVCAT_REFRESH_TOKEN` (which short-circuits `Resolve()` before `LoadLocal` is ever called, per `resolve.go:64`) authenticates successfully and reaches the API.
## Suggested fix
Bound the walk-up in `LoadLocal` to stop before reaching `$HOME` (exclusive), e.g.:
```go
home, _ := os.UserHomeDir()
for {
if dir == home {
return nil, ErrNoLocalConfig
}
path := filepath.Join(dir, LocalConfigPath)
...
}
```
This preserves the intended precedence (repo-local `.revcat/config.json` found via walk-up, falling back to the global file) while preventing the walk-up from ever colliding with the global file's own path.
## Environment
- revcat 0.7.0 darwin/arm64 go1.26.2 (commit 7c8411d)
- macOS (Darwin 25.5.0)
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Đánh giá
Issue này chưa được đánh giá.