psdk-cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'digest'
5
+ require_relative '../900 Yuki__VD'
6
+
7
+ module PluginManager
8
+ # Class responsible of building plugins
9
+ class Builder # rubocop:disable Metrics/ClassLength
10
+ PLUGIN_FILE_EXT = 'psdkplug'
11
+ SCRIPTS_FOLDER = 'scripts'
12
+
13
+ # Create a new plugin builder
14
+ # @param plugin_name [String] name of the plugin directory
15
+ # @param in_project [Boolean] whether we are building inside a PSDK project
16
+ # @param out_dir [String] directory to output the compiled plugin
17
+ def initialize(plugin_name, in_project: true, out_dir: '.')
18
+ @name = plugin_name
19
+ @in_project = in_project
20
+ @out_dir = out_dir
21
+ end
22
+
23
+ # Start the building process
24
+ def build # rubocop:disable Metrics/MethodLength
25
+ puts "--- Starting build for plugin '#{@name}' ---"
26
+ if @in_project
27
+ puts '[INFO] Operating inside a PSDK project.'
28
+ else
29
+ puts '[INFO] Operating in standalone mode (outside a PSDK project).'
30
+ end
31
+
32
+ project_root = @in_project.is_a?(String) ? @in_project : Dir.pwd
33
+ out_dir = File.absolute_path(@out_dir)
34
+
35
+ Dir.chdir(project_root) do
36
+ @config = load_plugin_configuration
37
+ plugin_filename = File.join(out_dir, "#{@config.name}.#{PLUGIN_FILE_EXT}")
38
+ tmp_filename = "#{plugin_filename}.tmp"
39
+
40
+ build_internal(tmp_filename)
41
+ compute_sha512(tmp_filename)
42
+ write_config_an_rename_file(tmp_filename, plugin_filename)
43
+
44
+ puts "\e[32m[SUCCESS]\e[0m Built #{@config.name} at #{plugin_filename}"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # Return the base directory of the plugin source code
51
+ def base_dir
52
+ if @in_project
53
+ File.join(SCRIPTS_FOLDER, @name)
54
+ else
55
+ @name == '.' ? '.' : @name
56
+ end
57
+ end
58
+
59
+ # Return the directory name used for scripts relative to base_dir
60
+ def script_src_dir
61
+ # Inside a project, scripts are often in `scripts/{plugin_name}/scripts/**/*.rb`
62
+ # Outside, it's `scripts/**/*.rb`
63
+ # In both cases, the folder is `scripts` relative to base_dir (or `script` according to some docs, we check both or use 'scripts') # rubocop:disable Layout/LineLength
64
+ return 'scripts'
65
+ end
66
+
67
+ # Internal plugin build process
68
+ # @param tmp_filename [String] temporary filename of the .psdkplug file (before SHA512 computation)
69
+ def build_internal(tmp_filename)
70
+ puts "Creating temporary plugin file: #{tmp_filename}"
71
+ @yuki_vd = Yuki::VD.new(tmp_filename, :write)
72
+
73
+ add_scripts
74
+ add_files
75
+ add_testers
76
+
77
+ @yuki_vd.close
78
+ end
79
+
80
+ # Function that adds all the scripts for the plugin
81
+ def add_scripts # rubocop:disable Metrics/MethodLength
82
+ b_dir = base_dir
83
+ b_dir_prefix = b_dir == '.' ? '' : "#{b_dir}/"
84
+
85
+ search_path = File.join(b_dir, script_src_dir, '**', '*.rb')
86
+ search_path = search_path.sub(%r{^/}, '') if search_path.start_with?('/')
87
+
88
+ scripts = Dir[search_path]
89
+
90
+ puts "Found #{scripts.size} ruby scripts to pack."
91
+ scripts.each do |filename|
92
+ script = File.read(filename)
93
+ internal_path = filename.sub(b_dir_prefix, '')
94
+ puts " - Packing script: #{filename} -> #{internal_path}"
95
+ @yuki_vd.write_data(internal_path, script)
96
+ end
97
+ end
98
+
99
+ # Function that add all the files for the plugin
100
+ def add_files
101
+ filenames = (@config.added_files || []).flat_map { |dirspec| Dir[dirspec] }.select { |f| File.file?(f) }
102
+
103
+ puts "Found #{filenames.size} resource files to pack."
104
+ filenames.each do |filename|
105
+ data = File.binread(filename)
106
+ puts " - Packing file: #{filename}"
107
+ @yuki_vd.write_data(filename, data)
108
+ end
109
+ end
110
+
111
+ # Function that adds the compatibility test script
112
+ def add_testers # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
113
+ b_dir = base_dir
114
+ if @config.psdk_compatibility_script
115
+ tester_path = File.join(b_dir, @config.psdk_compatibility_script)
116
+ if File.exist?(tester_path)
117
+ puts "Adding PSDK compatibility script: #{tester_path}"
118
+ data = File.read(tester_path)
119
+ @yuki_vd.write_data("\x01", data)
120
+ else
121
+ puts "[WARNING] PSDK compatibility script not found: #{tester_path}"
122
+ end
123
+ end
124
+ return unless @config.additional_compatibility_script
125
+
126
+ tester_path = File.join(b_dir, @config.additional_compatibility_script)
127
+ if File.exist?(tester_path)
128
+ puts "Adding additional compatibility script: #{tester_path}"
129
+ data = File.read(tester_path)
130
+ @yuki_vd.write_data("\x02", data)
131
+ else
132
+ puts "[WARNING] Additional compatibility script not found: #{tester_path}"
133
+ end
134
+ end
135
+
136
+ # Load the plugin configuration
137
+ # @return [PluginManager::Config]
138
+ def load_plugin_configuration
139
+ config_path = File.join(base_dir, 'config.yml')
140
+ raise "Configuration file not found at #{config_path}" unless File.exist?(config_path)
141
+
142
+ puts "Loading configuration from #{config_path}"
143
+ return YAML.unsafe_load(File.read(config_path))
144
+ end
145
+
146
+ # Compute the SHA512 hash of the temporary psdkplug
147
+ # @param tmp_filename [String] filename of the temporary psdkplug
148
+ def compute_sha512(tmp_filename)
149
+ puts 'Computing SHA512 of the generated package...'
150
+ filesize = File.binread(tmp_filename, Yuki::VD::POINTER_SIZE).unpack1(Yuki::VD::UNPACK_METHOD) - Yuki::VD::POINTER_SIZE
151
+ filedata = File.binread(tmp_filename, filesize, Yuki::VD::POINTER_SIZE)
152
+ @config.sha512 = Digest::SHA512.hexdigest(filedata)
153
+ end
154
+
155
+ # Write the config of the plugin and rename the temporary file to the final plugin filename
156
+ # @param tmp_filename [String] filename of the temporary psdkplug
157
+ # @param plugin_filename [String] filename of the final psdkplug
158
+ def write_config_an_rename_file(tmp_filename, plugin_filename)
159
+ puts 'Writing final configuration with SHA512 to package...'
160
+ @yuki_vd = Yuki::VD.new(tmp_filename, :update)
161
+ @yuki_vd.write_data("\x00", Marshal.dump(@config))
162
+ @yuki_vd.close
163
+
164
+ File.rename(tmp_filename, plugin_filename)
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PluginManager
4
+ # Plugin configuration
5
+ class Config
6
+ # Get the plugin name
7
+ # @return [String]
8
+ attr_accessor :name
9
+ # Get the plugin authors
10
+ # @return [Array<String>]
11
+ attr_accessor :authors
12
+ # Get the version of the plugin
13
+ # @return [String]
14
+ attr_accessor :version
15
+ # Get the dependecies or incompatibilities of the plugin
16
+ # @return [Array<Hash>]
17
+ attr_accessor :deps
18
+ # Get the script that tests if PSDK is compatible with this plugin
19
+ # @return [String, nil]
20
+ attr_accessor :psdk_compatibility_script
21
+ # Tell if the psdk_compatibility_script should be executed after all plugins has been loaded
22
+ # @return [Boolean, nil]
23
+ attr_accessor :retry_psdk_compatibility_after_plugin_load
24
+ # Get the script that tests if the plugin is compatible with other plugins
25
+ # @return [String, nil]
26
+ attr_accessor :additional_compatibility_script
27
+ # Get all the files added by the plugin (in order to compile the plugin / remove files)
28
+ # @return [Array<String>]
29
+ attr_accessor :added_files
30
+ # Get the SHA512 of the plugin (computed after it got compiled)
31
+ # @return [String]
32
+ attr_accessor :sha512
33
+ # Get the PSDK version the plugin was installed
34
+ # @return [Integer]
35
+ attr_accessor :psdk_version
36
+ end
37
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PluginManager
4
+ # Module handling the listing of plugins
5
+ module List
6
+ # Folder containing scripts
7
+ SCRIPTS_FOLDER = 'scripts'
8
+ # File containing plugin information
9
+ PLUGIN_INFO_FILE = "#{SCRIPTS_FOLDER}/plugins.dat".freeze
10
+
11
+ class << self
12
+ # List all the plugins
13
+ def list_plugins
14
+ plugins = load_existing_plugins
15
+ show_splash(' List of your plugins')
16
+ if plugins.empty?
17
+ puts 'No plugins installed.'
18
+ return
19
+ end
20
+
21
+ plugins.each do |plugin|
22
+ puts "- \e[34m#{plugin.name}\e[36m v#{plugin.version}\e[0m"
23
+ puts " authors: #{plugin.authors.join(', ')}"
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ # Load the plugins that are already installed
30
+ # @return [Array<PluginManager::Config>]
31
+ def load_existing_plugins
32
+ return File.exist?(PLUGIN_INFO_FILE) ? Marshal.load(File.binread(PLUGIN_INFO_FILE)) : [] # rubocop:disable Security/MarshalLoad
33
+ rescue StandardError => e
34
+ puts "Failed to load plugins.dat: #{e.message}"
35
+ []
36
+ end
37
+
38
+ # Show the plugin manager splash
39
+ # @param reason [String] reason to show the splash
40
+ def show_splash(reason = ' Something changed in your plugins! ')
41
+ sep = ''.center(80, '=')
42
+ puts "\e[32m#{sep}\e[0m"
43
+ puts "\e[32m##{' PSDK Plugin Manager v1.0 '.center(78, ' ')}#\e[0m"
44
+ puts "\e[32m##{reason.ljust(78, ' ')}#\e[0m"
45
+ puts "\e[32m#{sep}\e[0m"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'plugin_manager/config'
4
+ require_relative 'plugin_manager/list'
5
+ require_relative 'plugin_manager/builder'
6
+
7
+ # Module handling the plugin commands
8
+ module PluginManager
9
+ class << self
10
+ # List all the plugins installed in the current PSDK project
11
+ def list
12
+ List.list_plugins
13
+ end
14
+
15
+ # Build a plugin
16
+ # @param plugin_name [String] name of the plugin to build
17
+ # @param in_project [Boolean] whether to build using PSDK project structure
18
+ # @param out_dir [String] directory to output the compiled plugin
19
+ def build(plugin_name, in_project: true, out_dir: '.')
20
+ Builder.new(plugin_name, in_project: in_project, out_dir: out_dir).build
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cli/configuration'
4
+ require 'fileutils'
5
+
6
+ module Psdk
7
+ module Cli
8
+ # Module holding all the utility to interact with PSDK repository
9
+ module PSDK # rubocop:disable Metrics/ModuleLength
10
+ # Default URL to the PSDK repository
11
+ MAIN_REPOSITORY_URL = 'https://gitlab.com/pokemonsdk/pokemonsdk.git'
12
+
13
+ module_function
14
+
15
+ # Ensure the PSDK module is cloned
16
+ def ensure_repository_cloned
17
+ return if Dir.exist?(File.join(repository_path, '.git'))
18
+
19
+ res = system('git', 'clone', MAIN_REPOSITORY_URL, chdir: Configuration::PATH)
20
+ return if res
21
+
22
+ puts "[Error] Failed to setup pokemonsdk repository in `#{Configuration::PATH}`"
23
+ exit(1)
24
+ end
25
+
26
+ # Get the repository path
27
+ # @return [String]
28
+ def repository_path
29
+ return File.join(Configuration::PATH, 'pokemonsdk')
30
+ end
31
+
32
+ # Make the project use an official PSDK release
33
+ # @param version [String] release identifier (e.g., "26.58")
34
+ def use_version(version)
35
+ switch_project_repository("official release #{version}") do |path|
36
+ fetch_repository(path)
37
+ commit = run_git(path, 'log', '--format=%H', '--extended-regexp',
38
+ "--grep=^Release #{Regexp.escape(version)}$", '--max-count=1', 'origin/release')
39
+ raise "PSDK release #{version} was not found" if commit.empty?
40
+
41
+ run_git(path, 'checkout', '--detach', commit)
42
+ end
43
+ end
44
+
45
+ # Make the project use a specific PSDK commit
46
+ # @param commit [String] commit identifier (e.g., "deadcafe")
47
+ def use_commit(commit)
48
+ switch_project_repository("commit #{commit}") do |path|
49
+ fetch_repository(path)
50
+ resolved_commit = run_git(path, 'rev-parse', '--verify', '--end-of-options', "#{commit}^{commit}")
51
+ run_git(path, 'checkout', '--detach', resolved_commit)
52
+ end
53
+ end
54
+
55
+ # Make the project use the head of a GitLab merge request
56
+ # @param id [String] merge request ID (e.g., "123")
57
+ def use_mr(id)
58
+ switch_project_repository("merge request !#{id}") do |path|
59
+ ref = "refs/remotes/origin/merge-requests/#{id}"
60
+ run_git(path, 'fetch', 'origin', "+refs/merge-requests/#{id}/head:#{ref}")
61
+ run_git(path, 'checkout', '-B', "mr-#{id}", ref)
62
+ end
63
+ end
64
+
65
+ # Make the project use the latest development commit
66
+ def use_latest
67
+ switch_project_repository('latest development commit') do |path|
68
+ fetch_repository(path)
69
+ run_git(path, 'checkout', '-B', 'development', 'origin/development')
70
+ end
71
+ end
72
+
73
+ # Unuse the local pokemonsdk folder (meaning we want the project to fallback on Pokémon Studio's PSDK)
74
+ # @param delete [Boolean] if the folder should be deleted
75
+ def unuse_local_pokemonsdk(delete:)
76
+ project_path = Configuration.project_path
77
+ psdk_path = File.join(project_path, 'pokemonsdk')
78
+ return unless Dir.exist?(psdk_path)
79
+
80
+ if git_project?(project_path) && submodule?(project_path)
81
+ remove_submodule(project_path, delete)
82
+ else
83
+ handle_non_submodule_folder(psdk_path, delete)
84
+ end
85
+ ensure
86
+ puts "Successfully set project to use Pokémon Studio's PSDK version"
87
+ end
88
+
89
+ # Handle the pokemonsdk folder when it's not a submodule
90
+ # @param psdk_path [String] the path to the pokemonsdk folder
91
+ # @param delete [Boolean] if the folder should be deleted
92
+ def handle_non_submodule_folder(psdk_path, delete)
93
+ if delete
94
+ FileUtils.rm_rf(psdk_path)
95
+ else
96
+ rename_pokemonsdk_folder(psdk_path)
97
+ end
98
+ end
99
+
100
+ # Check if the project is a git project
101
+ # @param project_path [String] the path to the project
102
+ # @return [Boolean]
103
+ def git_project?(project_path)
104
+ return File.exist?(File.join(project_path, '.git'))
105
+ end
106
+
107
+ # Check if the project is a submodule
108
+ # @param project_path [String] the path to the project
109
+ # @return [Boolean]
110
+ def submodule?(project_path)
111
+ return system('git', 'submodule', 'status', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL)
112
+ end
113
+
114
+ # Remove the submodule
115
+ # @param project_path [String] the path to the project
116
+ # @param delete [Boolean] if the folder should be deleted
117
+ def remove_submodule(project_path, delete)
118
+ return show_remove_submodule_delete_error unless delete
119
+
120
+ r = system('git', 'submodule', 'deinit', '-f', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL)
121
+ raise 'Failed to deinit pokemonsdk submodule' unless r
122
+
123
+ r = system('git', 'rm', '-f', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL)
124
+ raise 'Failed to remove pokemonsdk submodule' unless r
125
+
126
+ FileUtils.rm_rf(File.join(project_path, '.git', 'modules', 'pokemonsdk'))
127
+ puts 'Successfully removed the submodule'
128
+ rescue StandardError => e
129
+ puts "[Error] Failed to remove the submodule (#{e.message})"
130
+ exit(1)
131
+ end
132
+
133
+ # Show the error message when attempting to delete the pokemonsdk submodule
134
+ def show_remove_submodule_delete_error
135
+ puts "[Error] Cannot use Studio's PSDK version if the project has a submodule."
136
+ puts 'Please follow this guide to remove the submodule: https://stackoverflow.com/a/1260982'
137
+ exit(1)
138
+ end
139
+
140
+ # Rename the pokemonsdk folder
141
+ # @param psdk_path [String] the path to the pokemonsdk folder
142
+ def rename_pokemonsdk_folder(psdk_path)
143
+ new_path = "#{psdk_path}_old"
144
+ if File.exist?(new_path)
145
+ puts "[Error] Folder `#{new_path}` already exists. Please remove it manually."
146
+ exit(1)
147
+ else
148
+ File.rename(psdk_path, new_path)
149
+ end
150
+ end
151
+
152
+ # Run a repository switch: resolve the project's pokemonsdk path, apply the given block,
153
+ # then refresh submodules and report the active target
154
+ # @param target [String] human-readable description of the switch target, used in the confirmation message
155
+ # @yieldparam path [String] path to the project's pokemonsdk repository
156
+ def switch_project_repository(target)
157
+ path = ensure_project_repository
158
+ yield(path)
159
+ run_git(path, 'submodule', 'update', '--init', '--recursive')
160
+ show_active_target(path, target)
161
+ rescue StandardError => e
162
+ show_switch_error(e)
163
+ end
164
+
165
+ # Ensure the project's pokemonsdk repository exists, cloning it if necessary
166
+ # @return [String] path to the project's pokemonsdk repository
167
+ def ensure_project_repository
168
+ path = File.join(Configuration.project_path, 'pokemonsdk')
169
+ return path if File.exist?(File.join(path, '.git'))
170
+
171
+ raise "#{path} exists but is not a Git repository" if Dir.exist?(path)
172
+
173
+ success = system('git', 'clone', MAIN_REPOSITORY_URL, path)
174
+ raise "Failed to clone pokemonsdk into `#{path}`" unless success
175
+
176
+ return path
177
+ end
178
+
179
+ # Fetch the latest refs from origin
180
+ # @param path [String] path to the repository
181
+ def fetch_repository(path)
182
+ run_git(path, 'fetch', 'origin')
183
+ end
184
+
185
+ # Run a git command in the given repository and return its output
186
+ # @param path [String] path to the repository
187
+ # @param arguments [Array<String>] git subcommand and its arguments
188
+ # @return [String] the stripped stdout of the command
189
+ def run_git(path, *arguments)
190
+ output = IO.popen(['git', *arguments], chdir: path, &:read)
191
+ return output.strip if $?.success? # rubocop:disable Style/SpecialGlobalVars
192
+
193
+ raise "git #{arguments.join(' ')} failed"
194
+ end
195
+
196
+ # Show the confirmation message for the currently active PSDK target
197
+ # @param path [String] path to the repository
198
+ # @param target [String] human-readable description of the active target
199
+ def show_active_target(path, target)
200
+ commit = run_git(path, 'rev-parse', '--short', 'HEAD')
201
+ version = File.read(File.join(path, 'version.txt')).to_i
202
+ version_string = [version].pack('I>').unpack('C*').join('.').gsub(/^(0\.)+/, '')
203
+ puts "Active PSDK: #{target} (version #{version_string}, commit #{commit})"
204
+ end
205
+
206
+ # Show the error message when a PSDK switch operation fails
207
+ # @param error [StandardError] the error that was raised
208
+ def show_switch_error(error)
209
+ puts "[Error] Failed to switch PSDK: #{error.message}"
210
+ exit(1)
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cli/configuration'
4
+
5
+ module Psdk
6
+ module Cli
7
+ # Module holding all the logic about the Pokémon Studio
8
+ module Studio
9
+ module_function
10
+
11
+ # Find and Save Pokemon studio path
12
+ # @param type [:global | :local] where to save the located studio path
13
+ def find_and_save_path(type)
14
+ locations = common_studio_location.select { |l| Dir.exist?(l) }
15
+ binaries_locations = psdk_binaries_locations
16
+ studio_path = locations.find { |l| binaries_locations.any? { |b| Dir.exist?(File.join(l, b)) } }
17
+ unless studio_path
18
+ puts '[Error] failed to locate Pokemon Studio, please set it up manually'
19
+ exit(1)
20
+ end
21
+
22
+ puts "Located Pokemon Studio in `#{studio_path}`"
23
+ Configuration.get(type).studio_path = studio_path
24
+ Configuration.save
25
+ end
26
+
27
+ # Get the PSDK binary path based on Studio path
28
+ # @param path [String]
29
+ # @return [String | nil]
30
+ def psdk_binaries_path(path)
31
+ valid_path = psdk_binaries_locations.find { |l| Dir.exist?(File.join(path, l)) }
32
+ return nil unless valid_path
33
+
34
+ return File.join(path, valid_path)
35
+ end
36
+
37
+ # Get all the common Pokemon Studio location
38
+ # @return [Array<String>]
39
+ def common_studio_location
40
+ volumes = Dir['/Volumes/**'] + Dir['/dev/sd*']
41
+ return [
42
+ '/Applications/PokemonStudio.app',
43
+ *(ENV['AppData'] ? studio_app_data_location : nil),
44
+ *volumes.map { |v| File.join(v, 'projects', 'PokemonStudio') },
45
+ 'C:/Projects/PokemonStudio'
46
+ ]
47
+ end
48
+
49
+ # Get all the psdk-binaries common location in Studio
50
+ # @return [Array<String>]
51
+ def psdk_binaries_locations
52
+ return [
53
+ 'psdk-binaries',
54
+ 'Contents/Resources/psdk-binaries',
55
+ 'resources/psdk-binaries'
56
+ ]
57
+ end
58
+
59
+ # Get the location of Studio in appdata
60
+ # @return [String]
61
+ def studio_app_data_location
62
+ return File.join(ENV.fetch('AppData', '.'), '../Local/Programs/pokemon-studio')
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../cli/configuration'
4
+ require_relative 'studio'
5
+ require_relative 'psdk'
6
+
7
+ module Psdk
8
+ module Cli
9
+ # Module holding all the logic about the version command
10
+ module Version
11
+ module_function
12
+
13
+ # Run the version command
14
+ # @param no_psdk_version [Boolean] do not show PSDK version if true
15
+ def run(no_psdk_version)
16
+ puts "psdk-cli v#{VERSION}"
17
+ return if no_psdk_version
18
+
19
+ print "Searching for PSDK version...\r"
20
+ search_and_show_psdk_version
21
+ end
22
+
23
+ # Search and show the PSDK version
24
+ def search_and_show_psdk_version
25
+ search_and_show_global_psdk_version
26
+ search_and_show_local_psdk_version
27
+ end
28
+
29
+ # Search and show the global PSDK version
30
+ def search_and_show_global_psdk_version
31
+ PSDK.ensure_repository_cloned
32
+ psdk_path = PSDK.repository_path
33
+ show_global_psdk_version(psdk_path)
34
+ git_data = load_git_data(psdk_path)
35
+ puts "Global PSDK git Target: #{git_data}"
36
+ end
37
+
38
+ # Show the global PSDK version
39
+ # @param psdk_path [String] Path to the PSDK repository
40
+ def show_global_psdk_version(psdk_path)
41
+ version_string = version_to_string(load_version_integer(psdk_path))
42
+ puts "Global PSDK version: #{version_string} "
43
+ end
44
+
45
+ # Search and show the local PSDK version
46
+ def search_and_show_local_psdk_version
47
+ Configuration.get(:local)
48
+ return unless Configuration.project_path
49
+
50
+ psdk_path = File.join(Configuration.project_path, 'pokemonsdk')
51
+ return show_no_local_psdk_version unless Dir.exist?(psdk_path)
52
+
53
+ version_string = version_to_string(load_version_integer(psdk_path))
54
+ puts "Project PSDK version: #{version_string}"
55
+ git_data = load_git_data(psdk_path)
56
+ puts "Project's PSDK git target: #{git_data}" unless git_data.empty?
57
+ end
58
+
59
+ # Show that there's no local PSDK version
60
+ def show_no_local_psdk_version
61
+ Studio.find_and_save_path(:local) if Configuration.get(:local).studio_path.empty?
62
+ psdk_binaries_path = Studio.psdk_binaries_path(Configuration.get(:local).studio_path)
63
+ unless psdk_binaries_path
64
+ puts 'Project PSDK Version: Cannot locate Pokémon Studio or local repository...'
65
+ exit(1)
66
+ end
67
+
68
+ version_string = version_to_string(load_version_integer(File.join(psdk_binaries_path, 'pokemonsdk')))
69
+ puts "Project PSDK Version: #{version_string} (Pokémon Studio)"
70
+ end
71
+
72
+ # Load the Git data if any
73
+ # @param path [String] path to the PSDK installation
74
+ # @return [String]
75
+ def load_git_data(path)
76
+ Dir.chdir(path) do
77
+ return '' unless Dir.exist?('.git') || Dir.exist?('../.git')
78
+
79
+ commit = `git log --oneline -n 1`.chomp
80
+ branch = `git branch --show-current`.chomp
81
+ return "[#{branch}] #{commit}" unless branch.empty?
82
+
83
+ return "[!detached] #{commit}"
84
+ end
85
+ end
86
+
87
+ # Convert a version integer to a version string
88
+ # @param version [Integer]
89
+ # @return [String]
90
+ def version_to_string(version)
91
+ return [version].pack('I>').unpack('C*').join('.').gsub(/^(0\.)+/, '')
92
+ end
93
+
94
+ # Get the version integer from a path
95
+ # @param path [String] path where PSDK repository is
96
+ # @return [Integer]
97
+ def load_version_integer(path)
98
+ filename = File.join(path, 'version.txt')
99
+ return 0 unless File.exist?(filename)
100
+
101
+ return File.read(filename).to_i
102
+ end
103
+ end
104
+ end
105
+ end