├── Gemfile ├── script ├── bootstrap └── cibuild ├── .gitignore ├── test ├── fixtures │ ├── non-mentioned.md │ ├── index.md │ ├── test.json │ ├── _docs │ │ └── file.md │ ├── parkr.txt │ ├── mentioned-markdown.md │ └── leave-liquid-alone.md ├── helper.rb ├── test_jekyll_commit_mentions.rb └── test_commit_mention_filter.rb ├── jekyll-issue-mentions-0.1.1.gem ├── History.markdown ├── Rakefile ├── .travis.yml ├── jekyll-commit-mentions.gemspec ├── LICENSE ├── README.md └── lib ├── jekyll-commit-mentions.rb └── commit_mention_filter.rb /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /script/bootstrap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | bundle install -j8 4 | -------------------------------------------------------------------------------- /script/cibuild: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | bundle exec rake test 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/gems 2 | .bundle 3 | bin 4 | /*.gem 5 | Gemfile.lock 6 | *.swp 7 | -------------------------------------------------------------------------------- /test/fixtures/non-mentioned.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: don't mention me bro 3 | --- 4 | 5 | 1234 1234 1234 6 | > 1234 7 | -------------------------------------------------------------------------------- /test/fixtures/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: I'm a page 3 | --- 4 | 5 | 1234 665d43f96ef1018e66d294ccc433fefadd236090 1234 6 | -------------------------------------------------------------------------------- /test/fixtures/test.json: -------------------------------------------------------------------------------- 1 | --- 2 | title: I'm a page 3 | --- 4 | 5 | 665d43f96ef1018e66d294ccc433fefadd236090 1234 1234 6 | -------------------------------------------------------------------------------- /test/fixtures/_docs/file.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A document 3 | --- 4 | 5 | 1234 665d43f96ef1018e66d294ccc433fefadd236090 1234 6 | -------------------------------------------------------------------------------- /jekyll-issue-mentions-0.1.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workato/jekyll-commit-mentions/master/jekyll-issue-mentions-0.1.1.gem -------------------------------------------------------------------------------- /test/fixtures/parkr.txt: -------------------------------------------------------------------------------- 1 | --- 2 | title: Parker Moore 3 | type: author-info 4 | --- 5 | 6 | Parker 665d43f96ef1018e66d294ccc433fefadd236090 Moore 7 | -------------------------------------------------------------------------------- /test/fixtures/mentioned-markdown.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: mention me but don't eff my markdown 3 | --- 4 | 5 | 1234 665d43f96ef1018e66d294ccc433fefadd236090 1234 6 | > 1234 7 | -------------------------------------------------------------------------------- /History.markdown: -------------------------------------------------------------------------------- 1 | ## 0.1.1 / 2015-03-28 2 | 3 | * Added tests. Got the version working with 40 digit commit SHA 4 | 5 | ## 0.1.0 / 2015-03-23 6 | 7 | * First release 8 | -------------------------------------------------------------------------------- /test/fixtures/leave-liquid-alone.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: don't mangle that liquid plz 3 | --- 4 | 5 | 1234 665d43f96ef1018e66d294ccc433fefadd236090 12341234 6 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler' 3 | begin 4 | Bundler.setup(:default, :development, :test) 5 | rescue Bundler::BundlerError => e 6 | $stderr.puts e.message 7 | $stderr.puts "Run `bundle install` to install missing gems" 8 | exit e.status_code 9 | end 10 | require 'bundler/gem_tasks' 11 | require 'rake' 12 | require 'rake/testtask' 13 | 14 | Rake::TestTask.new(:test) do |test| 15 | test.libs << 'lib' << 'test' 16 | test.pattern = 'test/**/test_*.rb' 17 | test.verbose = true 18 | end 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2 4 | - 2.1 5 | - 2.0 6 | - 1.9.3 7 | script: script/cibuild 8 | sudo: false 9 | cache: bundler 10 | env: 11 | global: 12 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true 13 | #notifications: 14 | #irc: 15 | #on_success: change 16 | #on_failure: change 17 | #channels: 18 | #- irc.freenode.org#jekyll 19 | #template: 20 | #- '%{repository}#%{build_number} (%{branch}) %{message} %{build_url}' 21 | #email: 22 | #on_success: never 23 | #on_failure: never 24 | addons: 25 | code_climate: 26 | repo_token: adf08323 27 | -------------------------------------------------------------------------------- /jekyll-commit-mentions.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = "jekyll-commit-mentions" 3 | s.summary = "Github commit sha mention support for your jekyll site" 4 | s.version = "0.1.3" 5 | s.authors = ["harish shetty"] 6 | s.email = "support@workato.com" 7 | 8 | s.homepage = "https://github.com/workato/jekyll-commit-mentions" 9 | s.licenses = ["mit"] 10 | s.files = ["lib/jekyll-commit-mentions.rb", "lib/commit_mention_filter.rb" ] 11 | 12 | s.add_dependency "jekyll", '~> 2.0' 13 | s.add_dependency "html-pipeline", '~> 1.9.0' 14 | s.add_dependency "nokogiri", [">= 1.4", "<= 1.6.5"] 15 | s.add_dependency "github-markdown" 16 | 17 | s.add_development_dependency 'rake' 18 | s.add_development_dependency 'rdoc' 19 | s.add_development_dependency 'shoulda' 20 | s.add_development_dependency 'minitest' 21 | end 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 GitHub, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'minitest/autorun' 3 | require 'shoulda' 4 | 5 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 6 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 7 | require 'jekyll-commit-mentions' 8 | 9 | TEST_DIR = File.expand_path("../", __FILE__) 10 | FIXTURES_DIR = File.expand_path("fixtures", TEST_DIR) 11 | DEST_DIR = File.expand_path("destination", TEST_DIR) 12 | 13 | module CommitMentionsTestHelpers 14 | def fixture_site 15 | Jekyll::Site.new( 16 | Jekyll::Utils.deep_merge_hashes( 17 | Jekyll::Configuration::DEFAULTS, 18 | { 19 | "source" => FIXTURES_DIR, 20 | "destination" => DEST_DIR, 21 | "collections" => { 22 | "docs" => {} 23 | }, 24 | "jekyll-commit-mentions" => { 25 | "base_url" => "https://github.com/usr1/repo1/commit" 26 | } 27 | } 28 | ) 29 | ) 30 | end 31 | 32 | def page_with_name(site, name) 33 | site.pages.find { |p| p.name == name } 34 | end 35 | 36 | def document(doc_filename) 37 | @site.collections["docs"].docs.find { |d| d.relative_path.match(doc_filename) } 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jekyll Commit Mentions 2 | 3 | Github commit sha mention support for your Jekyll site 4 | 5 | [![Gem Version](https://badge.fury.io/rb/jekyll-commit-mentions.png)](http://badge.fury.io/rb/jekyll-commit-mentions) 6 | [![Build Status](https://travis-ci.org/workato/jekyll-commit-mentions.svg?branch=master)](https://travis-ci.org/workato/jekyll-commit-mentions) 7 | 8 | ## Usage 9 | 10 | Add the following to your site's `Gemfile` 11 | 12 | ``` 13 | gem 'jekyll-commit-mentions' 14 | ``` 15 | 16 | And add the following to your site's `_config.yml` 17 | 18 | ```yml 19 | gems: 20 | - jekyll-commit-mentions 21 | ``` 22 | 23 | In any page or post, use commit SHA id as you would normally, e.g. 24 | 25 | ```markdown 26 | Refer to this commit a5c3785ed8d6a35868bc169f07e40e889087fd2e for more 27 | details 28 | ``` 29 | 30 | This will be rendered as 31 | 32 | > Refer to this commit [087fd2e](https://github.com/workato/jekyll-commit-mentions/commit/a5c3785ed8d6a35868bc169f07e40e889087fd2e) for more details 33 | 34 | ## Configuration 35 | 36 | Set the Github repo url: 37 | 38 | ```yaml 39 | jekyll-commit-mentions: 40 | base_url: https://github.com/workato/jekyll-commit-mentions/commit 41 | ``` 42 | 43 | Or, you can use this shorthand: 44 | 45 | ```yaml 46 | jekyll-commit-mentions: https://github.com/workato/jekyll-commit-mentions/commit 47 | ``` 48 | -------------------------------------------------------------------------------- /lib/jekyll-commit-mentions.rb: -------------------------------------------------------------------------------- 1 | require 'jekyll' 2 | require 'html/pipeline' 3 | require 'commit_mention_filter' 4 | 5 | module Jekyll 6 | class CommitMentions < Jekyll::Generator 7 | safe true 8 | attr_reader :base_url 9 | 10 | def initialize(config = Hash.new) 11 | validate_config!(config) 12 | end 13 | 14 | def generate(site) 15 | site.pages.each { |page| mentionify page if html_page?(page) } 16 | site.posts.each { |post| mentionify post } 17 | site.docs_to_write.each { |doc| mentionify doc } 18 | end 19 | 20 | def mentionify(page) 21 | @filter = HTML::Pipeline::CommitMentionFilter.new(page.content, {:base_url => base_url}) 22 | page.content = @filter.call.to_s. 23 | gsub(">", ">"). 24 | gsub("<", "<"). 25 | gsub("%7B", "{"). 26 | gsub("%20", " "). 27 | gsub("%7D", "}") 28 | end 29 | 30 | def html_page?(page) 31 | page.html? || page.url.end_with?('/') 32 | end 33 | 34 | private 35 | def validate_config!(configs) 36 | configs = configs['jekyll-commit-mentions'] 37 | base_url = nil 38 | case configs 39 | when String 40 | base_url = configs 41 | when Hash 42 | base_url = configs['base_url'] 43 | end 44 | error_prefix = "jekyll-commit-mentions" 45 | raise ArgumentError.new("#{error_prefix}.base_url is missing/empty") if (base_url.nil? || base_url.empty?) 46 | 47 | @base_url = base_url 48 | end 49 | 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/test_jekyll_commit_mentions.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | class TestJekyllCommitMentions < Minitest::Test 4 | include CommitMentionsTestHelpers 5 | 6 | def commitid 7 | "665d43f96ef1018e66d294ccc433fefadd236090" 8 | end 9 | 10 | def short_commitid 11 | commitid[-7..-1] 12 | end 13 | 14 | def setup 15 | @site = fixture_site 16 | @site.read 17 | @site.config['jekyll-commit-mentions'] = {"base_url" => "https://github.com/usr1/repo1/commit"} 18 | @mentions = Jekyll::CommitMentions.new(@site.config) 19 | @mention = "1234 #{short_commitid} 1234" 20 | end 21 | 22 | def content page 23 | # counter UTF-8 encoding https://groups.google.com/forum/#!msg/nokogiri-talk/Q2Nh1cLeQzk/dNyAwQ3vgQsJ 24 | page.content.gsub(/\n\z/, '') 25 | end 26 | 27 | def base_url(configs) 28 | Jekyll::CommitMentions.new("jekyll-commit-mentions" => configs).base_url 29 | end 30 | 31 | should "replace mention with link" do 32 | page = page_with_name(@site, "index.md") 33 | 34 | @mentions.mentionify page 35 | assert_equal @mention, content(page) 36 | end 37 | 38 | should "replace mention with link in collections" do 39 | page = document("file.md") 40 | 41 | @mentions.mentionify page 42 | assert_equal @mention, content(page) 43 | end 44 | 45 | should "replace page content on generate" do 46 | @mentions.generate(@site) 47 | assert_equal @mention, content(@site.pages.first) 48 | end 49 | 50 | should "not mangle liquid templating" do 51 | page = page_with_name(@site, "leave-liquid-alone.md") 52 | 53 | @mentions.mentionify page 54 | assert_equal "#{@mention}1234", content(page) 55 | end 56 | 57 | should "not mangle markdown" do 58 | page = page_with_name(@site, "mentioned-markdown.md") 59 | 60 | @mentions.mentionify page 61 | assert_equal "#{@mention}\n> 1234", content(page) 62 | end 63 | 64 | should "not mangle non-mentioned content" do 65 | page = page_with_name(@site, "non-mentioned.md") 66 | 67 | @mentions.mentionify page 68 | assert_equal "1234 1234 1234\n> 1234", content(page) 69 | end 70 | 71 | should "not touch non-HTML pages" do 72 | @mentions.generate(@site) 73 | assert_equal "#{commitid} 1234 1234", content(page_with_name(@site, "test.json")) 74 | end 75 | 76 | should "also convert pages with permalinks ending in /" do 77 | page = page_with_name(@site, "parkr.txt") 78 | 79 | @mentions.mentionify page 80 | assert_equal "Parker #{short_commitid} Moore", content(page) 81 | end 82 | 83 | 84 | context "config" do 85 | context "bad config" do 86 | should "should raise exception for invalid values" do 87 | assert_raises(ArgumentError) { base_url(nil) } 88 | assert_raises(ArgumentError) { base_url({}) } 89 | assert_raises(ArgumentError) { base_url(123) } 90 | end 91 | end 92 | 93 | context "base_url" do 94 | should "handle a raw string" do 95 | assert_equal "https://twitter.com", base_url("https://twitter.com") 96 | end 97 | 98 | should "handle a hash config" do 99 | assert_equal "https://twitter.com", base_url({"base_url" => "https://twitter.com"}) 100 | end 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /lib/commit_mention_filter.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module HTML 4 | class Pipeline 5 | # HTML filter that replaces mention mentions with links to Github commit. Mentions within
,
  6 |     # , "
 58 |     assert_equal body, filter(body).to_html
 59 |   end
 60 | 
 61 |   def test_not_replacing_mentions_in_links
 62 |     body = "

