Files
gh-commit/cmd/migrate.go
T
t 9647c854c9 feat: rewrite gh-commit in Go with local Ollama backend
Replaces the Python/Crush/DuckDB implementation with a CGO-free Go binary:
cobra CLI, modernc.org/sqlite storage, huh/lipgloss UI, and speed-tuned raw
/api/generate calls (think:false, keep_alive, capped num_predict) against a
local qwen3.5:2b. Ships as a gh extension via cli/gh-extension-precompile.
2026-08-15 19:10:16 -04:00

94 lines
2.6 KiB
Go

package cmd
import (
"encoding/json"
"os"
"path/filepath"
"time"
"github.com/BurntSushi/toml"
"github.com/prdlk/gh-commit/internal/ai"
"github.com/prdlk/gh-commit/internal/gitx"
"github.com/prdlk/gh-commit/internal/ui"
)
// archiveMigrated renames a consumed legacy file to *.migrated.<timestamp>.
func archiveMigrated(path string) (string, error) {
backup := path + ".migrated." + time.Now().Format("20060102_150405")
return backup, os.Rename(path, backup)
}
// migrateTOML imports scopes from .github/Repo.toml.
func migrateTOML(repoPath, repoName, tomlPath string) bool {
ui.Warnf("↻ Migrating .github/Repo.toml → database")
var doc struct {
Scopes map[string]any `toml:"scopes"`
}
if _, err := toml.DecodeFile(tomlPath, &doc); err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
scopes := ai.CoerceScopeMap(doc.Scopes)
if scopes == nil {
scopes = map[string][]string{}
}
if err := store.SaveScopes(repoPath, repoName, scopes, gitx.GitignoreHash(repoPath)); err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
backup, err := archiveMigrated(tomlPath)
if err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
ui.Dimf(" Archived: %s", backup)
return true
}
// migrateJSON imports scopes from .github/scopes.json
// (an array of {"scope": ..., "path": ...} entries).
func migrateJSON(repoPath, repoName, jsonPath string) bool {
ui.Warnf("↻ Migrating .github/scopes.json → database")
data, err := os.ReadFile(jsonPath)
if err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
var entries []struct {
Scope string `json:"scope"`
Path string `json:"path"`
}
if err := json.Unmarshal(data, &entries); err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
scopes := map[string][]string{}
for _, e := range entries {
scopes[e.Scope] = append(scopes[e.Scope], e.Path)
}
if err := store.SaveScopes(repoPath, repoName, scopes, gitx.GitignoreHash(repoPath)); err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
backup, err := archiveMigrated(jsonPath)
if err != nil {
ui.Errorf("Migration failed: %s", err)
return false
}
ui.Dimf(" Archived: %s", backup)
return true
}
// migrateLegacy imports whichever legacy config file exists, if any.
func migrateLegacy(repoPath string) bool {
repoName := filepath.Base(repoPath)
if tomlPath := filepath.Join(repoPath, ".github", "Repo.toml"); fileExists(tomlPath) {
return migrateTOML(repoPath, repoName, tomlPath)
}
if jsonPath := filepath.Join(repoPath, ".github", "scopes.json"); fileExists(jsonPath) {
return migrateJSON(repoPath, repoName, jsonPath)
}
return false
}