Class: Sxn::Core::ProjectManager

Inherits:
Object
  • Object
show all
Defined in:
lib/sxn/core/project_manager.rb

Overview

Manages project registration and configuration

Instance Method Summary collapse

Constructor Details

#initialize(config_manager = nil) ⇒ ProjectManager

Returns a new instance of ProjectManager.



8
9
10
# File 'lib/sxn/core/project_manager.rb', line 8

def initialize(config_manager = nil)
  @config_manager = config_manager || ConfigManager.new
end

Instance Method Details

#add_project(name, path, type: nil, default_branch: nil) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/sxn/core/project_manager.rb', line 12

def add_project(name, path, type: nil, default_branch: nil)
  validate_project_name!(name)
  validate_project_path!(path)

  # Detect project type if not provided
  type ||= Sxn::Rules::ProjectDetector.new(path).detect_project_type.to_s

  # Detect default branch if not provided
  default_branch ||= detect_default_branch(path)

  @config_manager.add_project(name, path, type: type, default_branch: default_branch)

  {
    name: name,
    path: File.expand_path(path),
    type: type,
    default_branch: default_branch
  }
end

#auto_register_projects(detected_projects) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/sxn/core/project_manager.rb', line 147

def auto_register_projects(detected_projects)
  results = []

  detected_projects.each do |project|
    result = add_project(
      project[:name],
      project[:path],
      type: project[:type]
    )
    results << { status: :success, project: result }
  rescue StandardError => e
    results << {
      status: :error,
      project: project,
      error: e.message
    }
  end

  results
end

#detect_project_type(path) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/sxn/core/project_manager.rb', line 96

def detect_project_type(path)
  path = Pathname.new(path)

  # Rails detection
  return "rails" if (path / "Gemfile").exist? &&
                    (path / "config" / "application.rb").exist?

  # Ruby gem detection
  return "ruby" if (path / "Gemfile").exist? || Dir.glob((path / "*.gemspec").to_s).any?

  # Node.js/JavaScript detection
  if (path / "package.json").exist?
    begin
      package_json = JSON.parse((path / "package.json").read)
      return "nextjs" if package_json.dig("dependencies", "next")
      return "react" if package_json.dig("dependencies", "react")
      return "typescript" if (path / "tsconfig.json").exist?

      return "javascript"
    rescue StandardError
      return "javascript"
    end
  end

  "unknown"
end

#detect_projects(base_path = nil) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/sxn/core/project_manager.rb', line 74

def detect_projects(base_path = nil)
  base_path ||= Dir.pwd
  detected = []

  # Scan for common project types
  Dir.glob(File.join(base_path, "*")).each do |path|
    next unless File.directory?(path)
    next if File.basename(path).start_with?(".")

    project_type = detect_project_type(path)
    next if project_type == "unknown"

    detected << {
      name: File.basename(path),
      path: path,
      type: project_type
    }
  end

  detected
end

#get_project(name) ⇒ Object

Raises:



58
59
60
61
62
63
# File 'lib/sxn/core/project_manager.rb', line 58

def get_project(name)
  project = @config_manager.get_project(name)
  raise Sxn::ProjectNotFoundError, "Project '#{name}' not found" unless project

  project
end

#get_project_rules(name) ⇒ Object

Raises:



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/sxn/core/project_manager.rb', line 190

def get_project_rules(name)
  project = get_project(name)
  raise Sxn::ProjectNotFoundError, "Project '#{name}' not found" unless project

  # Get project-specific rules from config
  config = @config_manager.get_config

  # Handle both OpenStruct and Hash for projects config
  projects = config.projects
  project_config = if projects.is_a?(OpenStruct)
                     projects.to_h[name.to_sym] || projects.to_h[name]
                   elsif projects.is_a?(Hash)
                     projects[name]
                   end

  # Extract rules, handling both OpenStruct and Hash
  rules = if project_config.is_a?(OpenStruct)
            project_config.to_h[:rules] || project_config.to_h["rules"] || {}
          elsif project_config.is_a?(Hash)
            project_config["rules"] || project_config[:rules] || {}
          else
            {}
          end

  # Convert OpenStruct rules to hash if needed
  rules = rules.to_h if rules.is_a?(OpenStruct)

  # Add default rules based on project type
  default_rules = get_default_rules_for_type(project[:type])

  merge_rules(default_rules, rules)
end

#list_projectsObject



54
55
56
# File 'lib/sxn/core/project_manager.rb', line 54

def list_projects
  @config_manager.list_projects
end

#project_exists?(name) ⇒ Boolean

Returns:



65
66
67
68
# File 'lib/sxn/core/project_manager.rb', line 65

def project_exists?(name)
  project = @config_manager.get_project(name)
  !project.nil?
end

#remove_project(name) ⇒ Object

Raises:



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sxn/core/project_manager.rb', line 32

def remove_project(name)
  project = @config_manager.get_project(name)
  raise Sxn::ProjectNotFoundError, "Project '#{name}' not found" unless project

  # Check if project is used in any active sessions
  session_manager = SessionManager.new(@config_manager)
  active_sessions = session_manager.list_sessions(status: "active")

  sessions_using_project = active_sessions.select do |session|
    session[:projects].include?(name)
  end

  unless sessions_using_project.empty?
    session_names = sessions_using_project.map { |s| s[:name] }.join(", ")
    raise Sxn::ProjectInUseError,
          "Project '#{name}' is used in active sessions: #{session_names}"
  end

  @config_manager.remove_project(name)
  true
end

#scan_projects(_base_path = nil) ⇒ Object



70
71
72
# File 'lib/sxn/core/project_manager.rb', line 70

def scan_projects(_base_path = nil)
  @config_manager.detect_projects
end

#update_project(name, updates = {}) ⇒ Object

Raises:



123
124
125
126
127
128
129
130
131
132
133
# File 'lib/sxn/core/project_manager.rb', line 123

def update_project(name, updates = {})
  project = get_project(name)
  raise Sxn::ProjectNotFoundError, "Project '#{name}' not found" unless project

  # Validate updates
  raise Sxn::InvalidProjectPathError, "Path is not a directory" if updates[:path] && !File.directory?(updates[:path])

  @config_manager.update_project(name, updates)
  @config_manager.get_project(name) || raise(Sxn::ProjectNotFoundError,
                                             "Project '#{name}' was deleted during update")
end

#validate_project(name) ⇒ Object

Raises:



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/sxn/core/project_manager.rb', line 168

def validate_project(name)
  project = get_project(name)
  raise Sxn::ProjectNotFoundError, "Project '#{name}' not found" unless project

  issues = []

  # Check if path exists
  issues << "Project path does not exist: #{project[:path]}" unless File.directory?(project[:path])

  # Check if it's a git repository
  issues << "Project path is not a git repository" unless git_repository?(project[:path])

  # Check if path is readable
  issues << "Project path is not readable" unless File.readable?(project[:path])

  {
    valid: issues.empty?,
    issues: issues,
    project: project
  }
end

#validate_projectsObject



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/sxn/core/project_manager.rb', line 135

def validate_projects
  projects = list_projects
  results = []

  projects.each do |project|
    result = validate_project(project[:name])
    results << result
  end

  results
end