From 4a493543deaefdc09b9b5649caaa6469c636403b Mon Sep 17 00:00:00 2001 From: David Taylor Date: Tue, 25 Jul 2023 14:31:27 +0100 Subject: [PATCH] DEV: Support new discourse-compatibility format on old core (#186) This introduces a fallback compatibility parser, identical to the one introduced in https://github.com/discourse/discourse/pull/22714 This will allow Docker Manager to parse new compatibility files, even when running on older Discourse instances. This is only intended as a temporary solution, and will be removed before the Discourse 3.2 release. --- .discourse-compatibility | 5 +- .../fallback_compatibility_parser.rb | 72 +++++++++++++++++++ lib/docker_manager/git_repo.rb | 10 ++- spec/lib/git_repo_spec.rb | 27 +++++++ 4 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 lib/docker_manager/fallback_compatibility_parser.rb diff --git a/.discourse-compatibility b/.discourse-compatibility index 3f7b505..edb8fb6 100644 --- a/.discourse-compatibility +++ b/.discourse-compatibility @@ -1,4 +1,3 @@ # Do you need to make fixes for Discourse v3.1.0.beta1 and below? -# Push to the `old-version-up-to-v3.1.0.beta1` branch, update the commit hash for "3.1.0.beta1" -# in the line below and push this file to the `main` branch. -3.1.0.beta1: 1b2d7eb0369155b6442b09adfb17b8942c04b093 +# Make a PR to the `old-version-up-to-v3.1.0.beta1` branch +3.1.0.beta1: old-version-up-to-v3.1.0.beta1 diff --git a/lib/docker_manager/fallback_compatibility_parser.rb b/lib/docker_manager/fallback_compatibility_parser.rb new file mode 100644 index 0000000..5a34636 --- /dev/null +++ b/lib/docker_manager/fallback_compatibility_parser.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +# This is the compatibility parser introduced in https://github.com/discourse/discourse/pull/22714 +# It's copied here to be used as a fallback when docker_manager is running on an older Discourse version. +# Should be removed just before the Discourse 3.2 release. +module DockerManager::FallbackCompatibilityParser + def self.find_compatible_resource(version_list, target_version = ::Discourse::VERSION::STRING) + return unless version_list.present? + + begin + version_list = YAML.safe_load(version_list) + rescue Psych::SyntaxError, Psych::DisallowedClass => e + end + + raise Discourse::InvalidVersionListError unless version_list.is_a?(Hash) + + version_list = + version_list + .transform_keys do |v| + Gem::Requirement.parse(v) + rescue Gem::Requirement::BadRequirementError => e + raise Discourse::InvalidVersionListError, "Invalid version specifier: #{v}" + end + .sort_by do |parsed_requirement, _| + operator, version = parsed_requirement + [version, operator == "<" ? 0 : 1] + end + + parsed_target_version = Gem::Version.new(target_version) + + lowest_matching_entry = + version_list.find do |parsed_requirement, target| + req_operator, req_version = parsed_requirement + req_operator = "<=" if req_operator == "=" + + if !%w[<= <].include?(req_operator) + raise Discourse::InvalidVersionListError, + "Invalid version specifier operator for '#{req_operator} #{req_version}'. Operator must be one of <= or <" + end + + resolved_requirement = Gem::Requirement.new("#{req_operator} #{req_version.to_s}") + resolved_requirement.satisfied_by?(parsed_target_version) + end + + return if lowest_matching_entry.nil? + + checkout_version = lowest_matching_entry[1] + + begin + Discourse::Utils.execute_command "git", + "check-ref-format", + "--allow-onelevel", + checkout_version + rescue RuntimeError + raise Discourse::InvalidVersionListError, "Invalid ref name: #{checkout_version}" + 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}", + ) + self.find_compatible_resource(compat_resource) if s.success? + rescue Discourse::InvalidVersionListError => e + $stderr.puts "Invalid version list in #{path}" + end +end diff --git a/lib/docker_manager/git_repo.rb b/lib/docker_manager/git_repo.rb index 176efea..ed7724f 100644 --- a/lib/docker_manager/git_repo.rb +++ b/lib/docker_manager/git_repo.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "fallback_compatibility_parser" + class DockerManager::GitRepo attr_reader :path, :name @@ -150,7 +152,13 @@ class DockerManager::GitRepo end def tracking_branch - branch = Discourse.find_compatible_git_resource(path) + branch = + begin + Discourse.find_compatible_git_resource(path) + rescue ArgumentError + DockerManager::FallbackCompatibilityParser.find_compatible_git_resource(path) + end + return branch if branch.present? head = upstream_branch diff --git a/spec/lib/git_repo_spec.rb b/spec/lib/git_repo_spec.rb index 654f740..14bc6dd 100644 --- a/spec/lib/git_repo_spec.rb +++ b/spec/lib/git_repo_spec.rb @@ -651,5 +651,32 @@ RSpec.describe DockerManager::GitRepo do include_examples "common tests" end + + context "with modern discourse-compatibility file" do + let!(:clone_method) { GitHelpers::CLONE_SHALLOW } + + before do + @before_local_repo_clone << -> { + @remote_git_repo.commit( + filename: ".discourse-compatibility", + commits: [{ content: "<= 1000.0.0: twoPointFiveBranch", date: "2023-03-06T20:31:17Z" }], + ) + } + end + + it "works" do + DockerManager::FallbackCompatibilityParser.expects(:find_compatible_resource).never + expect(subject.tracking_ref).to eq("twoPointFiveBranch") + end + + it "works even in old core" do + Discourse + .stubs(:find_compatible_resource) + .with { |version_list| version_list.include?("<=") } + .raises(ArgumentError) + + expect(subject.tracking_ref).to eq("twoPointFiveBranch") + end + end end end