Skip to content

Commit 4808b52

Browse files
committed
Initial implementation of UE Git Manager
0 parents  commit 4808b52

File tree

14 files changed

+3233
-0
lines changed

14 files changed

+3233
-0
lines changed

.gitignore

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Code coverage profiles and other test artifacts
15+
*.out
16+
coverage.*
17+
*.coverprofile
18+
profile.cov
19+
20+
# Dependency directories (remove the comment below to include it)
21+
# vendor/
22+
23+
# Go workspace file
24+
go.work
25+
go.work.sum
26+
27+
# env file
28+
.env
29+
30+
# Editor/IDE
31+
# .idea/
32+
# .vscode/
33+
34+
digest.txt

build.bat

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@echo off
2+
echo Building UE Git Manager...
3+
4+
REM Check if Go is installed
5+
go version >nul 2>&1
6+
if %ERRORLEVEL% NEQ 0 (
7+
echo.
8+
echo Go is not installed or not in PATH!
9+
echo.
10+
echo Please install Go 1.21 or later from: https://golang.org/dl/
11+
echo After installation, restart Command Prompt and try again.
12+
echo.
13+
pause
14+
exit /b 1
15+
)
16+
17+
REM Check Go version
18+
for /f "tokens=3" %%i in ('go version') do set GO_VERSION=%%i
19+
echo Go version: %GO_VERSION%
20+
21+
REM Create logs directory if it doesn't exist
22+
if not exist "logs" mkdir logs
23+
24+
REM Initialize Go module if needed
25+
if not exist "go.mod" (
26+
echo Initializing Go module...
27+
go mod init ue-git-manager
28+
)
29+
30+
REM Download dependencies
31+
echo Downloading dependencies...
32+
go mod tidy
33+
34+
REM Create dist directory if it doesn't exist
35+
if not exist "dist" mkdir dist
36+
37+
REM Check if UE-Git-Manager.exe is running and stop it
38+
tasklist /FI "IMAGENAME eq UE-Git-Manager.exe" 2>NUL | find /I /N "UE-Git-Manager.exe">NUL
39+
if "%ERRORLEVEL%"=="0" (
40+
echo ⚠️ UE-Git-Manager.exe is currently running. Stopping it...
41+
taskkill /F /IM UE-Git-Manager.exe >NUL 2>&1
42+
timeout /t 2 >NUL
43+
)
44+
45+
REM Remove any existing executable and temporary files
46+
if exist "dist\UE-Git-Manager.exe" del /F /Q "dist\UE-Git-Manager.exe" >NUL 2>&1
47+
if exist "dist\UE-Git-Manager.exe~" del /F /Q "dist\UE-Git-Manager.exe~" >NUL 2>&1
48+
49+
REM Build the executable
50+
echo Building executable...
51+
go build -o dist/UE-Git-Manager.exe .
52+
53+
if %ERRORLEVEL% EQU 0 (
54+
echo.
55+
echo Build successful!
56+
echo UE-Git-Manager.exe created in dist/ folder.
57+
echo.
58+
echo You can now run the application by double-clicking dist/UE-Git-Manager.exe
59+
60+
REM Clean up any temporary files that might have been created
61+
if exist "dist\UE-Git-Manager.exe~" (
62+
echo Cleaning up temporary files...
63+
del /F /Q "dist\UE-Git-Manager.exe~" >NUL 2>&1
64+
)
65+
) else (
66+
echo.
67+
echo Build failed!
68+
echo Please check the error messages above.
69+
echo.
70+
echo Common issues:
71+
echo - Go not installed or not in PATH
72+
echo - Git not installed [required for dependencies]
73+
echo - Network connectivity issues
74+
echo - UE-Git-Manager.exe is locked by another process
75+
pause
76+
)

