Class: Pindo::PindoContext

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/pindo/base/pindocontext.rb

Defined Under Namespace

Modules: SelectionKey

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePindoContext

Returns a new instance of PindoContext.



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/pindo/base/pindocontext.rb', line 20

def initialize
  @current_command = nil      # 根命令(第一个设置的命令)
  @current_directory = nil    # 项目路径
  @memory_selections = {}      # 三级结构的内存缓存
  @file_cache_loaded = false   # 标记文件缓存是否已加载
  @cache_enabled = false       # 默认禁用缓存
  @command_group = nil         # 命令组名称
  @verbose = false             # 是否输出详细日志
  @loaded_file_cache = nil     # 已加载但未应用的文件缓存数据
  @cache_confirmed = false     # 标记缓存确认是否已完成
  ensure_cache_dir
end

Instance Attribute Details

#current_commandObject (readonly)

Returns the value of attribute current_command.



18
19
20
# File 'lib/pindo/base/pindocontext.rb', line 18

def current_command
  @current_command
end

#current_directoryObject (readonly)

Returns the value of attribute current_directory.



18
19
20
# File 'lib/pindo/base/pindocontext.rb', line 18

def current_directory
  @current_directory
end

Instance Method Details

#cache_enabled?Boolean

检查缓存是否启用

Returns:

  • (Boolean)


163
164
165
# File 'lib/pindo/base/pindocontext.rb', line 163

def cache_enabled?
  @cache_enabled
end

#clear_current_cacheObject

清除当前上下文的缓存



206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/pindo/base/pindocontext.rb', line 206

def clear_current_cache
  return unless @cache_enabled

  proj_path = get_project_path
  cmd = root_command
  return unless proj_path && cmd

  if @memory_selections[proj_path] && @memory_selections[proj_path][cmd]
    @memory_selections[proj_path].delete(cmd)
    @memory_selections.delete(proj_path) if @memory_selections[proj_path].empty?
    save_file_cache
    puts "[PindoContext] 清除缓存: [#{proj_path}][#{cmd}]" if verbose?
  end
end

#confirm_and_apply_cacheObject

确认并应用缓存(供命令在 run 函数中调用)返回值:true 表示使用缓存,false 表示不使用缓存



463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'lib/pindo/base/pindocontext.rb', line 463

def confirm_and_apply_cache
  return false unless @cache_enabled
  return false unless @loaded_file_cache

  # 如果已经确认过缓存,直接返回(避免嵌套调用时重复询问)
  if @cache_confirmed
    puts "[PindoContext] 缓存已确认,跳过重复询问" if verbose?
    # 检查内存中是否已有缓存数据
    project_path = get_project_path
    root_command = @command_group || @current_command
    return @memory_selections.dig(project_path, root_command) != nil
  end

  project_path = get_project_path
  root_command = @command_group || @current_command

  return false unless project_path && root_command

  # 检查三级结构中是否有当前上下文的缓存
  cached_selections = @loaded_file_cache.dig(project_path, root_command)

  unless cached_selections && !cached_selections.empty?
    puts "[PindoContext] 当前上下文没有缓存数据" if verbose?
    return false
  end

  # 根据命令组显示更友好的描述
  group_desc = case root_command
               when 'ios:autobuild'
                 'iOS 构建'
               when 'and:autobuild', 'android:autobuild'
                 'Android 构建'
               when 'web:autobuild'
                 'Web 构建'
               else
                 @current_command  # 其他命令显示原始命令名
               end

  puts "\n检测到之前的选择 (#{group_desc}):"
  puts "────────────────────────────────────────"

  cached_selections.each do |key, value|
    # 跳过内部字段
    next if key.to_s.start_with?('__')

    case key.to_s
    when 'bundle_id'
      puts "  Bundle ID: #{value}"
    when 'project_name', 'app_key'  # 兼容旧的 app_key
      puts "  项目名称: #{value}"
    when 'tag_decision'
      if value.is_a?(Hash)
        action_desc = value['description'] || value['action']
        puts "  Tag决定: #{action_desc}"
      end
    when 'cert_type'
      puts "  证书类型: #{value}"
    end
  end
  puts "────────────────────────────────────────"

  # 检查是否设置了强制编译环境变量
  force_build = ENV['PINDO_FORCE_BUILD']

  confirm = false
  if force_build && !force_build.empty?
    # 环境变量存在,自动使用缓存
    puts "\n检测到 PINDO_FORCE_BUILD 环境变量,自动使用缓存的选择"
    confirm = true
  else
    # 询问用户是否使用缓存
    require 'highline/import'
    cli = HighLine.new
    confirm = cli.agree("\n是否使用以上缓存的选择? (y/n) ")
  end

  if confirm
    puts "使用缓存的选择\n" unless force_build
    # 恢复当前上下文的文件缓存到内存缓存
    @memory_selections[project_path] ||= {}
    @memory_selections[project_path][root_command] ||= {}
    cached_selections.each do |key, value|
      symbol_key = key.to_sym
      @memory_selections[project_path][root_command][symbol_key] = symbolize_keys(value)
    end
    # 标记缓存已确认,避免嵌套调用时重复询问
    @cache_confirmed = true
    return true
  else
    puts "清除缓存,重新选择\n"
    # 清除当前上下文的文件缓存(三级结构)
    file_path = cache_file_path
    if @loaded_file_cache[project_path] && @loaded_file_cache[project_path][root_command]
      @loaded_file_cache[project_path].delete(root_command)
      # 如果项目路径下没有任何命令缓存了,删除项目路径键
      @loaded_file_cache.delete(project_path) if @loaded_file_cache[project_path].empty?
      # 保存清理后的缓存到文件
      save_file_cache
    end
    # 标记缓存已确认(用户选择不使用)
    @cache_confirmed = true
    return false
  end
