Class: Manifestly::Repository

Inherits:
Object
  • Object
show all
Defined in:
lib/manifestly/repository.rb

Defined Under Namespace

Classes: CommitContentError, CommitNotPresent, ManifestUnchanged, NoCommitsError, ShaAlreadyTagged, TagShaNotFound

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.is_github_name?(value) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/manifestly/repository.rb', line 54

def self.is_github_name?(value)
  value.match(/\A[^\/]+\/[^\/]+\z/)
end

.load(path) ⇒ Object

Returns an object if can load a git repository at the specified path, otherwise nil



25
26
27
28
# File 'lib/manifestly/repository.rb', line 25

def self.load(path)
  repository = new(path)
  repository.is_git_repository? ? repository : nil
end

.load_cached(github_name_or_path, options) ⇒ Object

Loads a gem-cached copy of the specified repository, cloning it if necessary.

Raises:

  • (IllegalArgument)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/manifestly/repository.rb', line 32

def self.load_cached(github_name_or_path, options)
  options[:update] ||= false

  raise(IllegalArgument, "Repository name is blank.") if github_name_or_path.blank?

  cached_path = "#{Manifestly.configuration.cached_repos_root_dir}/.manifestly/.manifest_repositories/#{github_name_or_path}"
  FileUtils.mkdir_p(cached_path)

  repository = load(cached_path)

  if repository.nil?
    remote_location = is_github_name?(github_name_or_path) ?
                        "[email protected]:#{github_name_or_path}.git" :
                        github_name_or_path
    Git.clone(remote_location, cached_path)
    repository = new(cached_path)
  end

  repository.make_like_just_cloned! if options[:update]
  repository
end

Instance Method Details

#checkout_commit(sha, fetch_if_unfound = false) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/manifestly/repository.rb', line 154

def checkout_commit(sha, fetch_if_unfound=false)
  begin
    git.checkout(sha)
  rescue Git::GitExecuteError => e
    if is_commit_not_found_exception?(e)
      if fetch_if_unfound
        git.fetch
        checkout_commit(sha, false)
      else
        raise CommitNotPresent.new(sha, self)
      end
    else
      raise
    end
  end
end

#commits(options = {}) ⇒ Object



99
100
101
102
103
104
105
106
107
108
# File 'lib/manifestly/repository.rb', line 99

def commits(options={})
  begin
    log = git.log(1000000).object('master') # don't limit
    log = log.between(options[:between][0], options[:between][1]) if options[:between]
    log = log.grep("Merge pull request") if @prs_only
    log.tap(&:first) # tap to force otherwise lazy checks
  rescue Git::GitExecuteError => e
    raise NoCommitsError
  end
end

#current_branch_nameObject



176
177
178
# File 'lib/manifestly/repository.rb', line 176

def current_branch_name
  git.lib.branch_current
end

#current_commitObject



180
181
182
183
# File 'lib/manifestly/repository.rb', line 180

def current_commit
  sha = git.show.split("\n")[0].split(" ")[1]
  find_commit(sha)
end

#deepest_working_dirObject



242
243
244
# File 'lib/manifestly/repository.rb', line 242

def deepest_working_dir
  working_dir.to_s.split(File::SEPARATOR).last
end

#display_nameObject



246
247
248
# File 'lib/manifestly/repository.rb', line 246

def display_name
  "[#{deepest_working_dir}]#{' ' + github_name_or_path if !github_name_or_path.nil?}"
end

#fetchObject



74
75
76
# File 'lib/manifestly/repository.rb', line 74

def fetch
  git.fetch
end

#file_commits(file) ⇒ Object



146
147
148
149
150
151
152
# File 'lib/manifestly/repository.rb', line 146

def file_commits(file)
  commits = git.log
  commits = commits.select do |commit|
    diff = Diff.new(commit.diff_parent.to_s)
    diff.has_file?(file)
  end
end

#find_commit(sha) ⇒ Object

returns the commit matching the provided sha or raises



111
112
113
114
115
116
117
# File 'lib/manifestly/repository.rb', line 111

def find_commit(sha)
  begin
    git.gcommit(sha).tap(&:sha)
  rescue Git::GitExecuteError => e
    raise CommitNotPresent.new(sha, self)
  end
end

#get_commit_content(sha) ⇒ Object



119
120
121
# File 'lib/manifestly/repository.rb', line 119

def get_commit_content(sha)
  get_commit_file(sha).to_content
end

#get_commit_file(sha) ⇒ Object

Raises:



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/manifestly/repository.rb', line 127

def get_commit_file(sha)
  diff_string = begin
    git.show(sha)
  rescue Git::GitExecuteError => e
    if is_commit_not_found_exception?(e)
      raise CommitNotPresent.new(sha, self)
    else
      raise
    end
  end

  sha_diff = Diff.new(diff_string)

  raise(CommitContentError, "No content to retrieve for SHA #{sha}!") if sha_diff.num_files == 0
  raise(CommitContentError, "More than one file in the commit for SHA #{sha}!") if sha_diff.num_files > 1

  sha_diff[0]