config.example.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"version": 1,
3+
"base_dir": ".",
4+
"origin_dir": "repo-origin",
5+
"worktrees_dir": "worktrees",
6+
"default_remote_branch": "dev",
7+
"engines": [
8+
{
9+
"engine_path": "C:\\Program Files\\Epic Games\\UE_5.4",
10+
"engine_version": "5.4",
11+
"worktree_subdir": "UE_5.4",
12+
"branch": "engine-5.4",
13+
"plugin_link_path": "C:\\Program Files\\Epic Games\\UE_5.4\\Engine\\Plugins\\UEGitPlugin_PB",
14+
"stock_plugin_disabled_by_tool": false
15+
},
16+
{
17+
"engine_path": "C:\\Program Files\\Epic Games\\UE_5.5",
18+
"engine_version": "5.5",
19+
"worktree_subdir": "UE_5.5",
20+
"branch": "engine-5.5",
21+
"plugin_link_path": "C:\\Program Files\\Epic Games\\UE_5.5\\Engine\\Plugins\\UEGitPlugin_PB",
22+
"stock_plugin_disabled_by_tool": true
23+
}
24+
],
25+
"custom_engine_roots": [
26+
"D:\\Engines",
27+
"E:\\Unreal\\UE"
28+
],
29+
"last_run_utc": "2025-01-27T12:34:56Z"
30+
}
31+

create_digest.bat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@echo off
2+
call conda activate crowdtracker
3+
gitingest . -o digest.txt -e dist/

go.mod

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module ue-git-manager
2+
3+
go 1.21
4+
5+
require (
6+
github.com/fatih/color v1.16.0
7+
github.com/manifoldco/promptui v0.9.0
8+
)
9+
10+
require (
11+
github.com/chzyer/readline v1.5.1 // indirect
12+
github.com/mattn/go-colorable v0.1.13 // indirect
13+
github.com/mattn/go-isatty v0.0.20 // indirect
14+
golang.org/x/sys v0.14.0 // indirect
15+
)

go.sum

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
2+
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
3+
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
4+
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
5+
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
6+
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
7+
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
8+
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
9+
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
10+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
11+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
12+
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
13+
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
14+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
15+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
16+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
17+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
18+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
19+
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
20+
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
21+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
22+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
23+
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
24+
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

