gitlab-gollum-rugged_adapter-0.4.4.2/0000755000004100000410000000000013447442616017434 5ustar www-datawww-datagitlab-gollum-rugged_adapter-0.4.4.2/README.md0000644000004100000410000000414013447442616020712 0ustar www-datawww-data[![Gem Version](https://badge.fury.io/rb/gollum-rugged_adapter.svg)](http://badge.fury.io/rb/gollum-rugged_adapter) [![Build Status](https://travis-ci.org/gollum/rugged_adapter.svg?branch=master)](https://travis-ci.org/gollum/rugged_adapter) [![Dependency Status](https://gemnasium.com/gollum/rugged_adapter.svg)](https://gemnasium.com/gollum/rugged_adapter) ## DESCRIPTION Adapter for [gollum](https://github.com/gollum/gollum) to use [Rugged](https://github.com/libgit2/rugged) (libgit2) at the backend. See the [gollum wiki](https://github.com/gollum/gollum/wiki/Git-adapters) for more information on adapters. Currently gollum uses grit as a backend by default, but since that is abandonware, the plan is to make this adapter the default in the future. **Please note that this adapter is currently in beta. It passes the unit tests for gollum and [gollum-lib](https://github.com/gollum/gollum-lib), but it needs more comprehensive testing. Please [report any issues](https://github.com/gollum/rugged_adapter/issues) that you encounter.** ## USAGE Install the gem: ```bash gem install --pre gollum-rugged_adapter # --pre required for beta-releases ``` Now run gollum as follows: ```bash gollum --adapter rugged ``` ## CONTRIBUTING 1. Start by cloning the repo [on GitHub](http://github.com/gollum/rugged_adapter). 2. From inside the repo's directory, install the (development) dependencies with `bundle install` 3. Create a thoughtfully named topic branch to contain your changes. 4. Hack away. 5. Make sure your changes pass the adapter's specs: `bundle exec rake` 6. Make sure your changes pass gollum-lib's tests * Clone [gollum-lib](https://github.com/gollum/gollum-lib) and add your local version of the rugged adapter to the gollum-lib `Gemfile`: `gem "gollum-rugged_adapter", :path => '/path/to/rugged_adapter'` * `bundle install` * `bundle exec rake GIT_ADAPTER=rugged` 1. If necessary, rebase your commits into logical chunks, without errors. 1. Push the branch up to GitHub. 1. Send a pull request to the gollum/rugged_adapter project. ## RELEASING This gem uses [Semantic Versioning](http://semver.org/). gitlab-gollum-rugged_adapter-0.4.4.2/LICENSE0000644000004100000410000000207513447442616020445 0ustar www-datawww-data(The MIT License) Copyright (c) Bart Kamphorst, Dawa Ometto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gitlab-gollum-rugged_adapter-0.4.4.2/rugged_adapter.gemspec0000644000004100000410000000211513447442616023755 0ustar www-datawww-data# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "rugged_adapter/version" Gem::Specification.new do |s| s.name = "gitlab-gollum-rugged_adapter" s.version = Gollum::Lib::Git::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Bart Kamphorst, Dawa Ometto"] s.email = ["fjlopez@gitlab.com"] s.homepage = "https://gitlab.com/gitlab-org/gitlab-gollum-rugged_adapter" s.summary = %q{Adapter for Gollum to use Rugged (libgit2) at the backend.} s.description = %q{Adapter for Gollum to use Rugged (libgit2) at the backend.} s.license = "MIT" s.add_runtime_dependency 'rugged', '~> 0.25' s.add_runtime_dependency 'mime-types', '>= 1.15' s.add_development_dependency "rspec", "3.4.0" s.files = Dir['lib/**/*.rb'] + ["README.md", "Gemfile"] s.require_paths = ["lib"] # = MANIFEST = s.files = %w( Gemfile LICENSE README.md Rakefile lib/rugged_adapter.rb lib/rugged_adapter/git_layer_rugged.rb lib/rugged_adapter/version.rb rugged_adapter.gemspec ) # = MANIFEST = end gitlab-gollum-rugged_adapter-0.4.4.2/Rakefile0000644000004100000410000000567713447442616021120 0ustar www-datawww-datarequire 'rubygems' task :default => :rspec require 'rspec/core/rake_task' def name "rugged_adapter" end def version line = File.read("lib/rugged_adapter/version.rb")[/^\s*VERSION\s*=\s*.*/] line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1] end # assumes x.y.z all digit version def next_version # x.y.z v = version.split '.' # bump z v[-1] = v[-1].to_i + 1 v.join '.' end def bump_version old_file = File.read("lib/#{name}/version.rb") old_version_line = old_file[/^\s*VERSION\s*=\s*.*/] new_version = next_version # replace first match of old version with new version old_file.sub!(old_version_line, " VERSION = '#{new_version}'") File.write("lib/#{name}/version.rb", old_file) new_version end def date Date.today.to_s end def gemspec "#{name}.gemspec" end def gem_file "gollum-#{name}-#{version}.gem" end def replace_header(head, header_name) head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"} end desc "Run specs." RSpec::Core::RakeTask.new(:rspec) do |spec| ruby_opts = "-w" spec.pattern = 'spec/**/*_spec.rb' spec.rspec_opts = ['--backtrace --color'] end desc "Update version number and gemspec" task :bump do puts "Updated version to #{bump_version}" # Execute does not invoke dependencies. # Manually invoke gemspec then validate. Rake::Task[:gemspec].execute Rake::Task[:validate].execute end desc 'Create a release build' task :release => :build do unless `git branch` =~ /^\* master$/ puts "You must be on the master branch to release!" exit! end sh "git commit --allow-empty -a -m 'Release #{version}'" sh "git pull --rebase origin master" sh "git tag v#{version}" sh "git push origin master" sh "git push origin v#{version}" sh "gem push pkg/#{gem_file}" end desc 'Publish to rubygems. Same as release' task :publish => :release desc 'Build gem' task :build => :gemspec do sh "mkdir -p pkg" sh "gem build #{gemspec}" sh "mv #{gem_file} pkg" end desc 'Update gemspec' task :gemspec => :validate do # read spec file and split out manifest section spec = File.read(gemspec) head, manifest, tail = spec.split(" # = MANIFEST =\n") # replace name version and date replace_header(head, :name) replace_header(head, :date) # determine file list from git ls-files files = `git ls-files`. split("\n"). sort. reject { |file| file =~ /^\./ }. reject { |file| file =~ /^(rdoc|pkg|spec|\.gitattributes|Guardfile)/ }. map { |file| " #{file}" }. join("\n") # piece file back together and write manifest = " s.files = %w(\n#{files}\n )\n" spec = [head, manifest, tail].join(" # = MANIFEST =\n") File.open(gemspec, 'w') { |io| io.write(spec) } puts "Updated #{gemspec}" end desc 'Validate lib files and version file' task :validate do libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"] unless libfiles.empty? puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir." exit! end endgitlab-gollum-rugged_adapter-0.4.4.2/lib/0000755000004100000410000000000013447442616020202 5ustar www-datawww-datagitlab-gollum-rugged_adapter-0.4.4.2/lib/rugged_adapter.rb0000644000004100000410000000005513447442616023504 0ustar www-datawww-datarequire 'rugged_adapter/git_layer_rugged.rb' gitlab-gollum-rugged_adapter-0.4.4.2/lib/rugged_adapter/0000755000004100000410000000000013447442616023157 5ustar www-datawww-datagitlab-gollum-rugged_adapter-0.4.4.2/lib/rugged_adapter/version.rb0000644000004100000410000000012413447442616025166 0ustar www-datawww-datamodule Gollum module Lib module Git VERSION = '0.4.4.2' end end end gitlab-gollum-rugged_adapter-0.4.4.2/lib/rugged_adapter/git_layer_rugged.rb0000644000004100000410000004606713447442616027035 0ustar www-datawww-data# ~*~ encoding: utf-8 ~*~ require 'rugged' require 'ostruct' require 'mime-types' module Gollum def self.set_git_timeout(time) end def self.set_git_max_filesize(size) end module Git DEFAULT_MIME_TYPE = "text/plain" class NoSuchShaFound < StandardError; end class Actor attr_accessor :name, :email def self.default_actor self.new("Gollum", "Gollum@wiki") end def initialize(name, email) @name = name @email = email end def output(time) # implementation from grit offset = time.utc_offset / 60 "%s <%s> %d %+.2d%.2d" % [ @name, @email || "null", time.to_i, offset / 60, offset.abs % 60] end def to_h {:name => @name, :email => @email} end end class Blob attr_reader :mode attr_reader :name attr_reader :id def self.create(repo, options) blob = repo.git.lookup(options[:id]) self.new(blob, options) end def initialize(blob, options = {}) @blob = blob @mode = options[:mode] @name = options[:name] @size = options[:size] @id = blob.oid end def data @content ||= @blob.content end def is_symlink @mode == 0120000 end def mime_type guesses = MIME::Types.type_for(self.name) rescue [] guesses.first ? guesses.first.simplified : DEFAULT_MIME_TYPE end def size @size || @blob.size end def symlink_target(base_path = nil) target = data new_path = ::File.expand_path(::File.join('..', target), base_path) return new_path if ::File.file? new_path nil end end class Commit def initialize(commit) @commit = commit end def id @commit.oid end alias_method :sha, :id alias_method :to_s, :id attr_reader :commit def author @author ||= Gollum::Git::Actor.new(@commit.author[:name], @commit.author[:email]) end def authored_date @commit.author[:time] end def message @commit.message end def tree Gollum::Git::Tree.new(@commit.tree) end def stats @stats ||= build_stats end private def build_stats additions = 0 deletions = 0 total = 0 files = [] parent = @commit.parents.first diff = Rugged::Tree.diff(@commit.tree.repo, parent ? parent.tree : nil, @commit.tree) diff = diff.each_patch do |patch| new_additions = patch.stat[1] new_deletions = patch.stat[0] additions += new_additions deletions += new_deletions total += patch.changes files << [patch.delta.new_file[:path].force_encoding("UTF-8"), new_deletions, new_additions, patch.changes] # Rugged seems to generate the stat diffs in the other direciton than grit does by default, so switch the order of additions and deletions. end OpenStruct.new(:additions => additions, :deletions => deletions, :files => files, :id => id, :total => total) end end class Git # Rugged does not have a Git class, but the Repository class should allows us to do what's necessary. def initialize(repo) @repo = repo end def exist? ::File.exists?(@repo.path) end def grep(query, options={}) ref = options[:ref] ? options[:ref] : "HEAD" tree = @repo.lookup(sha_from_ref(ref)).tree tree = @repo.lookup(tree[options[:path]][:oid]) if options[:path] results = [] tree.walk_blobs(:postorder) do |root, entry| blob = @repo.lookup(entry[:oid]) count = 0 blob.content.each_line do |line| next unless line.force_encoding("UTF-8").match(/#{Regexp.escape(query)}/i) count += 1 end path = options[:path] ? ::File.join(options[:path], root, entry[:name]) : "#{root}#{entry[:name]}" results << {:name => path, :count => count} unless count == 0 end results end def rm(path, options = {}) index = @repo.index index.write ::File.unlink ::File.join(@repo.workdir, path) end def cat_file(options, sha) @repo.lookup(sha).read_raw end def apply_patch(head_sha = 'HEAD', patch=nil) false # Rewrite gollum-lib's revert so that it doesn't require a direct equivalent of Grit's apply_patch end def revert(path, sha1, sha2, ref) # FIXME: See https://github.com/gollum/grit_adapter/pull/14 fail NotImplementedError end def checkout(path, ref = 'HEAD', options = {}) path = path.nil? ? path : [path] options = options.merge({:paths => path, :strategy => :force}) if ref == 'HEAD' @repo.checkout_head(options) else ref = "refs/heads/#{ref}" unless ref =~ /^refs\/heads\// @repo.checkout_tree(sha_from_ref(ref), options) end end def log(ref = 'refs/heads/master', path = nil, options = {}) default_options = { :limit => options[:max_count] ? options[:max_count] : 10, :offset => options[:skip] ? options[:skip] : 0, :path => path, :follow => false, :skip_merges => false } options = default_options.merge(options) options[:limit] ||= 0 options[:offset] ||= 0 sha = sha_from_ref(ref) return [] if sha.nil? begin build_log(sha, options) rescue Rugged::OdbError, Rugged::InvalidError, Rugged::ReferenceError # Return an empty array if the ref wasn't found [] end end def versions_for_path(path = nil, ref = nil, options = {}) log(ref, path, options) end def ls_files(query, options = {}) ref = options[:ref] || "refs/heads/master" tree = @repo.lookup(sha_from_ref(ref)).tree tree = @repo.lookup(tree[options[:path]][:oid]) if options[:path] results = [] tree.walk_blobs do |root, blob| next unless blob[:name] =~ /#{query}/ path = options[:path] ? ::File.join(options[:path], root, blob[:name]) : "#{root}#{blob[:name]}" results << path end results end def lookup(id) @repo.lookup(id) end def ref_to_sha(query) return query if sha?(query) query = "refs/heads/#{query}" if !query.nil? && !(query =~ /^refs\/heads\//) && !(query == "HEAD") begin return @repo.rev_parse_oid(query) rescue Rugged::ReferenceError, Rugged::InvalidError return nil end end def sha_or_commit_from_ref(ref, request_kind = nil) sha = ref_to_sha(ref) return nil if sha.nil? object = @repo.lookup(sha) if object.kind_of?(Rugged::Commit) then return Gollum::Git::Commit.new(object) if request_kind == :commit sha elsif object.respond_to?(:target) sha_or_commit_from_ref(object.target.oid, request_kind) end end alias_method :sha_from_ref, :sha_or_commit_from_ref def commit_from_ref(ref) sha_or_commit_from_ref(ref, :commit) end def push(remote, branches = nil, options = {}) branches = [branches].flatten.map {|branch| "refs/heads/#{branch}" unless branch =~ /^refs\/heads\//} @repo.push(remote, branches, options) end def pull(remote, branches = nil, options = {}) branches = [branches].flatten.map {|branch| "refs/heads/#{branch}" unless branch =~ /^refs\/heads\//} r = @repo.remotes[remote] r.fetch(branches, options) branches.each do |branch| branch_name = branch.match(/^refs\/heads\/(.*)/)[1] remote_name = remote.match(/^(refs\/heads\/)?(.*)/)[2] remote_ref = @repo.branches["#{remote_name}/#{branch_name}"].target local_ref = @repo.branches[branch].target index = @repo.merge_commits(local_ref, remote_ref) options = { author: Actor.default_actor.to_h, committer: Actor.default_actor.to_h, message: "Merged branch #{branch} of #{remote}.", parents: [local_ref, remote_ref], tree: index.write_tree(@repo), update_ref: branch } Rugged::Commit.create @repo, options @repo.checkout(@repo.head.name, :strategy => :force) if !@repo.bare? && branch == @repo.head.name end end private def sha?(str) !!(str =~ /^[0-9a-f]{40}$/) end # Return an array of log commits, given an SHA hash and a hash of # options. From Gitlab::Git def build_log(sha, options) # Instantiate a Walker and add the SHA hash walker = Rugged::Walker.new(@repo) walker.push(sha) commits = [] skipped = 0 current_path = options[:path] current_path = nil if current_path == '' limit = options[:limit].to_i offset = options[:offset].to_i skip_merges = options[:skip_merges] walker.sorting(Rugged::SORT_DATE) walker.each do |c| break if limit > 0 && commits.length >= limit if skip_merges # Skip merge commits next if c.parents.length > 1 end if !current_path || commit_touches_path?(c, current_path, options[:follow], walker) # This is a commit we care about, unless we haven't skipped enough # yet skipped += 1 commits.push(Gollum::Git::Commit.new(c)) if skipped > offset end end walker.reset commits end # Returns true if +commit+ introduced changes to +path+, using commit # trees to make that determination. Uses the history simplification # rules that `git log` uses by default, where a commit is omitted if it # is TREESAME to any parent. # # If the +follow+ option is true and the file specified by +path+ was # renamed, then the path value is set to the old path. def commit_touches_path?(commit, path, follow, walker) entry = tree_entry(commit, path) if commit.parents.empty? # This is the root commit, return true if it has +path+ in its tree return entry != nil end num_treesame = 0 commit.parents.each do |parent| parent_entry = tree_entry(parent, path) # Only follow the first TREESAME parent for merge commits if num_treesame > 0 walker.hide(parent) next end if entry.nil? && parent_entry.nil? num_treesame += 1 elsif entry && parent_entry && entry[:oid] == parent_entry[:oid] num_treesame += 1 end end case num_treesame when 0 detect_rename(commit, commit.parents.first, path) if follow true else false end end # Find the entry for +path+ in the tree for +commit+ def tree_entry(commit, path) pathname = Pathname.new(path) tmp_entry = nil pathname.each_filename do |dir| tmp_entry = if tmp_entry.nil? commit.tree[dir] else @repo.lookup(tmp_entry[:oid])[dir] end return nil unless tmp_entry end tmp_entry end # Compare +commit+ and +parent+ for +path+. If +path+ is a file and was # renamed in +commit+, then set +path+ to the old filename. def detect_rename(commit, parent, path) diff = parent.diff(commit, paths: [path], disable_pathspec_match: true) # If +path+ is a filename, not a directory, then we should only have # one delta. We don't need to follow renames for directories. return nil if diff.each_delta.count > 1 delta = diff.each_delta.first if delta.added? full_diff = parent.diff(commit) full_diff.find_similar! full_diff.each_delta do |full_delta| if full_delta.renamed? && path == full_delta.new_file[:path] # Look for the old path in ancestors path.replace(full_delta.old_file[:path]) end end end end end class Index def initialize(index, repo) @index = index @rugged_repo = repo @treemap = {} end def delete(path) @index.remove(path) update_treemap(path, false) false end def delete_all(glob) # if the path start with escape it and avoid treating it as a comment escaped_glob = glob[0] == '#' ? glob.sub('#', '\#') : glob @index.remove_all(escaped_glob) update_treemap(glob, false) false end def add(path, data) blob = @rugged_repo.write(data, :blob) @index.add(:path => path, :oid => blob, :mode => 0100644) update_treemap(path, data) data end def index @index end def commit(message, parents = nil, actor = nil, last_tree = nil, head = 'refs/heads/master') commit_options = {} head = "refs/heads/#{head}" if head && head !~ %r(^refs/heads/) parents = get_parents(parents, head) || [] actor = Gollum::Git::Actor.default_actor if actor.nil? commit_options[:tree] = @index.write_tree commit_options[:author] = actor.to_h commit_options[:committer] = actor.to_h commit_options[:message] = message.to_s commit_options[:parents] = parents commit_options[:update_ref] = head Rugged::Commit.create(@rugged_repo, commit_options) end def tree @treemap end def read_tree(id) id = Gollum::Git::Git.new(@rugged_repo).ref_to_sha(id) return nil if id.nil? current_tree = @rugged_repo.lookup(id) current_tree = current_tree.tree unless current_tree.is_a?(Rugged::Tree) @index.read_tree(current_tree) @current_tree = Gollum::Git::Tree.new(current_tree) end def current_tree @current_tree end private def get_parents(parents, head) if parents parents.map{|parent| parent.commit} elsif ref = @rugged_repo.references[head] ref = ref.target ref = ref.target if ref.respond_to?(:target) [ref] end end def update_treemap(path, data) # From RJGit::Plumbing::Index path = path[1..-1] if path[0] == ::File::SEPARATOR path = path.split(::File::SEPARATOR) last = path.pop current = @treemap path.each do |dir| current[dir] ||= {} node = current[dir] current = node end current[last] = data @treemap end end class Ref def initialize(ref) @ref = ref end def name @ref.name end def commit Gollum::Git::Commit.new(@ref.target) end end class Repo def initialize(path, options) begin @repo = Rugged::Repository.new(path, options) #rescue Grit::InvalidGitRepositoryError # raise Gollum::InvalidGitRepositoryError #rescue Grit::NoSuchPathError # raise Gollum::NoSuchPathError end end def self.init(path) Rugged::Repository.init_at(path, false) self.new(path, :is_bare => false) end def self.init_bare(path) Rugged::Repository.init_at(path, true) self.new(path, :is_bare => true) end def bare @repo.bare? end def config @repo.config end def git @git ||= Gollum::Git::Git.new(@repo) end def commit(id) git.commit_from_ref(id) end def commits(start = 'refs/heads/master', max_count = 10, skip = 0) git.log(start, nil, :max_count => max_count, :skip => skip) end def head Gollum::Git::Ref.new(@repo.head) end def index @index ||= Gollum::Git::Index.new(@repo.index, @repo) end def diff(sha1, sha2, *paths) opts = path == nil ? {} : {:path => path} patches = @repo.diff(sha1, sha2, opts).patches if not paths.empty? patches.keep_if { |p| paths.include? p.delta.new_file[:path] } end patches.map {|patch| OpenStruct.new(:diff => patch.to_s.split("\n")[2..-1].join("\n").force_encoding("UTF-8"))}.reverse # First remove two superfluous lines. Rugged seems to order the diffs differently than Grit, so reverse. end def log(commit = 'refs/heads/master', path = nil, options = {}) git.log(commit, path, options) end def lstree(sha, options = {}) results = [] @repo.lookup(sha).tree.walk(:postorder) do |root, entry| entry[:sha] = entry[:oid] entry[:mode] = entry[:filemode].to_s(8) entry[:type] = entry[:type].to_s entry[:path] = "#{root}#{entry[:name]}" results << entry end results end def path @repo.path end # Checkout branch and if necessary first create it. Currently used only in gollum-lib's tests. def update_ref(ref, commit_sha) ref = "refs/heads/#{ref}" unless ref =~ /^refs\/heads\// if _ref = @repo.references[ref] @repo.references.update(_ref, commit_sha) else @repo.create_branch(ref, commit_sha) @repo.checkout(ref, :strategy => :force) unless @repo.bare? end end def files_sorted_by_created_at(sha = nil) sha ||= @repo.head.target.oid file_renamings = {} sorting = Rugged::SORT_DATE | Rugged::SORT_TOPO @repo.walk(sha, sorting).with_object([]) do |commit, files| parent = commit.parents.first diff = commit.diff(parent, reverse: true) diff.find_similar! diff.each_delta do |delta| name = delta.new_file[:path] if delta.added? files << (file_renamings[name] || name) elsif delta.renamed? file_renamings[delta.old_file[:path]] = file_renamings[name] || name end end end end end class Tree def initialize(tree) @tree = tree end def keys @tree.map{|entry| entry[:name]} end def [](i) @tree[i] end def id @tree.oid end def /(file) return self if file == '/' begin obj = @tree.path(file) rescue Rugged::TreeError return nil end return nil if obj.nil? obj = @tree.owner.lookup(obj[:oid]) obj.is_a?(Rugged::Tree) ? Gollum::Git::Tree.new(obj) : Gollum::Git::Blob.new(obj) end def blobs blobs = [] @tree.each_blob {|blob| blobs << Gollum::Git::Blob.new(@tree.owner.lookup(blob[:oid]), blob) } blobs end end end end gitlab-gollum-rugged_adapter-0.4.4.2/Gemfile0000644000004100000410000000025513447442616020731 0ustar www-datawww-datasource 'https://rubygems.org' gemspec gem 'rake', '~> 10.4.2' gem 'adapter_specs', git: 'https://github.com/gollum/adapter_specs.git' group :test do gem "simplecov" end