end

#get_commit_filename(sha) ⇒ Object



123
124
125
# File 'lib/manifestly/repository.rb', line 123

def get_commit_filename(sha)
  get_commit_file(sha).to_name
end

#get_shas_with_tag(options = {}) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/manifestly/repository.rb', line 205

def get_shas_with_tag(options={})
  options[:file] ||= ".*"
  options[:order] ||= :descending

  # only look for the unique tags, not the plain ones
  pattern = /.*\/#{options[:file]}\/#{options[:tag]}/

  tag_objects = git.tags.select{|tag| tag.name.match(pattern)}
                        .sort_by(&:name)

  tag_objects.reverse! if options[:order] == :descending

  tag_objects.collect do |tag_object|
    matched_sha = tag_object.contents.match(/[a-f0-9]{40}/)
    raise(TagShaNotFound, "Could not retrieve SHA for tag '#{full_tag}'") if matched_sha.nil?
    matched_sha.to_s
  end
end

#gitObject



78
79
80
# File 'lib/manifestly/repository.rb', line 78

def git
  Git.open(@path)
end

#github_name_or_pathObject



232
233
234
235
236
# File 'lib/manifestly/repository.rb', line 232

def github_name_or_path
  # Extract 'org/reponame' out of remote url for both HTTP and SSH clones
  return nil if origin.nil?
  origin.url[/github.com.(.*?)(.git)?$/,1]
end

#has_commits?Boolean

Returns:

  • (Boolean)


66
67
68
69
70
71
72
# File 'lib/manifestly/repository.rb', line 66

def has_commits?
  begin
    git && commits
  rescue NoCommitsError
    false
  end
end

#is_commit_not_found_exception?(e) ⇒ Boolean

Returns:

  • (Boolean)


171
172
173
174
# File 'lib/manifestly/repository.rb', line 171

def is_commit_not_found_exception?(e)
  e.message.include?("fatal: reference is not a tree") ||
  e.message.include?("fatal: ambiguous argument")
end

#is_git_repository?Boolean

Returns:

  • (Boolean)


58
59
60
61
62
63
64
# File 'lib/manifestly/repository.rb', line 58

def is_git_repository?
  begin
    git
  rescue ArgumentError
    false
  end
end

#make_like_just_cloned!Object



82
83
84
85
86
87
88
# File 'lib/manifestly/repository.rb', line 82

def make_like_just_cloned!
  git.branch('master').checkout
  # Blow away tags and fetch them back http://stackoverflow.com/a/5373319/1664216
  git.tags.map{|tag| git.delete_tag(tag.name)}
  git.fetch('origin', :tags => true)
  git.reset_hard('origin/master')
end

#originObject



228
229
230
# File 'lib/manifestly/repository.rb', line 228

def origin
  @origin ||= git.remotes.select{|remote| remote.name == 'origin'}.first
end

#push_file!(local_file_path, repository_file_path, message) ⇒ Object

Raises:



90
91
92
93
94
95
96
97
# File 'lib/manifestly/repository.rb', line 90

def push_file!(local_file_path, repository_file_path, message)
  full_repository_file_path = File.join(@path, repository_file_path)
  FileUtils.cp(local_file_path, full_repository_file_path)
  git.add(repository_file_path)
  raise ManifestUnchanged if git.status.changed.empty? && git.status.added.empty?
  git.commit(message)
  git.push
end

#tag_scoped_to_file(options = {}) ⇒ Object

Raises:

  • (IllegalArgument)


185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/manifestly/repository.rb', line 185

def tag_scoped_to_file(options={})
  raise(IllegalArgument, "Tag names cannot contain forward slashes") if options[:tag].include?("/")

  options[:push] ||= false
  options[:message] ||= "no message"

  existing_shas = get_shas_with_tag(tag: options[:tag])
  raise(ShaAlreadyTagged) if existing_shas.any?{|existing| existing.match(/#{options[:sha]}/)}

  filename = get_commit_filename(options[:sha])

  plain_tag = "#{filename}/#{options[:tag]}"
  git.add_tag(plain_tag, options[:sha], {annotate: true, message: options[:message], f: true})
  git.push('origin', "refs/tags/#{plain_tag}", f: true) if options[:push]

  unique_tag = "#{Time.now.utc.strftime("%Y%m%d-%H%M%S.%6N")}/#{::SecureRandom.hex(2)}/#{filename}/#{options[:tag]}"
  git.add_tag(unique_tag, options[:sha], {annotate: true, message: options[:message], f: true})
  git.push('origin', "refs/tags/#{unique_tag}", f: true) if options[:push]
end

#toggle_prs_onlyObject



224
225
226
# File 'lib/manifestly/repository.rb', line 224

def toggle_prs_only
  @prs_only = !@prs_only
end

#working_dirObject



238
239
240
# File 'lib/manifestly/repository.rb', line 238

def working_dir
  git.dir
end