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.
- checksums.yaml +4 -4
- data/.idea/plugin/PluginManager.md +65 -0
- data/.solargraph.yml +12 -11
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/exe/psdk-use +7 -0
- data/lib/psdk/cli/configuration.rb +139 -0
- data/lib/psdk/cli/plugin.rb +33 -4
- data/lib/psdk/cli/use.rb +60 -0
- data/lib/psdk/cli/version.rb +1 -1
- data/lib/psdk/cli.rb +15 -2
- data/lib/psdk/helpers/900 Yuki__VD.rb +147 -0
- data/lib/psdk/helpers/plugin_manager/builder.rb +167 -0
- data/lib/psdk/helpers/plugin_manager/config.rb +37 -0
- data/lib/psdk/helpers/plugin_manager/list.rb +49 -0
- data/lib/psdk/helpers/plugin_manager.rb +23 -0
- data/lib/psdk/helpers/psdk.rb +214 -0
- data/lib/psdk/helpers/studio.rb +66 -0
- data/lib/psdk/helpers/version.rb +105 -0
- data/lib/psdk/helpers/version_update.rb +60 -0
- metadata +15 -1
|
@@ -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
|