end

#debug_infoObject

调试信息



569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# File 'lib/pindo/base/pindocontext.rb', line 569

def debug_info
  puts "\n=== PindoContext Debug Info ==="
  puts "当前命令: #{@current_command || '无'}"
  puts "命令组: #{@command_group || '无'}"
  puts "设置的目录: #{@current_directory || '未设置'}"
  puts "实际项目路径: #{get_project_path}"
  puts "缓存启用: #{@cache_enabled}"

  proj_path = get_project_path
  root_cmd = @command_group || @current_command

  if root_cmd && @memory_selections.dig(proj_path, root_cmd)
    puts "当前缓存的选择:"
    @memory_selections[proj_path][root_cmd].each do |k, v|
      next if k.to_s.start_with?('__')  # 跳过内部字段
      puts "  #{k}: #{v.inspect}"
    end
  end
  puts "================================\n"
end

#enable_cache(enabled = true) ⇒ Object

启用/禁用缓存



154
155
156
157
158
159
160
# File 'lib/pindo/base/pindocontext.rb', line 154

def enable_cache(enabled = true)
  @cache_enabled = enabled
  if enabled && !@file_cache_loaded && @current_command
    load_file_cache
    @file_cache_loaded = true
  end
end

#get_project_pathObject

内部使用的项目路径获取方法优先使用已设置的目录,如果没有则尝试获取Git仓库根目录



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/pindo/base/pindocontext.rb', line 189

def get_project_path
  # 如果已经设置了目录,直接返回
  return @current_directory if @current_directory

  # 尝试获取Git仓库根目录
  begin
    git_root = `git rev-parse --show-toplevel 2>/dev/null`.strip
    return git_root if $?.success? && !git_root.empty?
  rescue
    # git命令失败
  end

  # 最后的后备:使用当前目录
  Dir.pwd
end

#get_selection(key) ⇒ Object

获取用户选择(三级结构访问)



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/pindo/base/pindocontext.rb', line 128

def get_selection(key)
  # 使用统一的项目路径获取方法
  project_path = get_project_path
  root_command = @command_group || @current_command

  return nil unless project_path && root_command

  # 从三级结构中获取值(总是从内存获取,不管是否启用缓存)
  value = @memory_selections.dig(project_path, root_command, key)

  if value
    puts "[PindoContext] 从内存获取: [#{project_path}][#{root_command}][#{key}] = #{value.inspect}" if verbose?
  else
    puts "[PindoContext] 内存中未找到: [#{project_path}][#{root_command}][#{key}]" if verbose?
  end

  value
end

#has_selection?(key) ⇒ Boolean

检查是否有缓存的选择

Returns:

  • (Boolean)


148
149
150
151
# File 'lib/pindo/base/pindocontext.rb', line 148

def has_selection?(key)
  # 总是检查内存中是否有值,不管是否启用缓存
  !get_selection(key).nil?
end

#load_and_apply_jps_config(project_dir = nil) ⇒ Object

加载 JPSBuildConfig.json 并覆盖项目名称(如果存在)应在 confirm_and_apply_cache 之前调用



416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/pindo/base/pindocontext.rb', line 416

def load_and_apply_jps_config(project_dir = nil)
  project_dir ||= get_project_path

  # 判断工程类型并确定配置文件路径
  # Unity 工程:ProjectSettings/JPSBuildConfig.json
  # iOS/Android 工程:JPSBuildConfig.json
  config_file = nil
  project_type = nil

  # 检查是否是 Unity 工程(存在 ProjectSettings 目录)
  if File.directory?(File.join(project_dir, 'ProjectSettings'))
    config_file = File.join(project_dir, 'ProjectSettings', 'JPSBuildConfig.json')
    project_type = 'Unity'
  else
    # iOS/Android 工程,直接在根目录
    config_file = File.join(project_dir, 'JPSBuildConfig.json')
    project_type = 'iOS/Android'
  end

  unless File.exist?(config_file)
    puts "[PindoContext] JPSBuildConfig.json 不存在 (#{project_type} 工程),跳过" if verbose?
    return nil
  end

  begin
    config_content = File.read(config_file)
    jps_config = JSON.parse(config_content)

    if jps_config['project_name'] && !jps_config['project_name'].empty?
      project_name = jps_config['project_name']
      puts "[PindoContext] 从 JPSBuildConfig.json 加载项目名称 (#{project_type}): #{project_name}" if verbose?

      # 设置到当前上下文的缓存中
      set_selection(SelectionKey::PROJECT_NAME, project_name)

      puts "使用 JPS 配置的项目名称: #{project_name}"
      return project_name
    end
  rescue => e
    puts "[PindoContext] 加载 JPSBuildConfig.json 失败: #{e.message}" if verbose?
  end

  nil