#{short_commitid} okay

" 63 | assert_equal body, filter(body).to_html 64 | end 65 | 66 | def test_html_injection 67 | body = "

#{commitid} <script>alert(0)</script>

" 68 | link = "#{short_commitid}" 69 | assert_equal "

#{link} <script>alert(0)</script>

", 70 | filter(body, '/').to_html 71 | end 72 | 73 | def test_base_url_slash 74 | body = "

Hi, #{commitid}!

" 75 | link = "#{short_commitid}" 76 | assert_equal "

Hi, #{link}!

", filter(body, '/').to_html 77 | end 78 | 79 | def test_base_url_under_custom_route 80 | body = "

Hi, #{commitid}!

" 81 | link = "#{short_commitid}" 82 | assert_equal "

Hi, #{link}!

", 83 | filter(body, '/commits').to_html 84 | end 85 | 86 | def test_base_url_slash_with_tilde 87 | body = "

Hi, #{commitid}!

" 88 | link = "#{short_commitid}" 89 | assert_equal "

Hi, #{link}!

", 90 | filter(body, '/~').to_html 91 | end 92 | 93 | MarkdownPipeline = 94 | HTML::Pipeline.new [ 95 | HTML::Pipeline::MarkdownFilter, 96 | HTML::Pipeline::CommitMentionFilter 97 | ] 98 | 99 | def mentioned_commitids 100 | result = {} 101 | MarkdownPipeline.call(@body, {}, result) 102 | result[:mentioned_commitids] 103 | end 104 | 105 | def test_matches_commitids_in_body 106 | @body = "#{commitid} how are you?" 107 | assert_equal [commitid], mentioned_commitids 108 | end 109 | 110 | def test_matches_commitids_followed_by_a_single_dot 111 | @body = "okay #{commitid}." 112 | assert_equal [commitid], mentioned_commitids 113 | end 114 | 115 | def test_matches_commitids_followed_by_multiple_dots 116 | @body = "okay #{commitid}..." 117 | assert_equal [commitid], mentioned_commitids 118 | end 119 | 120 | def test_matches_colon_suffixed_names 121 | @body = "#{commitid}: what do you think?" 122 | assert_equal [commitid], mentioned_commitids 123 | end 124 | 125 | def test_matches_list_of_names 126 | @body = "#{commitid} #{commitid1} #{commitid2}" 127 | assert_equal [commitid, commitid1, commitid2], mentioned_commitids 128 | end 129 | 130 | def test_matches_list_of_names_with_commas 131 | @body = "#{commitid}, #{commitid1}, #{commitid2}" 132 | assert_equal [commitid, commitid1, commitid2], mentioned_commitids 133 | end 134 | 135 | def test_matches_inside_brackets 136 | @body = "(#{commitid}) [#{commitid1}] {#{commitid2}}" 137 | assert_equal [commitid, commitid1, commitid2], mentioned_commitids 138 | end 139 | 140 | def test_returns_distinct_set 141 | @body = "#{commitid} #{commitid1} #{commitid2} #{commitid} #{commitid1} #{commitid2}" 142 | assert_equal [commitid, commitid1, commitid2], mentioned_commitids 143 | end 144 | 145 | def test_does_not_match_inline_code_block_with_multiple_code_blocks 146 | @body = "something\n\n`#{commitid} #{commitid1} #{commitid2}`" 147 | assert_equal [], mentioned_commitids 148 | end 149 | 150 | def test_mention_at_end_of_parenthetical_sentence 151 | @body = "(We're talking 'bout ##{commitid}.)" 152 | assert_equal [commitid], mentioned_commitids 153 | end 154 | 155 | def test_commitid_pattern_can_be_customized 156 | new_commitid = commitid[0...30] 157 | new_short_commitid = new_commitid[-7..-1] 158 | custom_commitid_pattern = /[0-9a-f]{30}/ 159 | 160 | body = "

#{new_commitid}: commit.

" 161 | doc = Nokogiri::HTML::DocumentFragment.parse(body) 162 | res = filter(doc, '/', custom_commitid_pattern) 163 | 164 | link = "#{new_short_commitid}" 165 | assert_equal "

#{link}: commit.

", res.to_html 166 | end 167 | 168 | def test_filter_does_not_create_a_new_object_for_default_commitid_pattern 169 | body = "
#{commitid}
" 170 | doc = Nokogiri::HTML::DocumentFragment.parse(body) 171 | 172 | filter(doc.clone, '/', nil) 173 | pattern_count = HTML::Pipeline::CommitMentionFilter::MentionPatterns.length 174 | filter(doc.clone, '/', nil) 175 | 176 | assert_equal pattern_count, HTML::Pipeline::CommitMentionFilter::MentionPatterns.length 177 | filter(doc.clone, '/', /#{commitid}/) 178 | assert_equal pattern_count + 1, HTML::Pipeline::CommitMentionFilter::MentionPatterns.length 179 | end 180 | end 181 | --------------------------------------------------------------------------------