discourse/lib/tasks/plugin.rake
Jeff Wong 339549d14a
Support plugin and Theme compatibility version manifests (#9995)
Adds a new rake task `plugin:checkout_compatible_all` and
`plugin:checkout_compatible[plugin-name]` that check out compatible plugin
versions.

Supports a .discourse-compatibility file in the root of plugins and themes that
list out a plugin's compatibility with certain discourse versions:

eg: .discourse-compatibility
```
2.5.0.beta6: some-git-hash
2.4.4.beta4: some-git-tag
2.2.0: git-reference
```

This ensures older Discourse installs are able to find and install older
versions of plugins without intervention, through the manifest only.

It iterates through the versions in descending order. If the current Discourse
version matches an item in the manifest, it checks out the listed plugin target.
If the Discourse version is greater than an item in the manifest, it checks out
the next highest version listed in the manifest.

If no versions match, it makes no change.
2020-07-06 14:48:00 -07:00

217 lines
6.4 KiB
Ruby

# frozen_string_literal: true
directory 'plugins'
desc 'install all official plugins (use GIT_WRITE=1 to pull with write access)'
task 'plugin:install_all_official' do
skip = Set.new([
'customer-flair',
'discourse-nginx-performance-report',
'lazy-yt',
'poll'
])
map = {
'Canned Replies' => 'https://github.com/discourse/discourse-canned-replies',
'discourse-perspective' => 'https://github.com/discourse/discourse-perspective-api'
}
STDERR.puts "Allowing write to all repos!" if ENV['GIT_WRITE']
Plugin::Metadata::OFFICIAL_PLUGINS.each do |name|
next if skip.include? name
repo = map[name] || "https://github.com/discourse/#{name}"
dir = repo.split('/').last
path = File.expand_path('plugins/' + dir)
if Dir.exist? path
STDERR.puts "Skipping #{dir} cause it already exists!"
next
end
if ENV['GIT_WRITE']
repo = repo.gsub("https://github.com/", "git@github.com:")
repo += ".git"
end
status = system("git clone #{repo} #{path}")
unless status
abort("Failed to clone #{repo}")
end
end
end
desc 'install plugin'
task 'plugin:install', :repo do |t, args|
repo = ENV['REPO'] || ENV['repo'] || args[:repo]
name = ENV['NAME'] || ENV['name'] || File.basename(repo, '.git')
plugin_path = File.expand_path('plugins/' + name)
if File.directory?(File.expand_path(plugin_path))
abort('Plugin directory, ' + plugin_path + ', already exists.')
end
clone_status = system('git clone ' + repo + ' ' + plugin_path)
unless clone_status
FileUtils.rm_rf(plugin_path)
abort('Unable to clone plugin')
end
end
desc 'update all plugins'
task 'plugin:update_all' do |t|
# Loop through each directory
plugins = Dir.glob(File.expand_path('plugins/*')).select { |f| File.directory? f }
# run plugin:update
plugins.each do |plugin|
next unless File.directory?(plugin + "/.git")
Rake::Task['plugin:update'].invoke(plugin)
Rake::Task['plugin:update'].reenable
end
end
desc 'update a plugin'
task 'plugin:update', :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
update_status = system('git --git-dir "' + plugin_path + '/.git" --work-tree "' + plugin_path + '" pull')
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 }
plugins.each do |plugin|
Rake::Task['plugin:install_gems'].invoke(plugin)
Rake::Task['plugin:install_gems'].reenable
end
end
desc 'install plugin gems'
task 'plugin:install_gems', :plugin do |t, args|
plugin = ENV['PLUGIN'] || ENV['plugin'] || args[:plugin]
plugin_path = plugin + "/plugin.rb"
if File.file?(plugin_path)
File.open(plugin_path).each do |l|
next if !l.start_with? "gem"
next unless /gem\s['"](.*)['"],\s['"](.*)['"]/.match(l)
puts "gem install #{$1} -v #{$2} -i #{plugin}/gems/#{RUBY_VERSION} --no-document --ignore-dependencies --no-user-install"
system("gem install #{$1} -v #{$2} -i #{plugin}/gems/#{RUBY_VERSION} --no-document --ignore-dependencies --no-user-install")
end
end
end
desc 'run plugin specs'
task 'plugin:spec', :plugin do |t, args|
args.with_defaults(plugin: "*")
params = ENV['RSPEC_FAILFAST'] ? '--profile --fail-fast' : '--profile'
ruby = `which ruby`.strip
files = Dir.glob("./plugins/#{args[:plugin]}/spec/**/*_spec.rb")
if files.length > 0
sh "LOAD_PLUGINS=1 #{ruby} -S rspec #{files.join(' ')} #{params}"
else
abort "No specs found."
end
end
desc 'run plugin qunit tests'
task 'plugin:qunit', [:plugin, :timeout] do |t, args|
args.with_defaults(plugin: "*")
rake = `which rake`.strip
cmd = 'LOAD_PLUGINS=1 '
cmd += 'QUNIT_SKIP_CORE=1 '
if args[:plugin] == "*"
puts "Running qunit tests for all plugins"
else
puts "Running qunit tests for #{args[:plugin]}"
cmd += "QUNIT_SINGLE_PLUGIN='#{args[:plugin]}' "
end
cmd += "#{rake} qunit:test"
cmd += "[#{args[:timeout]}]" if args[:timeout]
sh cmd
end
desc 'run all migrations of a plugin'
namespace 'plugin:migrate' do
def list_migrations(plugin_name)
plugin_root = File.join(Rails.root, "plugins", plugin_name)
migrations_root = File.join(plugin_root, "db", "{post_migrate,migrate}", "*.rb")
Dir[migrations_root]
.map do |migration_filename|
File.basename(migration_filename)[/(^.*?)_/, 1].to_i
end
.sort
end
def cmd(operation, migration_number)
"rails db:migrate:#{operation} LOAD_PLUGINS=1 VERSION=#{migration_number}"
end
task :down, [:plugin] do |t, args|
list_migrations(args[:plugin]).reverse.each do |migration_number|
sh cmd(:down, migration_number)
end
end
task :up, [:plugin] do |t, args|
list_migrations(args[:plugin]).each do |migration_number|
sh cmd(:up, migration_number)
end
end
end