From 04aaf71932646efd99f2abd74fc59e3129fcbe1d Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 17:03:14 +0100 Subject: Inline the gitlab-flowdock-git-hooks gem This allows us to avoid one transitive dependency on gitlab-grit. The aim is to remove all transitive dependencies. --- Gemfile | 2 +- Gemfile.lock | 6 +- Gemfile.rails5.lock | 6 +- app/models/project_services/flowdock_service.rb | 2 +- lib/flowdock/git.rb | 96 +++++++++++++++ lib/flowdock/git/builder.rb | 150 ++++++++++++++++++++++++ 6 files changed, 250 insertions(+), 12 deletions(-) create mode 100644 lib/flowdock/git.rb create mode 100644 lib/flowdock/git/builder.rb diff --git a/Gemfile b/Gemfile index 6772abeb49d..590ed17508e 100644 --- a/Gemfile +++ b/Gemfile @@ -210,7 +210,7 @@ gem 'hipchat', '~> 1.5.0' gem 'jira-ruby', '~> 1.4' # Flowdock integration -gem 'gitlab-flowdock-git-hook', '~> 1.0.1' +gem 'flowdock', '~> 0.7' # Slack integration gem 'slack-notifier', '~> 1.5.1' diff --git a/Gemfile.lock b/Gemfile.lock index bd95f417caa..e0c098c3d8d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -278,10 +278,6 @@ GEM google-protobuf (~> 3.1) grpc (~> 1.10) github-markup (1.7.0) - gitlab-flowdock-git-hook (1.0.1) - flowdock (~> 0.7) - gitlab-grit (>= 2.4.1) - multi_json gitlab-gollum-lib (4.2.7.5) gemojione (~> 3.2) github-markup (~> 1.6) @@ -1009,6 +1005,7 @@ DEPENDENCIES flipper (~> 0.13.0) flipper-active_record (~> 0.13.0) flipper-active_support_cache_store (~> 0.13.0) + flowdock (~> 0.7) fog-aliyun (~> 0.2.0) fog-aws (~> 2.0.1) fog-core (~> 1.44) @@ -1025,7 +1022,6 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-markup (~> 1.7.0) - gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) gitlab-markup (~> 1.6.4) gitlab-sidekiq-fetcher diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 8aebf78924f..56db3b4c9de 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -281,10 +281,6 @@ GEM google-protobuf (~> 3.1) grpc (~> 1.10) github-markup (1.7.0) - gitlab-flowdock-git-hook (1.0.1) - flowdock (~> 0.7) - gitlab-grit (>= 2.4.1) - multi_json gitlab-gollum-lib (4.2.7.5) gemojione (~> 3.2) github-markup (~> 1.6) @@ -1018,6 +1014,7 @@ DEPENDENCIES flipper (~> 0.13.0) flipper-active_record (~> 0.13.0) flipper-active_support_cache_store (~> 0.13.0) + flowdock (~> 0.7) fog-aliyun (~> 0.2.0) fog-aws (~> 2.0.1) fog-core (~> 1.44) @@ -1034,7 +1031,6 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-markup (~> 1.7.0) - gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) gitlab-markup (~> 1.6.4) gitlab-sidekiq-fetcher diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 2545df06f6b..365792f03c3 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require "flowdock-git-hook" +require 'flowdock/git' # Flow dock depends on Grit to compute the number of commits between two given # commits. To make this depend on Gitaly, a monkey patch is applied diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb new file mode 100644 index 00000000000..43e729c27d7 --- /dev/null +++ b/lib/flowdock/git.rb @@ -0,0 +1,96 @@ + +require "multi_json" +require "cgi" +require "flowdock" +require "flowdock/git/builder" + +module Flowdock + class Git + class TokenError < StandardError; end + + class << self + def post(ref, from, to, options = {}) + Git.new(ref, from, to, options).post + end + + def background_post(ref, from, to, options = {}) + Git.new(ref, from, to, options).background_post + end + end + + def initialize(ref, from, to, options = {}) + @ref = ref + @from = from + @to = to + @options = options + @token = options[:token] || config["flowdock.token"] || raise(TokenError.new("Flowdock API token not found")) + @commit_url = options[:commit_url] || config["flowdock.commit-url-pattern"] || nil + @diff_url = options[:diff_url] || config["flowdock.diff-url-pattern"] || nil + @repo_url = options[:repo_url] || config["flowdock.repository-url"] || nil + @repo_name = options[:repo_name] || config["flowdock.repository-name"] || nil + @permanent_refs = options[:permanent_refs] || + (config["flowdock.permanent-references"] || "refs/heads/master") + .split(",") + .map(&:strip) + .map {|exp| Regexp.new(exp) } + end + + # Send git push notification to Flowdock + def post + messages.each do |message| + Flowdock::Client.new(flow_token: @token).post_to_thread(message) + end + end + + # Create and post notification in background process. Avoid blocking the push notification. + def background_post + pid = Process.fork + if pid.nil? + Grit::Git.with_timeout(600) do + post + end + else + Process.detach(pid) # Parent + end + end + + def repo + @repo ||= Grit::Repo.new( + @options[:repo] || Dir.pwd, + is_bare: @options[:is_bare] || false + ) + end + + private + + def messages + Git::Builder.new(repo: @repo, + ref: @ref, + before: @from, + after: @to, + commit_url: @commit_url, + branch_url: @branch_url, + diff_url: @diff_url, + repo_url: @repo_url, + repo_name: @repo_name, + permanent_refs: @permanent_refs, + tags: tags + ).to_hashes + end + + # Flowdock tags attached to the push notification + def tags + if @options[:tags] + @options[:tags] + else + config["flowdock.tags"].to_s.split(",").map(&:strip) + end.map do |t| + CGI.escape(t) + end + end + + def config + @config ||= Grit::Config.new(repo) + end + end +end diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb new file mode 100644 index 00000000000..4aec6a86df1 --- /dev/null +++ b/lib/flowdock/git/builder.rb @@ -0,0 +1,150 @@ +require "grit" +require 'cgi' +require "securerandom" + +module Flowdock + class Git + class Commit + def initialize(external_thread_id, thread, tags, commit) + @commit = commit + @external_thread_id = external_thread_id + @thread = thread + @tags = tags + end + + def to_hash + hash = { + external_thread_id: @external_thread_id, + event: "activity", + author: { + name: @commit[:author][:name], + email: @commit[:author][:email] + }, + title: title, + thread: @thread, + body: body + } + hash[:tags] = @tags if @tags + encode(hash) + end + + private + + def encode(hash) + return hash unless "".respond_to? :encode + encode_as_utf8(hash) + end + + # This only works on Ruby 1.9 + def encode_as_utf8(obj) + if obj.is_a? Hash + obj.each_pair do |key, val| + encode_as_utf8(val) + end + elsif obj.is_a?(Array) + obj.each do |val| + encode_as_utf8(val) + end + elsif obj.is_a?(String) && obj.encoding != Encoding::UTF_8 + if !obj.force_encoding("UTF-8").valid_encoding? + obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, :invalid => :replace, :undef => :replace) + end + end + end + + def body + content = @commit[:message][first_line.size..-1] + content.strip! if content + "
#{content}
" unless content.empty? + end + + def first_line + @first_line ||= (@commit[:message].split("\n")[0] || @commit[:message]) + end + + def title + commit_id = @commit[:id][0, 7] + if @commit[:url] + "#{commit_id} #{message_title}" + else + "#{commit_id} #{message_title}" + end + end + + def message_title + CGI.escape_html(first_line.strip) + end + end + + # Class used to build Git payload + class Builder + def initialize(opts) + @repo = opts[:repo] + @ref = opts[:ref] + @before = opts[:before] + @after = opts[:after] + @opts = opts + end + + def commits + @repo.commits_between(@before, @after).map do |commit| + { + url: if @opts[:commit_url] then @opts[:commit_url] % [commit.sha] end, + id: commit.sha, + message: commit.message, + author: { + name: commit.author.name, + email: commit.author.email + } + } + end + end + + def ref_name + @ref.to_s.sub(/\Arefs\/(heads|tags)\//, '') + end + + def to_hashes + commits.map do |commit| + Commit.new(external_thread_id, thread, @opts[:tags], commit).to_hash + end + end + + private + + def thread + @thread ||= { + title: thread_title, + external_url: @opts[:repo_url] + } + end + + def permanent? + @permanent ||= @opts[:permanent_refs].select do |regex| + regex.match(@ref) + end.size > 0 + end + + def thread_title + action = if permanent? + "updated" + end + type = if @ref.match(%r(^refs/heads/)) + "branch" + else + "tag" + end + [@opts[:repo_name], type, ref_name, action].compact.join(" ") + end + + def external_thread_id + @external_thread_id ||= + if permanent? + SecureRandom.hex + else + @ref + end + end + end + end +end -- cgit v1.2.1 From 8d3222925eca278fc7641bdf319cc79c008a722a Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 17:25:35 +0100 Subject: Rubocop improvements --- lib/flowdock/git.rb | 30 ++++++++++++++++-------------- lib/flowdock/git/builder.rb | 29 ++++++++++++++--------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb index 43e729c27d7..d5e97dad3cf 100644 --- a/lib/flowdock/git.rb +++ b/lib/flowdock/git.rb @@ -1,4 +1,4 @@ - +# frozen_string_literal: true require "multi_json" require "cgi" require "flowdock" @@ -6,7 +6,7 @@ require "flowdock/git/builder" module Flowdock class Git - class TokenError < StandardError; end + TokenError = Class.new(StandardError) class << self def post(ref, from, to, options = {}) @@ -28,11 +28,12 @@ module Flowdock @diff_url = options[:diff_url] || config["flowdock.diff-url-pattern"] || nil @repo_url = options[:repo_url] || config["flowdock.repository-url"] || nil @repo_name = options[:repo_name] || config["flowdock.repository-name"] || nil - @permanent_refs = options[:permanent_refs] || - (config["flowdock.permanent-references"] || "refs/heads/master") - .split(",") - .map(&:strip) - .map {|exp| Regexp.new(exp) } + + refs = options[:permanent_refs] || config["flowdock.permanent-references"] || "refs/heads/master" + @permanent_refs = refs + .split(",") + .map(&:strip) + .map {|exp| Regexp.new(exp) } end # Send git push notification to Flowdock @@ -80,13 +81,14 @@ module Flowdock # Flowdock tags attached to the push notification def tags - if @options[:tags] - @options[:tags] - else - config["flowdock.tags"].to_s.split(",").map(&:strip) - end.map do |t| - CGI.escape(t) - end + tags = + if @options[:tags] + @options[:tags] + else + config["flowdock.tags"].to_s.split(",").map(&:strip) + end + + tags.map { |t| CGI.escape(t) } end def config diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb index 4aec6a86df1..3cb086f65b8 100644 --- a/lib/flowdock/git/builder.rb +++ b/lib/flowdock/git/builder.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true require "grit" require 'cgi' require "securerandom" @@ -31,7 +32,8 @@ module Flowdock private def encode(hash) - return hash unless "".respond_to? :encode + return hash unless "".respond_to?(:encode) + encode_as_utf8(hash) end @@ -46,8 +48,8 @@ module Flowdock encode_as_utf8(val) end elsif obj.is_a?(String) && obj.encoding != Encoding::UTF_8 - if !obj.force_encoding("UTF-8").valid_encoding? - obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, :invalid => :replace, :undef => :replace) + unless obj.force_encoding("UTF-8").valid_encoding? + obj.force_encoding("ISO-8859-1").encode!(Encoding::UTF_8, invalid: :replace, undef: :replace) end end end @@ -78,6 +80,8 @@ module Flowdock # Class used to build Git payload class Builder + include ::Gitlab::Utils::StrongMemoize + def initialize(opts) @repo = opts[:repo] @ref = opts[:ref] @@ -101,7 +105,7 @@ module Flowdock end def ref_name - @ref.to_s.sub(/\Arefs\/(heads|tags)\//, '') + @ref.to_s.sub(%r{\Arefs/(heads|tags)/}, '') end def to_hashes @@ -120,20 +124,15 @@ module Flowdock end def permanent? - @permanent ||= @opts[:permanent_refs].select do |regex| - regex.match(@ref) - end.size > 0 + strong_memoize(:permanent) do + @opts[:permanent_refs].any? { |regex| regex.match(@ref) } + end end def thread_title - action = if permanent? - "updated" - end - type = if @ref.match(%r(^refs/heads/)) - "branch" - else - "tag" - end + action = "updated" if permanent? + type = @ref =~ %r(^refs/heads/) ? "branch" : "tag" + [@opts[:repo_name], type, ref_name, action].compact.join(" ") end -- cgit v1.2.1 From 27e9ed7a4ce2b1d97b7b8093ecde02f7c591349b Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 17:31:32 +0100 Subject: Merge flowdock monkeypatch into the inlined gem --- app/models/project_services/flowdock_service.rb | 48 ------------------ lib/flowdock/git.rb | 67 +++++++------------------ lib/flowdock/git/builder.rb | 9 ++-- 3 files changed, 23 insertions(+), 101 deletions(-) diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 365792f03c3..76624263aab 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -1,53 +1,5 @@ # frozen_string_literal: true -require 'flowdock/git' - -# Flow dock depends on Grit to compute the number of commits between two given -# commits. To make this depend on Gitaly, a monkey patch is applied -module Flowdock - class Git - # pass down a Repository all the way down - def repo - @options[:repo] - end - - def config - {} - end - - def messages - Git::Builder.new(repo: repo, - ref: @ref, - before: @from, - after: @to, - commit_url: @commit_url, - branch_url: @branch_url, - diff_url: @diff_url, - repo_url: @repo_url, - repo_name: @repo_name, - permanent_refs: @permanent_refs, - tags: tags - ).to_hashes - end - - class Builder - def commits - @repo.commits_between(@before, @after).map do |commit| - { - url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil, - id: commit.sha, - message: commit.message, - author: { - name: commit.author_name, - email: commit.author_email - } - } - end - end - end - end -end - class FlowdockService < Service prop_accessor :token validates :token, presence: true, if: :activated? diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb index d5e97dad3cf..a1ecbe08884 100644 --- a/lib/flowdock/git.rb +++ b/lib/flowdock/git.rb @@ -1,39 +1,36 @@ # frozen_string_literal: true -require "multi_json" -require "cgi" -require "flowdock" -require "flowdock/git/builder" +require 'multi_json' +require 'cgi' +require 'flowdock' +require 'flowdock/git/builder' module Flowdock class Git TokenError = Class.new(StandardError) + DEFAULT_PERMANENT_REFS = [ + Regexp.new('refs/heads/master') + ].freeze + class << self def post(ref, from, to, options = {}) Git.new(ref, from, to, options).post end - - def background_post(ref, from, to, options = {}) - Git.new(ref, from, to, options).background_post - end end def initialize(ref, from, to, options = {}) + raise TokenError.new("Flowdock API token not found") unless options[:token] + @ref = ref @from = from @to = to @options = options - @token = options[:token] || config["flowdock.token"] || raise(TokenError.new("Flowdock API token not found")) - @commit_url = options[:commit_url] || config["flowdock.commit-url-pattern"] || nil - @diff_url = options[:diff_url] || config["flowdock.diff-url-pattern"] || nil - @repo_url = options[:repo_url] || config["flowdock.repository-url"] || nil - @repo_name = options[:repo_name] || config["flowdock.repository-name"] || nil - - refs = options[:permanent_refs] || config["flowdock.permanent-references"] || "refs/heads/master" - @permanent_refs = refs - .split(",") - .map(&:strip) - .map {|exp| Regexp.new(exp) } + @token = options[:token] + @commit_url = options[:commit_url] + @diff_url = options[:diff_url] + @repo_url = options[:repo_url] + @repo_name = options[:repo_name] + @permanent_refs = options.fetch(:permanent_refs, DEFAULT_PERMANENT_REFS) end # Send git push notification to Flowdock @@ -43,29 +40,14 @@ module Flowdock end end - # Create and post notification in background process. Avoid blocking the push notification. - def background_post - pid = Process.fork - if pid.nil? - Grit::Git.with_timeout(600) do - post - end - else - Process.detach(pid) # Parent - end - end - def repo - @repo ||= Grit::Repo.new( - @options[:repo] || Dir.pwd, - is_bare: @options[:is_bare] || false - ) + @options[:repo] end private def messages - Git::Builder.new(repo: @repo, + Git::Builder.new(repo: repo, ref: @ref, before: @from, after: @to, @@ -81,18 +63,7 @@ module Flowdock # Flowdock tags attached to the push notification def tags - tags = - if @options[:tags] - @options[:tags] - else - config["flowdock.tags"].to_s.split(",").map(&:strip) - end - - tags.map { |t| CGI.escape(t) } - end - - def config - @config ||= Grit::Config.new(repo) + Array(@options[:tags]).map { |tag| CGI.escape(tag) } end end end diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb index 3cb086f65b8..1ef9b66e747 100644 --- a/lib/flowdock/git/builder.rb +++ b/lib/flowdock/git/builder.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true -require "grit" require 'cgi' -require "securerandom" +require 'securerandom' module Flowdock class Git @@ -93,12 +92,12 @@ module Flowdock def commits @repo.commits_between(@before, @after).map do |commit| { - url: if @opts[:commit_url] then @opts[:commit_url] % [commit.sha] end, + url: @opts[:commit_url] ? @opts[:commit_url] % [commit.sha] : nil, id: commit.sha, message: commit.message, author: { - name: commit.author.name, - email: commit.author.email + name: commit.author_name, + email: commit.author_email } } end -- cgit v1.2.1 From c7be9f5cacadf196754159515a20d4ec31cff5f1 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 18:00:59 +0100 Subject: Remove a dependency on gitlab-gollum-lib Removing this dependency also allows us to remove a transitive dependency on gitlab_grit - which is the whole point of this exercise. I don't think we can EOL gitlab_grit until it's removed as a dependency from gitaly-ruby, but this at least gets it out of gitlab-ce. --- Gemfile | 4 ---- Gemfile.lock | 18 ------------------ Gemfile.rails5.lock | 18 ------------------ lib/gitlab/git/wiki.rb | 47 +++++++++++++++++++++++++++++++++-------------- 4 files changed, 33 insertions(+), 54 deletions(-) diff --git a/Gemfile b/Gemfile index 590ed17508e..c8f65701609 100644 --- a/Gemfile +++ b/Gemfile @@ -79,10 +79,6 @@ gem 'gpgme' gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap' gem 'net-ldap' -# Git Wiki -# Only used to compute wiki page slugs -gem 'gitlab-gollum-lib', '~> 4.2', require: false - # API gem 'grape', '~> 1.1' gem 'grape-entity', '~> 0.7.1' diff --git a/Gemfile.lock b/Gemfile.lock index e0c098c3d8d..ccddf288f01 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -278,19 +278,6 @@ GEM google-protobuf (~> 3.1) grpc (~> 1.10) github-markup (1.7.0) - gitlab-gollum-lib (4.2.7.5) - gemojione (~> 3.2) - github-markup (~> 1.6) - gollum-grit_adapter (~> 1.0) - nokogiri (>= 1.6.1, < 2.0) - rouge (~> 3.1) - sanitize (~> 4.6.4) - stringex (~> 2.6) - gitlab-grit (2.8.2) - charlock_holmes (~> 0.6) - diff-lcs (~> 1.1) - mime-types (>= 1.16) - posix-spawn (~> 0.3) gitlab-markup (1.6.4) gitlab-sidekiq-fetcher (0.3.0) sidekiq (~> 5) @@ -305,8 +292,6 @@ GEM rubyntlm (~> 0.5) globalid (0.4.1) activesupport (>= 4.2.0) - gollum-grit_adapter (1.0.1) - gitlab-grit (~> 2.7, >= 2.7.1) gon (6.2.0) actionpack (>= 3.0) multi_json @@ -596,7 +581,6 @@ GEM pg (0.18.4) po_to_json (1.0.1) json (>= 1.6.0) - posix-spawn (0.3.13) powerpack (0.1.1) premailer (1.10.4) addressable @@ -867,7 +851,6 @@ GEM state_machines-activerecord (0.5.1) activerecord (>= 4.1, < 6.0) state_machines-activemodel (>= 0.5.0) - stringex (2.8.4) sys-filesystem (1.1.6) ffi sysexits (1.2.0) @@ -1022,7 +1005,6 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-markup (~> 1.7.0) - gitlab-gollum-lib (~> 4.2) gitlab-markup (~> 1.6.4) gitlab-sidekiq-fetcher gitlab-styles (~> 2.4) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 56db3b4c9de..59dc829cb48 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -281,19 +281,6 @@ GEM google-protobuf (~> 3.1) grpc (~> 1.10) github-markup (1.7.0) - gitlab-gollum-lib (4.2.7.5) - gemojione (~> 3.2) - github-markup (~> 1.6) - gollum-grit_adapter (~> 1.0) - nokogiri (>= 1.6.1, < 2.0) - rouge (~> 3.1) - sanitize (~> 4.6.4) - stringex (~> 2.6) - gitlab-grit (2.8.2) - charlock_holmes (~> 0.6) - diff-lcs (~> 1.1) - mime-types (>= 1.16) - posix-spawn (~> 0.3) gitlab-markup (1.6.4) gitlab-sidekiq-fetcher (0.3.0) sidekiq (~> 5) @@ -308,8 +295,6 @@ GEM rubyntlm (~> 0.5) globalid (0.4.1) activesupport (>= 4.2.0) - gollum-grit_adapter (1.0.1) - gitlab-grit (~> 2.7, >= 2.7.1) gon (6.2.0) actionpack (>= 3.0) multi_json @@ -600,7 +585,6 @@ GEM pg (0.18.4) po_to_json (1.0.1) json (>= 1.6.0) - posix-spawn (0.3.13) powerpack (0.1.1) premailer (1.10.4) addressable @@ -875,7 +859,6 @@ GEM state_machines-activerecord (0.5.1) activerecord (>= 4.1, < 6.0) state_machines-activemodel (>= 0.5.0) - stringex (2.8.4) sys-filesystem (1.1.6) ffi sysexits (1.2.0) @@ -1031,7 +1014,6 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-markup (~> 1.7.0) - gitlab-gollum-lib (~> 4.2) gitlab-markup (~> 1.6.4) gitlab-sidekiq-fetcher gitlab-styles (~> 2.4) diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 072019dfb0a..563712c5d63 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -1,8 +1,3 @@ -# We only need Gollum::Page so let's not load all of gollum-lib. -require 'gollum-lib/pagination' -require 'gollum-lib/wiki' -require 'gollum-lib/page' - module Gitlab module Git class Wiki @@ -18,6 +13,38 @@ module Gitlab end PageBlob = Struct.new(:name) + # GollumSlug inlines just enough knowledge from Gollum::Page to generate a + # slug, which is used when previewing pages that haven't been persisted + class GollumSlug + class << self + def format_to_ext(format) + format == :markdown ? 'md' : format.to_s + end + + def cname(name, char_white_sub = '-', char_other_sub = '-') + if name.respond_to?(:gsub) + name.gsub(/\s/, char_white_sub).gsub(/[<>+]/, char_other_sub) + else + '' + end + end + + def generate(title, format) + name = cname(title) + '.' + format_to_ext(format) + blob = PageBlob.new(name) + + path = + if blob.name.include?('/') + blob.name.sub(%r{/[^/]+$}, '/') + else + '' + end + + path + cname(name) + end + end + end + attr_reader :repository def self.default_ref @@ -90,15 +117,7 @@ module Gitlab end def preview_slug(title, format) - # Adapted from gollum gem (Gollum::Wiki#preview_page) to avoid - # using Rugged through a Gollum::Wiki instance - page_class = Gollum::Page - page = page_class.new(nil) - ext = page_class.format_to_ext(format.to_sym) - name = page_class.cname(title) + '.' + ext - blob = PageBlob.new(name) - page.populate(blob) - page.url_path + GollumSlug.generate(title, format) end def page_formatted_data(title:, dir: nil, version: nil) -- cgit v1.2.1 From 0621ee38b501b2f2a1f7717aff249ebf1bcebbf8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 18:04:49 +0100 Subject: Remove explicit requires from flowdock --- lib/flowdock/git.rb | 2 -- lib/flowdock/git/builder.rb | 3 --- 2 files changed, 5 deletions(-) diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb index a1ecbe08884..f165ecfc1fa 100644 --- a/lib/flowdock/git.rb +++ b/lib/flowdock/git.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'multi_json' -require 'cgi' require 'flowdock' require 'flowdock/git/builder' diff --git a/lib/flowdock/git/builder.rb b/lib/flowdock/git/builder.rb index 1ef9b66e747..6f4428d1f42 100644 --- a/lib/flowdock/git/builder.rb +++ b/lib/flowdock/git/builder.rb @@ -1,7 +1,4 @@ # frozen_string_literal: true -require 'cgi' -require 'securerandom' - module Flowdock class Git class Commit -- cgit v1.2.1 From c5bff77ea4a6170a3dc1966254feac0ca1836eaa Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Mon, 15 Oct 2018 18:34:55 +0100 Subject: Remove a dependency on gitlab-gollum-lib Inlining this code allows us to remove a dependency on gitlab_grit in gitlab-ce. We can't stop maintaining gitlab_grit yet, since gitaly-ruby still depends on this gem, but it moves us a step closer. --- lib/gitlab/git/wiki.rb | 24 +++++++++++--------- spec/lib/gitlab/git/wiki_spec.rb | 47 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 563712c5d63..7fe56979d5c 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -11,16 +11,11 @@ module Gitlab { user_id: user_id, username: username, name: name, email: email, message: message } end end - PageBlob = Struct.new(:name) # GollumSlug inlines just enough knowledge from Gollum::Page to generate a # slug, which is used when previewing pages that haven't been persisted class GollumSlug class << self - def format_to_ext(format) - format == :markdown ? 'md' : format.to_s - end - def cname(name, char_white_sub = '-', char_other_sub = '-') if name.respond_to?(:gsub) name.gsub(/\s/, char_white_sub).gsub(/[<>+]/, char_other_sub) @@ -29,18 +24,27 @@ module Gitlab end end + def format_to_ext(format) + format == :markdown ? "md" : format.to_s + end + + def canonicalize_filename(filename) + ::File.basename(filename, ::File.extname(filename)).tr('-', ' ') + end + def generate(title, format) - name = cname(title) + '.' + format_to_ext(format) - blob = PageBlob.new(name) + ext = format_to_ext(format.to_sym) + name = cname(title) + '.' + ext + canonical_name = canonicalize_filename(name) path = - if blob.name.include?('/') - blob.name.sub(%r{/[^/]+$}, '/') + if name.include?('/') + name.sub(%r{/[^/]+$}, '/') else '' end - path + cname(name) + path + cname(canonical_name, '-', '-') end end end diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb index c5666e4ec61..ded5d7576df 100644 --- a/spec/lib/gitlab/git/wiki_spec.rb +++ b/spec/lib/gitlab/git/wiki_spec.rb @@ -1,10 +1,13 @@ require 'spec_helper' describe Gitlab::Git::Wiki do + using RSpec::Parameterized::TableSyntax + let(:project) { create(:project) } let(:user) { project.owner } let(:project_wiki) { ProjectWiki.new(project, user) } - subject { project_wiki.wiki } + + subject(:wiki) { project_wiki.wiki } describe '#pages' do before do @@ -64,8 +67,44 @@ describe Gitlab::Git::Wiki do end end - def create_page(name, content) - subject.write_page(name, :markdown, content, commit_details(name)) + describe '#preview_slug' do + where(:title, :format, :expected_slug) do + 'The Best Thing' | :markdown | 'The-Best-Thing' + 'The Best Thing' | :md | 'The-Best-Thing' + 'The Best Thing' | :txt | 'The-Best-Thing' + 'A Subject/Title Here' | :txt | 'A-Subject/Title-Here' + 'A subject' | :txt | 'A-subject' + 'A 1/B 2/C 3' | :txt | 'A-1/B-2/C-3' + 'subject/title' | :txt | 'subject/title' + 'subject/title.md' | :txt | 'subject/title.md' + 'foo+baz' | :txt | 'foo-bar--baz' + 'foo%2Fbar' | :txt | 'foo%2Fbar' + '' | :markdown | '.md' + '' | :md | '.md' + '' | :txt | '.txt' + end + + with_them do + subject { wiki.preview_slug(title, format) } + + let(:gitaly_slug) { wiki.pages.first } + + it { is_expected.to eq(expected_slug) } + + it 'matches the slug generated by gitaly' do + skip('Gitaly cannot generate a slug for an empty title') unless title.present? + + create_page(title, 'content', format: format) + + gitaly_slug = wiki.pages.first.url_path + + is_expected.to eq(gitaly_slug) + end + end + end + + def create_page(name, content, format: :markdown) + wiki.write_page(name, format, content, commit_details(name)) end def commit_details(name) @@ -73,7 +112,7 @@ describe Gitlab::Git::Wiki do end def destroy_page(title, dir = '') - page = subject.page(title: title, dir: dir) + page = wiki.page(title: title, dir: dir) project_wiki.delete_page(page, "test commit") end end -- cgit v1.2.1