mirror of
https://github.com/discourse/discourse.git
synced 2024-12-14 19:13:40 +08:00
271d6319ce
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.
134 lines
3.9 KiB
Ruby
134 lines
3.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module ThemeStore; end
|
|
|
|
class ThemeStore::GitImporter
|
|
|
|
attr_reader :url
|
|
|
|
def initialize(url, private_key: nil, branch: nil)
|
|
@url = url
|
|
if @url.start_with?("https://github.com") && !@url.end_with?(".git")
|
|
@url = @url.gsub(/\/$/, '')
|
|
@url += ".git"
|
|
end
|
|
@temp_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_#{SecureRandom.hex}"
|
|
@private_key = private_key
|
|
@branch = branch
|
|
end
|
|
|
|
def import!
|
|
if @private_key
|
|
import_private!
|
|
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)
|
|
theme = Theme.find_by(remote_theme_id: remote_theme_id)
|
|
raise Discourse::InvalidParameters.new(:id) unless theme
|
|
local_version = theme.remote_theme&.local_version
|
|
|
|
exporter = ThemeStore::ZipExporter.new(theme)
|
|
local_temp_folder = exporter.export_to_folder
|
|
|
|
Discourse::Utils.execute_command(chdir: @temp_folder) do |runner|
|
|
runner.exec("git", "checkout", local_version)
|
|
runner.exec("rm -rf ./*/")
|
|
runner.exec("cp", "-rf", "#{local_temp_folder}/#{exporter.export_name}/.", @temp_folder)
|
|
runner.exec("git", "checkout", "about.json")
|
|
# add + diff staged to catch uploads but exclude renamed assets
|
|
runner.exec("git", "add", "-A")
|
|
return runner.exec("git", "diff", "--staged", "--diff-filter=r")
|
|
end
|
|
ensure
|
|
FileUtils.rm_rf local_temp_folder if local_temp_folder
|
|
end
|
|
|
|
def commits_since(hash)
|
|
commit_hash, commits_behind = nil
|
|
|
|
Discourse::Utils.execute_command(chdir: @temp_folder) do |runner|
|
|
commit_hash = runner.exec("git", "rev-parse", "HEAD").strip
|
|
commits_behind = runner.exec("git", "rev-list", "#{hash}..HEAD", "--count").strip
|
|
end
|
|
|
|
[commit_hash, commits_behind]
|
|
end
|
|
|
|
def version
|
|
Discourse::Utils.execute_command("git", "rev-parse", "HEAD", chdir: @temp_folder).strip
|
|
end
|
|
|
|
def cleanup!
|
|
FileUtils.rm_rf(@temp_folder)
|
|
end
|
|
|
|
def real_path(relative)
|
|
fullpath = "#{@temp_folder}/#{relative}"
|
|
return nil unless File.exist?(fullpath)
|
|
|
|
# careful to handle symlinks here, don't want to expose random data
|
|
fullpath = Pathname.new(fullpath).realpath.to_s
|
|
|
|
if fullpath && fullpath.start_with?(@temp_folder)
|
|
fullpath
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def all_files
|
|
Dir.glob("**/*", base: @temp_folder).reject { |f| File.directory?(File.join(@temp_folder, f)) }
|
|
end
|
|
|
|
def [](value)
|
|
fullpath = real_path(value)
|
|
return nil unless fullpath
|
|
File.read(fullpath)
|
|
end
|
|
|
|
protected
|
|
|
|
def import_public!
|
|
begin
|
|
if @branch.present?
|
|
Discourse::Utils.execute_command("git", "clone", "--single-branch", "-b", @branch, @url, @temp_folder)
|
|
else
|
|
Discourse::Utils.execute_command("git", "clone", @url, @temp_folder)
|
|
end
|
|
rescue RuntimeError => err
|
|
raise RemoteTheme::ImportError.new(I18n.t("themes.import_error.git"))
|
|
end
|
|
end
|
|
|
|
def import_private!
|
|
ssh_folder = "#{Pathname.new(Dir.tmpdir).realpath}/discourse_theme_ssh_#{SecureRandom.hex}"
|
|
FileUtils.mkdir_p ssh_folder
|
|
|
|
File.write("#{ssh_folder}/id_rsa", @private_key)
|
|
FileUtils.chmod(0600, "#{ssh_folder}/id_rsa")
|
|
|
|
begin
|
|
git_ssh_command = { 'GIT_SSH_COMMAND' => "ssh -i #{ssh_folder}/id_rsa -o StrictHostKeyChecking=no" }
|
|
if @branch.present?
|
|
Discourse::Utils.execute_command(git_ssh_command, "git", "clone", "--single-branch", "-b", @branch, @url, @temp_folder)
|
|
else
|
|
Discourse::Utils.execute_command(git_ssh_command, "git", "clone", @url, @temp_folder)
|
|
end
|
|
rescue RuntimeError => err
|
|
raise RemoteTheme::ImportError.new(I18n.t("themes.import_error.git"))
|
|
end
|
|
ensure
|
|
FileUtils.rm_rf ssh_folder
|
|
end
|
|
|
|
end
|