diff --git a/Gemfile b/Gemfile index a45fcd0..8d866e5 100644 --- a/Gemfile +++ b/Gemfile @@ -13,3 +13,5 @@ gem 'rspec', '~> 3.0' gem 'rubocop', '~> 1.21' gem 'thor', '~> 1.4.0' + +gem 'fileutils', '~> 1.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 50ffa79..39616f2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,6 +10,7 @@ GEM date (3.5.0) diff-lcs (1.6.2) erb (5.1.3) + fileutils (1.5.0) io-console (0.8.1) irb (1.15.3) pp (>= 0.6.0) @@ -79,6 +80,7 @@ PLATFORMS ruby DEPENDENCIES + fileutils (~> 1.5.0) irb psdk-cli! rake (~> 13.0) diff --git a/lib/psdk/cli/use.rb b/lib/psdk/cli/use.rb index 707c919..157ae86 100644 --- a/lib/psdk/cli/use.rb +++ b/lib/psdk/cli/use.rb @@ -13,13 +13,8 @@ class Use < Thor desc: 'delete local pokemonsdk folder' def studio ensure_project - # should_delete_local_pokemonsdk_folder = options[:delete] - # if should_delete_local_pokemonsdk_folder - # # TODO: delete pokemonsdk folder from project, remove submodule if it is a submodule - # else - # # TODO: rename pokemonsdk folder from project, remove submodule if it is a submodule - # end - puts options[:delete] + require_relative '../helpers/psdk' + PSDK.unuse_local_pokemonsdk(delete: options[:delete]) end desc 'version PSDK_VERSION', 'make the project use a specific PSDK version' diff --git a/lib/psdk/helpers/psdk.rb b/lib/psdk/helpers/psdk.rb index 66fdc82..7ad890c 100644 --- a/lib/psdk/helpers/psdk.rb +++ b/lib/psdk/helpers/psdk.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require_relative '../cli/configuration' +require 'fileutils' module Psdk module Cli @@ -27,6 +28,85 @@ def ensure_repository_cloned def repository_path return File.join(Configuration::PATH, 'pokemonsdk') end + + # Unuse the local pokemonsdk folder (meaning we want the project to fallback on Pokémon Studio's PSDK) + # @param delete [Boolean] if the folder should be deleted + def unuse_local_pokemonsdk(delete:) + project_path = Configuration.project_path + psdk_path = File.join(project_path, 'pokemonsdk') + return unless Dir.exist?(psdk_path) + + if git_project?(project_path) && submodule?(project_path) + remove_submodule(project_path, delete) + else + handle_non_submodule_folder(psdk_path, delete) + end + ensure + puts "Successfully set project to use Pokémon Studio's PSDK version" + end + + # Handle the pokemonsdk folder when it's not a submodule + # @param psdk_path [String] the path to the pokemonsdk folder + # @param delete [Boolean] if the folder should be deleted + def handle_non_submodule_folder(psdk_path, delete) + if delete + FileUtils.rm_rf(psdk_path) + else + rename_pokemonsdk_folder(psdk_path) + end + end + + # Check if the project is a git project + # @param project_path [String] the path to the project + # @return [Boolean] + def git_project?(project_path) + return File.exist?(File.join(project_path, '.git')) + end + + # Check if the project is a submodule + # @param project_path [String] the path to the project + # @return [Boolean] + def submodule?(project_path) + return system('git', 'submodule', 'status', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL) + end + + # Remove the submodule + # @param project_path [String] the path to the project + # @param delete [Boolean] if the folder should be deleted + def remove_submodule(project_path, delete) + return show_remove_submodule_delete_error unless delete + + r = system('git', 'submodule', 'deinit', '-f', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL) + raise 'Failed to deinit pokemonsdk submodule' unless r + + r = system('git', 'rm', '-f', 'pokemonsdk', chdir: project_path, out: File::NULL, err: File::NULL) + raise 'Failed to remove pokemonsdk submodule' unless r + + FileUtils.rm_rf(File.join(project_path, '.git', 'modules', 'pokemonsdk')) + puts 'Successfully removed the submodule' + rescue StandardError => e + puts "[Error] Failed to remove the submodule (#{e.message})" + exit(1) + end + + # Show the error message when attempting to delete the pokemonsdk submodule + def show_remove_submodule_delete_error + puts "[Error] Cannot use Studio's PSDK version if the project has a submodule." + puts 'Please follow this guide to remove the submodule: https://stackoverflow.com/a/1260982' + exit(1) + end + + # Rename the pokemonsdk folder + # @param psdk_path [String] the path to the pokemonsdk folder + def rename_pokemonsdk_folder(psdk_path) + new_path = "#{psdk_path}_old" + if File.exist?(new_path) + puts "[Error] Folder `#{new_path}` already exists. Please remove it manually." + exit(1) + else + File.rename(psdk_path, new_path) + end + end end end end diff --git a/spec/psdk/helpers/psdk_spec.rb b/spec/psdk/helpers/psdk_spec.rb new file mode 100644 index 0000000..c9db673 --- /dev/null +++ b/spec/psdk/helpers/psdk_spec.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'psdk/helpers/psdk' + +RSpec.describe Psdk::Cli::PSDK do # rubocop:disable Metrics/BlockLength + describe '.unuse_local_pokemonsdk' do # rubocop:disable Metrics/BlockLength + let(:project_path) { '/path/to/project' } + let(:psdk_path) { File.join(project_path, 'pokemonsdk') } + let(:delete_option) { false } + + before do + allow(Psdk::Cli::Configuration).to receive(:project_path).and_return(project_path) + allow(File).to receive(:join).with(project_path, 'pokemonsdk').and_return(psdk_path) + # Allow File.join with other arguments to work as usual + allow(File).to receive(:join).and_call_original + allow(Psdk::Cli::PSDK).to receive(:puts) + allow(Psdk::Cli::PSDK).to receive(:exit) { raise 'Exited 1' } + allow(FileUtils).to receive(:rm_rf) + allow(File).to receive(:rename) + allow(Psdk::Cli::PSDK).to receive(:system) + end + + context 'when psdk folder does not exist' do + before do + allow(Dir).to receive(:exist?).with(psdk_path).and_return(false) + end + + it 'does nothing' do + expect(Psdk::Cli::PSDK).not_to receive(:git_project?) + expect(FileUtils).not_to receive(:rm_rf) + expect(File).not_to receive(:rename) + + Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) + end + end + + context 'when psdk folder exists' do # rubocop:disable Metrics/BlockLength + before do + allow(Dir).to receive(:exist?).with(psdk_path).and_return(true) + # Default non-git, non-submodule for basic checking unless overridden + allow(Psdk::Cli::PSDK).to receive(:git_project?).with(project_path).and_return(false) + allow(Psdk::Cli::PSDK).to receive(:submodule?).with(project_path).and_return(false) + end + + context 'when it is not a git submodule' do # rubocop:disable Metrics/BlockLength + context 'with delete: true' do + let(:delete_option) { true } + + it 'deletes the folder' do + expect(FileUtils).to receive(:rm_rf).with(psdk_path) + expect(Psdk::Cli::PSDK).to receive(:puts).with( + "Successfully set project to use Pokémon Studio's PSDK version" + ) + + Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) + end + end + + context 'with delete: false' do + let(:delete_option) { false } + let(:new_path) { "#{psdk_path}_old" } + + context 'when _old folder already exists' do + before do + allow(File).to receive(:exist?).with(new_path).and_return(true) + end + + it 'exits with error' do + expect(Psdk::Cli::PSDK).to receive(:puts).with( + "[Error] Folder `#{new_path}` already exists. Please remove it manually." + ) + expect { Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) }.to raise_error('Exited 1') + end + end + + context 'when _old folder does not exist' do + before do + allow(File).to receive(:exist?).with(new_path).and_return(false) + end + + it 'renames the folder' do + expect(File).to receive(:rename).with(psdk_path, new_path) + expect(Psdk::Cli::PSDK).to receive(:puts).with( + "Successfully set project to use Pokémon Studio's PSDK version" + ) + + Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) + end + end + end + end + + context 'when it IS a git submodule' do # rubocop:disable Metrics/BlockLength + before do + allow(Psdk::Cli::PSDK).to receive(:git_project?).with(project_path).and_return(true) + allow(Psdk::Cli::PSDK).to receive(:submodule?).with(project_path).and_return(true) + end + + context 'with delete: false' do + let(:delete_option) { false } + + it 'exits with error advising to remove submodule manually' do + expect(Psdk::Cli::PSDK).to receive(:puts).with( + "[Error] Cannot use Studio's PSDK version if the project has a submodule." + ) + expect(Psdk::Cli::PSDK).to receive(:puts).with('Please follow this guide to remove the submodule: https://stackoverflow.com/a/1260982') + expect { Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) }.to raise_error('Exited 1') + end + end + + context 'with delete: true' do # rubocop:disable Metrics/BlockLength + let(:delete_option) { true } + + it 'removes the submodule successfully' do + # Mock successful git commands + expect(Psdk::Cli::PSDK).to receive(:system).with('git', 'submodule', 'deinit', '-f', 'pokemonsdk', + chdir: project_path, out: File::NULL, err: File::NULL).and_return(true) + expect(Psdk::Cli::PSDK).to receive(:system).with('git', 'rm', '-f', 'pokemonsdk', chdir: project_path, + out: File::NULL, err: File::NULL).and_return(true) + + # Module folder cleanup + expect(FileUtils).to receive(:rm_rf).with(File.join(project_path, '.git', 'modules', 'pokemonsdk')) + + expect(Psdk::Cli::PSDK).to receive(:puts).with('Successfully removed the submodule') + expect(Psdk::Cli::PSDK).to receive(:puts).with( + "Successfully set project to use Pokémon Studio's PSDK version" + ) + + Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) + end + + it 'fails to deinit submodule' do + expect(Psdk::Cli::PSDK).to receive(:system).with('git', 'submodule', 'deinit', any_args).and_return(false) + + expect(Psdk::Cli::PSDK).to receive(:puts).with( + '[Error] Failed to remove the submodule (Failed to deinit pokemonsdk submodule)' + ) + expect { Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) }.to raise_error('Exited 1') + end + + it 'fails to remove submodule' do + expect(Psdk::Cli::PSDK).to receive(:system).with('git', 'submodule', 'deinit', any_args).and_return(true) + expect(Psdk::Cli::PSDK).to receive(:system).with('git', 'rm', any_args).and_return(false) + + expect(Psdk::Cli::PSDK).to receive(:puts).with( + '[Error] Failed to remove the submodule (Failed to remove pokemonsdk submodule)' + ) + expect { Psdk::Cli::PSDK.unuse_local_pokemonsdk(delete: delete_option) }.to raise_error('Exited 1') + end + end + end + end + end +end