Class: Pindo::BuildHelper

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/pindo/module/build/build_helper.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.share_instanceObject



12
13
14
# File 'lib/pindo/module/build/build_helper.rb', line 12

def share_instance
  instance
end

Instance Method Details

#android_project?(project_path) ⇒ Boolean

Returns:

  • (Boolean)


70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/pindo/module/build/build_helper.rb', line 70

def android_project?(project_path)
  # 检查Android工程的关键文件和目录
  gradle_file = File.exist?(File.join(project_path, "build.gradle")) || File.exist?(File.join(project_path, "build.gradle.kts"))
  settings_gradle = File.exist?(File.join(project_path, "settings.gradle")) || File.exist?(File.join(project_path, "settings.gradle.kts"))

  # 尝试获取主模块
  main_module = nil
  begin
    main_module = get_main_module(project_path)
  rescue => e
    puts "获取主模块失败: #{e.message}" if ENV['DEBUG']
  end

  # Android Studio项目结构
  if gradle_file && settings_gradle && main_module
    app_gradle = File.exist?(File.join(main_module, "build.gradle")) || File.exist?(File.join(main_module, "build.gradle.kts"))
    app_manifest = File.exist?(File.join(main_module, "src", "main", "AndroidManifest.xml"))
    return true if app_gradle && app_manifest
  end

  # 如果无法通过标准方式检测,尝试更宽松的检测
  # 检查是否有任何包含 build.gradle 的子目录
  if gradle_file && settings_gradle
    Dir.entries(project_path).each do |entry|
      next if entry.start_with?('.')
      entry_path = File.join(project_path, entry)
      if File.directory?(entry_path)
        app_gradle = File.exist?(File.join(entry_path, "build.gradle")) || File.exist?(File.join(entry_path, "build.gradle.kts"))
        app_manifest = File.exist?(File.join(entry_path, "src", "main", "AndroidManifest.xml"))
        if app_gradle && app_manifest
          return true
        end
      end
    end
  end

  false
end

#determine_jps_config_path(working_dir) ⇒ String

定位 JPSBuildConfig.json 文件路径先找 git root,再判断是否 Unity 项目来确定配置文件位置

Parameters:

  • working_dir (String)

    当前工作目录

Returns:

  • (String)

    配置文件绝对路径



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/pindo/module/build/build_helper.rb', line 198

def determine_jps_config_path(working_dir)
  # 获取 Git 仓库根目录
  repo_root_dir = nil
  if Pindo::GitHandler.is_git_directory?(local_repo_dir: working_dir)
    repo_root_dir = Pindo::GitHandler.git_root_directory(local_repo_dir: working_dir)
  end
  repo_root_dir ||= working_dir

  # 判断工程类型
  if File.directory?(File.join(repo_root_dir, 'ProjectSettings'))
    File.join(repo_root_dir, 'ProjectSettings', 'JPSBuildConfig.json')
  else
    File.join(repo_root_dir, 'JPSBuildConfig.json')
  end
end

#get_project_name(project_path) ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/pindo/module/build/build_helper.rb', line 132

