FEATURE: automatically link previous commit if hash is found

FEATURE: automatically link previous commit if hash is found

This searching applies to both title and subject, we will auto link subject

This searching applies to both title and subject, we will auto link subject


diff --git a/assets/javascripts/discourse/initializers/init-code-review.js.es6 b/assets/javascripts/discourse/initializers/init-code-review.js.es6
index 673805b..216dbee 100644
--- a/assets/javascripts/discourse/initializers/init-code-review.js.es6
+++ b/assets/javascripts/discourse/initializers/init-code-review.js.es6
@@ -21,6 +21,7 @@ function actOnCommit(topic, action) {
 function initialize(api) {
   api.addPostSmallActionIcon("followup", "far-clock");
   api.addPostSmallActionIcon("approved", "thumbs-up");
+  api.addPostSmallActionIcon("followed_up", "link");
 
   // we need to allow unconditional association even with 2fa
   // core hides this section if 2fa is on for a user
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 2f823bc..fcf3e31 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -3,6 +3,7 @@ en:
     action_codes:
       approved: "Approved"
       followup: "Follow Up"
+      followed_up: "Followed Up"
     code_review:
       approve:
         title: "Approve commit"
diff --git a/db/migrate/20181213080000_create_custom_field_indexes.rb b/db/migrate/20181213080000_create_custom_field_indexes.rb
new file mode 100644
index 0000000..bb8427a
--- /dev/null
+++ b/db/migrate/20181213080000_create_custom_field_indexes.rb
@@ -0,0 +1,5 @@
+class CreateCustomFieldIndexes < ActiveRecord::Migration[5.2]
+  def change
+    add_index :topic_custom_fields, [:value], unique: true, where: "name = 'commit hash'"
+  end
+end
diff --git a/lib/discourse_code_review/importer.rb b/lib/discourse_code_review/importer.rb
index 93cd7a6..70e73d9 100644
--- a/lib/discourse_code_review/importer.rb
+++ b/lib/discourse_code_review/importer.rb
@@ -33,6 +33,45 @@ module DiscourseCodeReview
       end
     end
 
+    def auto_link_commits(text)
+      linked_commits = find_linked_commits(text)
+      if (linked_commits.length > 0)
+        linked_commits.each do |hash, topic|
+          # this is the ultra naive implementation
+          # the ultra correct one here is to convert to HTML, modify HTML
+          # convert back to Markdown, lets see what milege this gives
+          text.gsub!(hash, "[#{hash}](#{topic.url})")
+        end
+      end
+      [text, linked_commits]
+    end
+
+    def find_linked_commits(text)
+      result = {}
+
+      shas = text.scan(/(?:\s|^)([a-f0-9]{8,})(?:\s|$)/).flatten
+      if shas.length > 0
+
+        like_clause = shas.map { |sha| "f.value LIKE '#{sha}%'" }.join(' OR ')
+
+        topics = Topic.select("topics.*, value AS sha")
+          .joins("JOIN topic_custom_fields f ON topics.id = topic_id AND f.name = '#{DiscourseCodeReview::CommitHash}'")
+          .where(like_clause)
+
+        topics.each do |topic|
+
+          lookup_shas = shas.select { |sha| topic.sha.start_with? sha }
+
+          lookup_shas.each do |sha|
+            result[sha] = topic
+          end
+
+        end
+      end
+
+      result
+    end
+
     def import_commit(commit)
       link = <<~LINK
         [<small>GitHub</small>](https://github.com/#{github_repo.name}/commit/#{commit[:hash]})
@@ -47,7 +86,10 @@ module DiscourseCodeReview
           "\n[... diff too long, it was truncated ...]\n"
         end
 
-      raw = "<div class='excerpt'>\n#{commit[:body]}\n</div>\n\n`‍``diff\n#{diff}\n#{truncated_message}`‍``\n#{link}"
+      body, linked_topics = auto_link_commits(commit[:body])
+      linked_topics.merge! find_linked_commits(title)
+
+      raw = "<div class='excerpt'>\n#{body}\n</div>\n\n`‍``diff\n#{diff}\n#{truncated_message}`‍``\n#{link}"
 
       user = ensure_user(
         email: commit[:email],
@@ -76,6 +118,16 @@ module DiscourseCodeReview
 
         github_repo.last_commit = commit[:hash]
 
+        linked_topics.values.each do |topic|
+          topic.add_moderator_post(
+            user,
+            post.topic.url,
+            bump: false,
+            post_type: Post.types[:small_action],
+            action_code: "followed_up"
+          )
+        end
+
         post
       end
     end
diff --git a/spec/discourse_code_review/lib/importer_spec.rb b/spec/discourse_code_review/lib/importer_spec.rb
index f76cbb1..1bd2ed3 100644
--- a/spec/discourse_code_review/lib/importer_spec.rb
+++ b/spec/discourse_code_review/lib/importer_spec.rb
@@ -14,6 +14,35 @@ module DiscourseCodeReview
       expect(Importer.new(repo).category_id).to eq(id)
     end
 
+    it "can cleanly associate old commits" do
+      repo = GithubRepo.new("discourse/discourse", Octokit::Client.new)
+
+      diff = "`‍``\nwith a diff"
+
+      commit = {
+        subject: "hello world",
+        body: "this is the body",
+        email: "sam@sam.com",
+        github_login: "sam",
+        github_id: "111",
+        date: 1.day.ago,
+        diff: diff,
+        hash: "a1db15feadc7951d8a2b4ae63384babd6c568ae0"
+      }
+
+      post = Importer.new(repo).import_commit(commit)
+
+      commit[:hash] = "dbbadb5c357bc23daf1fa732f8670e55dc28b7cb"
+      commit[:body] = "ab2787347ff this is\nfollowing up on a1db15fe"
+      post2 = Importer.new(repo).import_commit(commit)
+
+      expect(post2.cooked).to include(post.topic.url)
+
+      # expect a backlink
+      expect(post.topic.posts.length).to eq(2)
+
+    end
+
     it "can escape diff `‍``" do
 
       repo = GithubRepo.new("discourse/discourse", Octokit::Client.new)

GitHub

4 Likes

There’s a shorthand in Ruby for “[0-9a-f]” which is “\h:wink:

1 Like

Cool does that include A-F as well (the upper case )

Yes it does :wink:

> "abcdefABCDEF0123456789"[/\h+/]
 => "abcdefABCDEF0123456789" 

From the Ruby doc

  • /\h/ - A hexdigit character ( [0-9a-fA-F] )

Aha yeah I don’t want that, kind of like this only to catch lower case :blush:

Aren’t SHAs case insensitive?

Technically yes, but people tend to follow the practice lower case so this is a better habit to enforce