diff --git a/app/services/git/providers/gitlab.rb b/app/services/git/providers/gitlab.rb index 5a0f0a921..61303c52f 100644 --- a/app/services/git/providers/gitlab.rb +++ b/app/services/git/providers/gitlab.rb @@ -8,35 +8,46 @@ def url end def create_file(path, content) + new_path_file = tree_item_at_path(path) + action = new_path_file.nil? ? 'create' : 'update' + batch << { - action: 'create', + action: action, file_path: path, content: content } end def update_file(path, previous_path, content) - file = find previous_path - if file.present? && previous_path != path + previous_path_file = tree_item_at_path(previous_path) + new_path_file = tree_item_at_path(path) + # En cas de dissonnance entre l'analyzer et le provider, on raise une erreur + if previous_path_file.nil? && new_path_file.nil? + raise "File to update does not exist on Git (repository: #{repository}, previous_path: #{previous_path}, path: #{path})" + end + + # We will remove file at previous path and create or update it at new path + if previous_path != path && previous_path_file.present? batch << { - action: 'move', - file_path: path, - previous_path: previous_path + action: 'delete', + file_path: previous_path } end + + action = new_path_file.present? ? 'update' : 'create' batch << { - action: 'update', + action: action, file_path: path, content: content } end def destroy_file(path) - file = find path + file = tree_item_at_path(path) return if file.nil? batch << { action: 'delete', - file_path: path, + file_path: path } end @@ -58,23 +69,25 @@ def push(commit_message) branch, commit_message, batch + # The repo changed, invalidate the tree + @tree = nil + @tree_items_by_path = nil + # true end def computed_sha(string) - OpenSSL::Digest::SHA256.hexdigest string + # Git SHA-1 is calculated from the String "blob \x00" + # Source: https://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html + OpenSSL::Digest::SHA1.hexdigest "blob #{string.bytesize}\x00#{string}" end # https://gitlab.com/gitlab-org/gitlab/-/issues/23504 # TODO : Il faudrait, comme sur GitHub, stocker le tree pour éviter N requêtes pour N objets. def git_sha(path) - begin - file = find path - sha = file['content_sha256'] - rescue - sha = nil - end - sha + return if path.nil? + # Try to find in stored tree to avoid multiple queries + tree_item_at_path(path)&.dig(:sha) end def valid? @@ -89,22 +102,15 @@ def valid? end def branch - super.present? ? super - : 'main' + super.presence || 'main' end - # TODO def files_in_the_repository - super + @files_in_the_repository ||= tree.map { |file| file[:path] } end protected - def endpoint - @endpoint.blank? ? DEFAULT_ENDPOINT - : @endpoint - end - def client @client ||= Gitlab.client( endpoint: endpoint, @@ -112,6 +118,11 @@ def client ) end + def endpoint + @endpoint.blank? ? DEFAULT_ENDPOINT + : @endpoint + end + def find(path) client.get_file repository, path, @@ -120,4 +131,31 @@ def find(path) nil end + def tree_item_at_path(path) + tree_items_by_path[path] if tree_items_by_path.has_key? path + end + + def tree_items_by_path + unless @tree_items_by_path + @tree_items_by_path = {} + tree.each do |hash| + path = hash["path"] + @tree_items_by_path[path] = { + mode: hash["mode"], + type: hash["type"], + sha: hash["id"] + } + end + end + @tree_items_by_path + end + + def tree + @tree ||= client.tree(repository, { + ref: branch, + recursive: true, + per_page: 100 + }).auto_paginate + end + end