def get_project_name(project_path)
  case project_type(project_path)
  when :unity
    File.basename(project_path)
  when :ios
    xcodeproj = Dir.glob(File.join(project_path, "*.xcodeproj")).first
    File.basename(xcodeproj, ".xcodeproj") if xcodeproj
  when :android
    settings_gradle = File.join(project_path, "settings.gradle")
    settings_gradle_kts = File.join(project_path, "settings.gradle.kts")
    
    # 优先使用 settings.gradle.kts,如果不存在则使用 settings.gradle
    if File.exist?(settings_gradle_kts)
      settings_gradle = settings_gradle_kts
    end
    
    if File.exist?(settings_gradle)
      content = File.read(settings_gradle)
      if content =~ /rootProject\.name\s*=\s*['"](.+)['"]/
        $1
      else
        File.basename(project_path)
      end
    else
      File.basename(project_path)
    end
  else
    File.basename(project_path)
  end
end

#get_project_version(project_path) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/pindo/module/build/build_helper.rb', line 163

def get_project_version(project_path)
  case project_type(project_path)
  when :unity
    version_file = File.join(project_path, "ProjectSettings", "ProjectVersion.txt")
    if File.exist?(version_file)
      content = File.read(version_file)
      if content =~ /m_EditorVersion: (.*)/
        $1.strip
      end
    end
  when :ios
    # 从Info.plist获取版本号
    nil
  when :android
    # 从build.gradle获取版本号
    nil
  end
end

#ios_project?(project_path) ⇒ Boolean

Returns:

  • (Boolean)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/pindo/module/build/build_helper.rb', line 30

def ios_project?(project_path)
  # 检查iOS工程的关键文件
  xcodeproj_files = Dir.glob(File.join(project_path, "*.xcodeproj"))
  workspace_files = Dir.glob(File.join(project_path, "*.xcworkspace"))

  # 至少要有.xcodeproj文件或.xcworkspace文件
  return false if xcodeproj_files.empty? && workspace_files.empty?

  if !xcodeproj_files.empty?
    # 检查.xcodeproj内部结构
    project_file = File.join(xcodeproj_files.first, "project.pbxproj")
    return true if File.exist?(project_file)
  end

  if !workspace_files.empty?
    # 检查.xcworkspace内部结构
    contents_file = File.join(workspace_files.first, "contents.xcworkspacedata")
    return true if File.exist?(contents_file)
  end

  false
end

#load_jps_build_config(project_dir, conf = nil, **_) ⇒ Hash?

加载 JPSBuildConfig.json,设置全局状态,缺失字段时通过 JPS 请求补全

Parameters:

  • project_dir (String)

    项目目录

  • conf (String, nil) (defaults to: nil)

    显式指定的配置文件路径(优先于自动查找)

Returns:

  • (Hash, nil)

    JPSBuildConfig 的完整配置,文件不存在时返回 nil



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/pindo/module/build/build_helper.rb', line 218

def load_jps_build_config(project_dir, conf = nil, **_)
  config_file = if conf && !conf.to_s.empty?
    File.expand_path(conf)
  else
    determine_jps_config_path(project_dir)
  end

  return nil unless File.exist?(config_file)

  begin
    config = JSON.parse(File.read(config_file))
    state = Pindo::Options::GlobalOptionsState.instance

    project_name = config['project_name']
    if project_name && !project_name.empty?
      state[:proj] = project_name
      puts "使用 JPS 配置的项目名称: #{project_name}"
    end

    # 检查 wildcard_bundle_group_name 和 bundle_group_name(兼容旧字段名)
    wildcard_bundle_group_name = config['wildcard_bundle_group_name'] || config['wildcardBundleId']
    bundle_group_name = config['bundle_group_name'] || config['bundleId']
    project_id = config['project_id']

    # 如果缺失,通过 JPS 请求获取并回写
    if (wildcard_bundle_group_name.nil? || wildcard_bundle_group_name.empty?) ||
       (bundle_group_name.nil? || bundle_group_name.empty?)

      if ENV['PINDO_DEBUG']
        puts "[PINDO_DEBUG] load_jps_build_config: wildcard_bundle_group_name 或 bundle_group_name 缺失,尝试自动修复"
        puts "[PINDO_DEBUG]   wildcard_bundle_group_name=#{wildcard_bundle_group_name.inspect}"
        puts "[PINDO_DEBUG]   bundle_group_name=#{bundle_group_name.inspect}"
        puts "[PINDO_DEBUG]   project_id=#{project_id.inspect}, project_name=#{project_name.inspect}"
      end

      app_info_data = nil

      # 确保 PgyerHelper 已登录,否则 API 调用会因 @pgyer_client 为 nil 而失败
      pgyer_helper = PgyerHelper.share_instace
      pgyer_helper.

      if project_id && !project_id.to_s.empty?
        puts "[PINDO_DEBUG] 使用 project_id=#{project_id} 调用 get_project_detail" if ENV['PINDO_DEBUG']
        app_info_data = pgyer_helper.get_project_detail(project_id: project_id)
        puts "[PINDO_DEBUG] get_project_detail 返回: #{app_info_data.nil? ? 'nil' : '有数据'}" if ENV['PINDO_DEBUG']
      elsif project_name && !project_name.empty?
        puts "[PINDO_DEBUG] 使用 project_name=#{project_name} 调用 find_app_info_with_obj_list" if ENV['PINDO_DEBUG']
        app_info_data = pgyer_helper.find_app_info_with_obj_list(proj_name: project_name)
        puts "[PINDO_DEBUG] find_app_info_with_obj_list 返回: #{app_info_data.nil? ? 'nil' : '有数据'}" if ENV['PINDO_DEBUG']
      else
        puts "[PINDO_DEBUG] project_id 和 project_name 均为空,跳过自动修复" if ENV['PINDO_DEBUG']
      end

      if app_info_data
        if ENV['PINDO_DEBUG']
          puts "[PINDO_DEBUG] app_info_data 字段列表: #{app_info_data.keys.join(', ')}"
          puts "[PINDO_DEBUG]   wildcardBundleId=#{app_info_data['wildcardBundleId'].inspect}"
          puts "[PINDO_DEBUG]   bundleId=#{app_info_data['bundleId'].inspect}"
        end

        wildcard_bundle_group_name = app_info_data["wildcardBundleId"]
        bundle_group_name = app_info_data["bundleId"]

        if wildcard_bundle_group_name && !wildcard_bundle_group_name.empty?
          config['wildcard_bundle_group_name'] = wildcard_bundle_group_name
          config['bundle_group_name'] = bundle_group_name
          File.write(config_file, JSON.pretty_generate(config) + "\n")
          puts "已从 JPS 获取并保存: wildcard_bundle_group_name=#{wildcard_bundle_group_name}, bundle_group_name=#{bundle_group_name}"
        else
          puts "[PINDO_DEBUG] wildcardBundleId 为空,无法自动修复" if ENV['PINDO_DEBUG']
        end
      else
        puts "[PINDO_DEBUG] 未获取到 app_info_data,自动修复失败" if ENV['PINDO_DEBUG']
      end
    else
      puts "[PINDO_DEBUG] load_jps_build_config: wildcard_bundle_group_name 和 bundle_group_name 已存在,跳过自动修复" if ENV['PINDO_DEBUG']
    end

    # 设置全局状态
    if wildcard_bundle_group_name && !wildcard_bundle_group_name.empty?
      state[:wildcard_bundle_group_name] = wildcard_bundle_group_name
      state[:bundle_group_name] = bundle_group_name

      # 通过映射表将 wildcard_bundle_group_name 解析为真实 bundle_id
      require 'pindo/options/helpers/bundleid_selector'
      resolved_bundle_id = Pindo::Options::BundleIdSelector.resolve_bundleid_by_group_name(wildcard_bundle_group_name)
      if resolved_bundle_id && !resolved_bundle_id.empty?
        state[:bundleid] = resolved_bundle_id       # iOS 使用
        state[:bundle_name] = resolved_bundle_id    # Android 使用
        puts "使用 JPSBuildConfig 的 Bundle Name: #{resolved_bundle_id} (#{wildcard_bundle_group_name})"
      end
    end

    return config
  rescue => e
    puts "加载 JPSBuildConfig.json 失败: #{e.message}" if ENV['PINDO_VERBOSE'] == '1'
    if ENV['PINDO_DEBUG']
      puts "[PINDO_DEBUG] load_jps_build_config 异常: #{e.class}: #{e.message}"
      puts "[PINDO_DEBUG]   #{e.backtrace&.first(5)&.join("\n[PINDO_DEBUG]   ")}"
    end
  end
  nil
end

#macos_project?(project_path) ⇒ Boolean

Returns:

  • (Boolean)


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/pindo/module/build/build_helper.rb', line 53

def macos_project?(project_path)
  # 检查是否为 macOS 工程
  begin
    project_fullname = Dir.glob(File.join(project_path, "*.xcodeproj")).max_by {|f| File.mtime(f)}
    if !project_fullname.nil?
      project_obj = Xcodeproj::Project.open(project_fullname)
      project_build_platform = project_obj.root_object.build_configuration_list.get_setting("SDKROOT")["Release"]
      if !project_build_platform.nil? && project_build_platform.eql?("macosx")
        return true
      end
    end
  rescue => e
    puts "判断 macOS 工程失败: #{e.message}" if ENV['DEBUG']
  end
  false
end

#project_type(project_path) ⇒ Object

Raises:

  • (ArgumentError)


109
110
111
112
113
114
115
116
117
# File 'lib/pindo/module/build/build_helper.rb', line 109

def project_type(project_path)
  raise ArgumentError, "项目路径不能为空" if project_path.nil? || project_path.empty?
  raise ArgumentError, "项目路径不存在: #{project_path}" unless File.directory?(project_path)

  return :unity if unity_project?(project_path)
  return :ios if ios_project?(project_path)
  return :android if android_project?(project_path)
  :unknown
end

#project_type_name(project_path) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/pindo/module/build/build_helper.rb', line 119

def project_type_name(project_path)
  case project_type(project_path)
  when :unity
    "Unity"
  when :ios
    "iOS"
  when :android
    "Android"
  else
    "Unknown"
  end
end

#pull_appconfig_with_reponame(repo_name:, target_dir: nil) ⇒ Boolean

拉取应用配置仓库(委托给 BuildInfoManager)

Parameters:

  • repo_name (String)

    仓库名称(Bundle ID)

  • target_dir (String, nil) (defaults to: nil)

    目标目录(默认:当前目录)

Returns:

  • (Boolean)

    是否成功拉取配置



186
187
188
189
190
191
192
# File 'lib/pindo/module/build/build_helper.rb', line 186

def pull_appconfig_with_reponame(repo_name:, target_dir: nil)
  require_relative '../../config/build_info_manager'
  Pindo::BuildInfoManager.share_instance.pull_appconfig_with_reponame(
    repo_name: repo_name,
    target_dir: target_dir
  )
end

#unity_project?(project_path) ⇒ Boolean

Returns:

  • (Boolean)


17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/pindo/module/build/build_helper.rb', line 17

def unity_project?(project_path)
  # 检查Unity工程的关键文件和目录
  project_settings_path = File.join(project_path, "ProjectSettings")
  assets_path = File.join(project_path, "Assets")
  packages_path = File.join(project_path, "Packages")

  # Unity工程必须包含这些目录和文件
  File.directory?(project_settings_path) &&
  File.directory?(assets_path) &&
  File.directory?(packages_path) &&
  File.exist?(File.join(project_settings_path, "ProjectSettings.asset"))
end