Config: .env = projects root only; derive each project's forge from git
Reduce .env to just REPOS_HOST_PATH (the projects root); runtime bootstrap moves to compose/defaults. Projects are the subdirectories of the single root — removed the project-directories feature (store table, service methods, /api/config/project-dirs, Settings section). Each project's forge is derived from its git remote (matched to a configured forge, else the bare host) and shown as a pill next to its name (State.Forge via scanner ForgeFor + svc.ForgeDisplay). Removed first-run .env seeding; forges + identity are managed in Settings. Added forge.HostOf. AGENT.md updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+21
-5
@@ -26,6 +26,7 @@ type State struct {
|
||||
Ahead int `json:"ahead"`
|
||||
Behind int `json:"behind"`
|
||||
Remotes []string `json:"remotes"`
|
||||
Forge string `json:"forge,omitempty"` // forge this project belongs to (derived from its remote)
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Error string `json:"error,omitempty"` // set if refreshing this repo failed
|
||||
}
|
||||
@@ -69,6 +70,9 @@ type Config struct {
|
||||
MaxDepth int
|
||||
Ignore []string
|
||||
Fetch bool
|
||||
// ForgeFor maps a repo's origin remote URL to a display label for the forge it
|
||||
// belongs to (a configured forge's name, else the bare host, else ""). May be nil.
|
||||
ForgeFor func(remoteURL string) string
|
||||
}
|
||||
|
||||
// ConfigFunc supplies the current scan configuration.
|
||||
@@ -123,7 +127,7 @@ func (s *Scanner) Refresh(ctx context.Context) {
|
||||
return
|
||||
default:
|
||||
}
|
||||
s.Index.set(s.refreshOne(ctx, p, cfg.Fetch))
|
||||
s.Index.set(s.refreshOne(ctx, p, cfg.Fetch, cfg.ForgeFor))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,10 +135,11 @@ func (s *Scanner) Refresh(ctx context.Context) {
|
||||
// mutating action so the UI reflects the new state without waiting for the next
|
||||
// full scan. It never fetches (network) — it only re-reads local state.
|
||||
func (s *Scanner) RefreshRepo(ctx context.Context, path string) {
|
||||
s.Index.set(s.refreshOne(ctx, path, false))
|
||||
cfg := s.cfgFn(ctx)
|
||||
s.Index.set(s.refreshOne(ctx, path, false, cfg.ForgeFor))
|
||||
}
|
||||
|
||||
func (s *Scanner) refreshOne(ctx context.Context, path string, fetch bool) State {
|
||||
func (s *Scanner) refreshOne(ctx context.Context, path string, fetch bool, forgeFor func(string) string) State {
|
||||
rctx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -161,8 +166,19 @@ func (s *Scanner) refreshOne(ctx context.Context, path string, fetch bool) State
|
||||
|
||||
st.Ahead, st.Behind, _ = s.git.AheadBehind(rctx, path)
|
||||
|
||||
if remotes, err := s.git.Remotes(rctx, path); err == nil {
|
||||
st.Remotes = remotes
|
||||
// Remote names, plus the origin URL used to derive which forge this project
|
||||
// belongs to (§ "get it from git").
|
||||
if details, err := s.git.RemoteDetails(rctx, path); err == nil {
|
||||
var originURL string
|
||||
for _, rm := range details {
|
||||
st.Remotes = append(st.Remotes, rm.Name)
|
||||
if rm.Name == "origin" || originURL == "" {
|
||||
originURL = rm.URL
|
||||
}
|
||||
}
|
||||
if forgeFor != nil && originURL != "" {
|
||||
st.Forge = forgeFor(originURL)
|
||||
}
|
||||
}
|
||||
|
||||
return st
|
||||
|
||||
Reference in New Issue
Block a user