diff --git a/app/models/concerns/with_git.rb b/app/models/concerns/with_git.rb index d749a14032d2ae5f3e1f7f887fc0cd57e7c88240..6de6df853b214f1c7684227d9178f1082973b6df 100644 --- a/app/models/concerns/with_git.rb +++ b/app/models/concerns/with_git.rb @@ -21,7 +21,6 @@ module WithGit end handle_asynchronously :sync_with_git - def git_path_static '' end diff --git a/app/services/git/providers/github.rb b/app/services/git/providers/github.rb index dc67bcbf2686cb36236df9aa00f3a8706b4cb718..714e65953da686c387013f937144dad1b2bfdea7 100644 --- a/app/services/git/providers/github.rb +++ b/app/services/git/providers/github.rb @@ -1,26 +1,31 @@ class Git::Providers::Github + attr_reader :access_token, :repository + + def initialize(access_token, repository) + @access_token = access_token + @repository = repository + end def add_to_batch( path: nil, previous_path: nil, data:) - @batch ||= [] file = find_in_tree previous_path if file.nil? # New file - @batch << { + batch << { path: path, mode: '100644', # https://docs.github.com/en/rest/reference/git#create-a-tree type: 'blob', content: data } - elsif previous_path != path || file_sha(previous_path) != local_file_sha(data) + elsif previous_path != path || git_sha(previous_path) != sha(data) # Different path or content - @batch << { + batch << { path: previous_path, mode: file[:mode], type: file[:type], sha: nil } - @batch << { + batch << { path: path, mode: file[:mode], type: file[:type], @@ -29,50 +34,32 @@ class Git::Providers::Github end end - def commit_batch(commit_message) - unless @batch.empty? - new_tree = client.create_tree repository, @batch, base_tree: tree[:sha] - commit = client.create_commit repository, commit_message, new_tree[:sha], branch_sha - client.update_branch repository, default_branch, commit[:sha] - end - @tree = nil + def push(commit_message) + return unless valid? + return if batch.empty? + new_tree = client.create_tree repository, batch, base_tree: tree[:sha] + commit = client.create_commit repository, commit_message, new_tree[:sha], branch_sha + client.update_branch repository, default_branch, commit[:sha] true end - def remove(path, commit_message) - client.delete_contents repository, path, commit_message, file_sha(path) - true - rescue - false - end + protected - def read_file_at(path) - data = client.content repository, path: path - Base64.decode64 data.content - rescue - '' + def valid? + repository.present? && access_token.present? end - protected - def client @client ||= Octokit::Client.new access_token: access_token end - def file_sha(path) - begin - content = client.content repository, path: path - sha = content[:sha] - rescue - sha = nil - end - sha + def access_token + @access_token ||= website&.access_token end - def local_file_sha(data) - # Git SHA-1 is calculated from the String "blob <length>\x00<contents>" - # Source: https://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html - OpenSSL::Digest::SHA1.hexdigest "blob #{data.bytesize}\x00#{data}" + # Path of the repo + def repository + @repository ||= website&.repository end def default_branch @@ -87,14 +74,30 @@ class Git::Providers::Github @tree ||= client.tree repository, branch_sha, recursive: true end + def batch + @batch ||= [] + end + + def git_sha(path) + begin + content = client.content repository, path: path + sha = content[:sha] + rescue + sha = nil + end + sha + end + + def sha(data) + # Git SHA-1 is calculated from the String "blob <length>\x00<contents>" + # Source: https://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html + OpenSSL::Digest::SHA1.hexdigest "blob #{data.bytesize}\x00#{data}" + end + def find_in_tree(path) tree[:tree].each do |file| return file if path == file[:path] end nil end - - def tmp_directory - "tmp/github/#{repository}" - end end diff --git a/app/services/git/repository.rb b/app/services/git/repository.rb index 2c48cb8ff2a29505b17c7fced9c83b848805760d..7cd2916b4d92e233b89216dc67caf693fda2f063 100644 --- a/app/services/git/repository.rb +++ b/app/services/git/repository.rb @@ -11,125 +11,33 @@ class Git::Repository end def sync! - return unless valid? return if git_files.empty? sync_git_files - mark_as_synced if commit_batch + mark_as_synced if provider.push(commit_message) end protected - def client - @client ||= Octokit::Client.new access_token: access_token - end - - def access_token - @access_token ||= website&.access_token - end - - def repository - @repository ||= website&.repository - end - def provider - @provider ||= Git::Providers::Github.new + @provider ||= Git::Providers::Github.new(website&.access_token, website&.repository) end def git_files @git_files ||= [] end - def batch - @batch ||= [] - end - - def default_branch - @default_branch ||= client.repo(repository)[:default_branch] - end - - def branch_sha - @branch_sha ||= client.branch(repository, default_branch)[:commit][:sha] - end - - def tree - @tree ||= client.tree repository, branch_sha, recursive: true - end - - def valid? - repository.present? && access_token.present? - end - def sync_git_files git_files.each do |git_file| next if git_file.synced? - add_to_batch path: git_file.path, - previous_path: git_file.previous_path, - data: git_file.to_s - end - end - - def add_to_batch( path: nil, - previous_path: nil, - data:) - file = find_in_tree previous_path - if file.nil? # New file - batch << { - path: path, - mode: '100644', # https://docs.github.com/en/rest/reference/git#create-a-tree - type: 'blob', - content: data - } - elsif previous_path != path || git_sha(previous_path) != sha(data) - # Different path or content - batch << { - path: previous_path, - mode: file[:mode], - type: file[:type], - sha: nil - } - batch << { - path: path, - mode: file[:mode], - type: file[:type], - content: data - } + provider.add_to_batch path: git_file.path, + previous_path: git_file.previous_path, + data: git_file.to_s end end - def commit_batch - return if batch.empty? - new_tree = client.create_tree repository, batch, base_tree: tree[:sha] - commit = client.create_commit repository, commit_message, new_tree[:sha], branch_sha - client.update_branch repository, default_branch, commit[:sha] - true - end - def mark_as_synced git_files.each do |git_file| git_file.update_columns previous_path: git_file.path, previous_sha: git_file.sha end end - - def git_sha(path) - begin - content = client.content repository, path: path - sha = content[:sha] - rescue - sha = nil - end - sha - end - - def sha(data) - # Git SHA-1 is calculated from the String "blob <length>\x00<contents>" - # Source: https://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html - OpenSSL::Digest::SHA1.hexdigest "blob #{data.bytesize}\x00#{data}" - end - - def find_in_tree(path) - tree[:tree].each do |file| - return file if path == file[:path] - end - nil - end end diff --git a/docs/websites/export.md b/docs/websites/export.md index a4c71a4ac5b6f27e56be12bc5b7c4bcd3c49ac1d..bf9c2e88ad885f27f866847649dc9017d4e0bdab 100644 --- a/docs/websites/export.md +++ b/docs/websites/export.md @@ -67,6 +67,8 @@ def update end ``` +TODO gérer la suppression correctement + ## Code ### Website::WithRepository