internal/config/config.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package config
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
"os/user"
7+
"path/filepath"
8+
"time"
9+
)
10+
11+
// Config represents the application configuration
12+
type Config struct {
13+
Version int `json:"version"`
14+
BaseDir string `json:"base_dir"`
15+
OriginDir string `json:"origin_dir"`
16+
WorktreesDir string `json:"worktrees_dir"`
17+
DefaultRemoteBranch string `json:"default_remote_branch"`
18+
Engines []Engine `json:"engines"`
19+
CustomEngineRoots []string `json:"custom_engine_roots"`
20+
LastRunUTC string `json:"last_run_utc"`
21+
}
22+
23+
// Engine represents a managed Unreal Engine installation
24+
type Engine struct {
25+
EnginePath string `json:"engine_path"`
26+
EngineVersion string `json:"engine_version"`
27+
WorktreeSubdir string `json:"worktree_subdir"`
28+
Branch string `json:"branch"`
29+
PluginLinkPath string `json:"plugin_link_path"`
30+
StockPluginDisabledByTool bool `json:"stock_plugin_disabled_by_tool"`
31+
}
32+
33+
// Manager handles configuration operations
34+
type Manager struct {
35+
exeDir string
36+
baseDir string
37+
configPath string
38+
}
39+
40+
// New creates a new configuration manager
41+
func New(exeDir string) *Manager {
42+
baseDir := getUserConfigDir()
43+
return &Manager{
44+
exeDir: exeDir,
45+
baseDir: baseDir,
46+
configPath: filepath.Join(baseDir, "config.json"),
47+
}
48+
}
49+
50+
// getUserConfigDir returns the user's config directory for the application
51+
func getUserConfigDir() string {
52+
// Get the current user
53+
usr, err := user.Current()
54+
if err != nil {
55+
// Fallback to executable directory if we can't get user info
56+
exePath, _ := os.Executable()
57+
return filepath.Dir(exePath)
58+
}
59+
60+
// Use the user's config directory
61+
// On Windows: %APPDATA%\Pi\unreal_source_control
62+
// On Linux/macOS: ~/.config/Pi/unreal_source_control
63+
configDir := filepath.Join(usr.HomeDir, "AppData", "Roaming", "Pi", "unreal_source_control")
64+
65+
// Create the directory if it doesn't exist
66+
os.MkdirAll(configDir, 0755)
67+
68+
return configDir
69+
}
70+
71+
// GetExeDir returns the executable directory
72+
func (m *Manager) GetExeDir() string {
73+
return m.exeDir
74+
}
75+
76+
// GetBaseDir returns the base directory for the application data
77+
func (m *Manager) GetBaseDir() string {
78+
return m.baseDir
79+
}
80+
81+
// Exists checks if the configuration file exists
82+
func (m *Manager) Exists() bool {
83+
_, err := os.Stat(m.configPath)
84+
return !os.IsNotExist(err)
85+
}
86+
87+
// Load loads the configuration from file
88+
func (m *Manager) Load() (*Config, error) {
89+
data, err := os.ReadFile(m.configPath)
90+
if err != nil {
91+
return nil, err
92+
}
93+
94+
var config Config
95+
if err := json.Unmarshal(data, &config); err != nil {
96+
return nil, err
97+
}
98+
99+
// Resolve relative paths
100+
config.BaseDir = m.resolvePath(config.BaseDir)
101+
config.OriginDir = m.resolvePath(config.OriginDir)
102+
config.WorktreesDir = m.resolvePath(config.WorktreesDir)
103+
104+
return &config, nil
105+
}
106+
107+
// Save saves the configuration to file
108+
func (m *Manager) Save(config *Config) error {
109+
// Make a copy to avoid modifying the original
110+
saveConfig := *config
111+
112+
// Convert absolute paths to relative where possible
113+
saveConfig.BaseDir = m.makeRelative(saveConfig.BaseDir)
114+
saveConfig.OriginDir = m.makeRelative(saveConfig.OriginDir)
115+
saveConfig.WorktreesDir = m.makeRelative(saveConfig.WorktreesDir)
116+
117+
// Update last run time
118+
saveConfig.LastRunUTC = time.Now().UTC().Format(time.RFC3339)
119+
120+
data, err := json.MarshalIndent(saveConfig, "", " ")
121+
if err != nil {
122+
return err
123+
}
124+
125+
return os.WriteFile(m.configPath, data, 0644)
126+
}
127+
128+
// CreateDefault creates a default configuration
129+
func (m *Manager) CreateDefault() *Config {
130+
return &Config{
131+
Version: 1,
132+
BaseDir: m.baseDir,
133+
OriginDir: "repo-origin",
134+
WorktreesDir: "worktrees",
135+
DefaultRemoteBranch: "dev",
136+
Engines: []Engine{},
137+
CustomEngineRoots: []string{},
138+
LastRunUTC: time.Now().UTC().Format(time.RFC3339),
139+
}
140+
}
141+
142+
// AddEngine adds an engine to the configuration
143+
func (m *Manager) AddEngine(config *Config, eng Engine) {
144+
config.Engines = append(config.Engines, eng)
145+
}
146+
147+
// RemoveEngine removes an engine from the configuration
148+
func (m *Manager) RemoveEngine(config *Config, enginePath string) {
149+
for i, eng := range config.Engines {
150+
if eng.EnginePath == enginePath {
151+
config.Engines = append(config.Engines[:i], config.Engines[i+1:]...)
152+
break
153+
}
154+
}
155+
}
156+
157+
// GetEngineByPath gets an engine by its path
158+
func (m *Manager) GetEngineByPath(config *Config, enginePath string) *Engine {
159+
for i, eng := range config.Engines {
160+
if eng.EnginePath == enginePath {
161+
return &config.Engines[i]
162+
}
163+
}
164+
return nil
165+
}
166+
167+
// resolvePath resolves a path relative to the base directory
168+
func (m *Manager) resolvePath(path string) string {
169+
if filepath.IsAbs(path) {
170+
return path
171+
}
172+
return filepath.Join(m.baseDir, path)
173+
}
174+
175+
// makeRelative makes a path relative to the base directory if possible
176+
func (m *Manager) makeRelative(path string) string {
177+
rel, err := filepath.Rel(m.baseDir, path)
178+
if err != nil || len(rel) >= len(path) {
179+
return path // Return original if can't make relative or if relative is longer
180+
}
181+
return rel
182+
}

0 commit comments

Comments
 (0)