diff --git a/lib/tasks/plugin.rake b/lib/tasks/plugin.rake index 9da1f433506..a5d26368b21 100644 --- a/lib/tasks/plugin.rake +++ b/lib/tasks/plugin.rake @@ -88,6 +88,45 @@ task 'plugin:update', :plugin do |t, args| abort('Unable to pull latest version of plugin') unless update_status end +desc 'pull compatible plugin versions for all plugins' +task 'plugin:pull_compatible_all' do |t| + # Loop through each directory + plugins = Dir.glob(File.expand_path('plugins/*')).select { |f| File.directory? f } + # run plugin:pull_compatible + plugins.each do |plugin| + next unless File.directory?(plugin + "/.git") + Rake::Task['plugin:pull_compatible'].invoke(plugin) + Rake::Task['plugin:pull_compatible'].reenable + end +end + +desc 'pull a compatible plugin version' +task 'plugin:pull_compatible', :plugin do |t, args| + + plugin = ENV['PLUGIN'] || ENV['plugin'] || args[:plugin] + plugin_path = plugin + plugin = File.basename(plugin) + + unless File.directory?(plugin_path) + if File.directory?('plugins/' + plugin) + plugin_path = File.expand_path('plugins/' + plugin) + else + abort('Plugin ' + plugin + ' not found') + end + end + + checkout_version = Discourse.find_compatible_git_resource(plugin_path) + + # Checkout value of the version compat + if checkout_version + puts "checking out compatible #{plugin} version: #{checkout_version}" + update_status = system("git -C '#{plugin_path}' reset --hard #{checkout_version}") + abort('Unable to checkout a compatible plugin version') unless update_status + else + puts "#{plugin} is already at latest compatible version" + end +end + desc 'install all plugin gems' task 'plugin:install_all_gems' do |t| plugins = Dir.glob(File.expand_path('plugins/*')).select { |f| File.directory? f } diff --git a/lib/theme_store/git_importer.rb b/lib/theme_store/git_importer.rb index c73d44f8070..2419464db82 100644 --- a/lib/theme_store/git_importer.rb +++ b/lib/theme_store/git_importer.rb @@ -23,6 +23,12 @@ class ThemeStore::GitImporter else import_public! end + if version = Discourse.find_compatible_git_resource(@temp_folder) + Discourse::Utils.execute_command(chdir: @temp_folder) do |runner| + Rails.logger.warn "git reset --hard #{version}" + return runner.exec("git", "reset", "--hard", version) + end + end end def diff_local_changes(remote_theme_id) diff --git a/lib/version.rb b/lib/version.rb index dbb5a6b5445..31cd01cf575 100644 --- a/lib/version.rb +++ b/lib/version.rb @@ -3,6 +3,8 @@ module Discourse VERSION_REGEXP = /\A\d+\.\d+\.\d+(\.beta\d+)?\z/ unless defined? ::Discourse::VERSION_REGEXP + VERSION_COMPATIBILITY_FILENAME = ".discourse-compatibility" + # work around reloader unless defined? ::Discourse::VERSION module VERSION #:nodoc: @@ -18,4 +20,40 @@ module Discourse def self.has_needed_version?(current, needed) Gem::Version.new(current) >= Gem::Version.new(needed) end + + # lookup an external resource (theme/plugin)'s best compatible version + # compatible resource files are YAML, in the format: + # `discourse_version: plugin/theme git reference.` For example: + # 2.5.0.beta6: c4a6c17 + # 2.5.0.beta4: d1d2d3f + # 2.5.0.beta2: bbffee + # 2.4.4.beta6: some-other-branch-ref + # 2.4.2.beta1: v1-tag + def self.find_compatible_resource(version_list) + + return unless version_list + + version_list = YAML.load(version_list).sort_by { |version, pin| Gem::Version.new(version) }.reverse + + # If plugin compat version is listed as less than current Discourse version, take the version/hash listed before. + checkout_version = nil + version_list.each do |core_compat, target| + if Gem::Version.new(core_compat) == Gem::Version.new(::Discourse::VERSION::STRING) # Exact version match - return it + checkout_version = target + break + elsif Gem::Version.new(core_compat) < Gem::Version.new(::Discourse::VERSION::STRING) # Core is on a higher version than listed, use a later version + break + end + checkout_version = target + end + + checkout_version + end + + # Find a compatible resource from a git repo + def self.find_compatible_git_resource(path) + return unless File.directory?("#{path}/.git") + compat_resource, std_error, s = Open3.capture3("git -C '#{path}' show HEAD@{upstream}:#{Discourse::VERSION_COMPATIBILITY_FILENAME}") + Discourse.find_compatible_resource(compat_resource) if s.success? + end end diff --git a/spec/components/version_spec.rb b/spec/components/version_spec.rb index 255ef64c1b0..e2dc8a58e1e 100644 --- a/spec/components/version_spec.rb +++ b/spec/components/version_spec.rb @@ -46,4 +46,61 @@ describe Discourse::VERSION do end end + + context "compatible_resource" do + after do + # Cleanup versions + ::Discourse::VERSION::STRING = [::Discourse::VERSION::MAJOR, ::Discourse::VERSION::MINOR, ::Discourse::VERSION::TINY, ::Discourse::VERSION::PRE].compact.join('.') + end + + shared_examples "test compatible resource" do + it "returns nil when the current version is above all pinned versions" do + ::Discourse::VERSION::STRING = "2.6.0" + expect(Discourse.find_compatible_resource(version_list)).to be_nil + end + + it "returns the correct version if matches exactly" do + ::Discourse::VERSION::STRING = "2.5.0.beta4" + expect(Discourse.find_compatible_resource(version_list)).to eq("twofivebetafour") + end + + it "returns the closest matching version" do + ::Discourse::VERSION::STRING = "2.4.6.beta12" + expect(Discourse.find_compatible_resource(version_list)).to eq("twofivebetatwo") + end + + it "returns the lowest version possible when using an older version" do + ::Discourse::VERSION::STRING = "1.4.6.beta12" + expect(Discourse.find_compatible_resource(version_list)).to eq("twofourtwobetaone") + end + end + + it "returns nil when nil" do + expect(Discourse.find_compatible_resource(nil)).to be_nil + end + + context "with a regular compatible list" do + let(:version_list) { <<~VERSION_LIST + 2.5.0.beta6: twofivebetasix + 2.5.0.beta4: twofivebetafour + 2.5.0.beta2: twofivebetatwo + 2.4.4.beta6: twofourfourbetasix + 2.4.2.beta1: twofourtwobetaone + VERSION_LIST + } + include_examples "test compatible resource" + end + + context "handle a compatible resource out of order" do + let(:version_list) { <<~VERSION_LIST + 2.4.2.beta1: twofourtwobetaone + 2.5.0.beta4: twofivebetafour + 2.5.0.beta6: twofivebetasix + 2.5.0.beta2: twofivebetatwo + 2.4.4.beta6: twofourfourbetasix + VERSION_LIST + } + include_examples "test compatible resource" + end + end end