end

#nested_call?Boolean

是否是嵌套调用

Returns:

  • (Boolean)


168
169
170
# File 'lib/pindo/base/pindocontext.rb', line 168

def nested_call?
  !@current_command.nil?
end

#project_pathObject

获取当前项目路径(统一的获取方法,确保一致性)



183
184
185
# File 'lib/pindo/base/pindocontext.rb', line 183

def project_path
  get_project_path
end

#reset_contextObject

重置上下文(顶层命令结束时调用)



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/pindo/base/pindocontext.rb', line 83

def reset_context
  # 仅在启用缓存时保存文件缓存
  save_file_cache if @cache_enabled && @current_command

  @current_command = nil
  @current_directory = nil
  @file_cache_loaded = false
  @cache_enabled = false
  @command_group = nil
  @cache_confirmed = false
  # 暂不清理内存缓存,让同一进程内可以复用
end

#root_commandObject

获取当前根命令



178
179
180
# File 'lib/pindo/base/pindocontext.rb', line 178

def root_command
  @command_group || @current_command
end

#set_context(command, directory = nil, options = {}) ⇒ Object

设置当前上下文



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/pindo/base/pindocontext.rb', line 39

def set_context(command, directory = nil, options = {})
  # 如果已有命令在执行,这是嵌套调用,不需要做任何事
  unless @current_command.nil?
    puts "[PindoContext] 嵌套调用 #{command},使用已有上下文 (根命令: #{@current_command},命令组: #{@command_group})" if verbose?
    return
  end

  # 第一次设置,是顶层命令
  @current_command = command  # 保持原始命令名称
  @current_directory = directory || Dir.pwd  # 固定项目目录

  # 从选项中获取缓存配置
  @cache_enabled = options[:cache_enabled] || false
  @verbose = options[:verbose] || false
  # 使用 get_command_group 来确定缓存组
  @command_group = get_command_group(command)

  if verbose?
    puts "\n[PindoContext] ========== 设置顶层命令上下文 =========="
    puts "[PindoContext] 命令: #{command}"
    puts "[PindoContext] 目录: #{@current_directory}"
    puts "[PindoContext] 缓存启用: #{@cache_enabled}"
    puts "[PindoContext] 命令组: #{@command_group}"
    puts "[PindoContext] options: #{options.inspect}"
  end

  # 仅在启用缓存时加载文件缓存(不确认,只加载到临时变量)
  if @cache_enabled && !@file_cache_loaded
    puts "[PindoContext] 准备加载文件缓存..." if verbose?
    load_file_cache
    @file_cache_loaded = true
  else
    if verbose?
      if !@cache_enabled
        puts "[PindoContext] 缓存未启用,跳过文件缓存加载"
      elsif @file_cache_loaded
        puts "[PindoContext] 文件缓存已加载,跳过重复加载"
      end
    end
  end
  puts "[PindoContext] ======================================\n" if verbose?
end

#set_selection(key, value) ⇒ Object

设置用户选择(三级结构:项目路径 -> 根命令 -> 键值)



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
122
123
124
125
# File 'lib/pindo/base/pindocontext.rb', line 97

def set_selection(key, value)
  # 构建三级键 - 使用统一的项目路径获取方法
  project_path = get_project_path
  root_command = @command_group || @current_command

  # 如果没有有效的项目路径或命令,不保存
  unless project_path && root_command
    puts "[PindoContext] 警告: 无法保存选择,缺少项目路径或命令上下文"
    return
  end

  # 初始化三级结构(总是保存到内存,不管是否启用缓存)
  @memory_selections[project_path] ||= {}
  @memory_selections[project_path][root_command] ||= {}
  @memory_selections[project_path][root_command][key] = value

  # 更新该命令组的最后修改时间
  @memory_selections[project_path][root_command]['__last_modified__'] = Time.now.to_i

  puts "[PindoContext] 保存到内存: [#{project_path}][#{root_command}][#{key}] = #{value.inspect}" if verbose?

  # 仅在启用缓存时保存到文件
  if @cache_enabled
    save_file_cache
    puts "[PindoContext] 已保存到文件缓存" if verbose?
  else
    puts "[PindoContext] 缓存未启用,仅保存到内存" if verbose?
  end
end

#top_level_command?Boolean

是否是顶层命令(非嵌套)

Returns:

  • (Boolean)


173
174
175
# File 'lib/pindo/base/pindocontext.rb', line 173

def top_level_command?
  @current_command.nil?
end

#verbose?Boolean

判断是否处于 verbose 模式

Returns:

  • (Boolean)


34
35
36
# File 'lib/pindo/base/pindocontext.rb', line 34

def verbose?
  @verbose || ENV['PINDO_VERBOSE'] == '1' || ENV['PINDO_DEBUG'] == '1'
end