gitlab-org--gitlab-foss/app/services/lfs/file_transformer.rb
Luke Duncalfe dcf811ba14 CE backport for changes in EE MR 14017
This backports to CE changes to allow the EE model
DesignManagement::Repository to override the #attributes_at method to
provide its own git attributes.

The #attributes_at method was freely available, as it's never called by
anything in the app. It looks like the code that called it got
refactored out of existence in ca66a04f. It was still being called in a
spec
85b29c1c2f/spec/services/files/create_service_spec.rb (L40)
which I've left because with the change in Lfs::FileTransformer in fact
is now again the perfect test!

See EE MR
https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/14017

And these comment threads
https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13894#note_178002089
https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/13894#note_178049984
2019-06-19 17:51:57 +00:00

89 lines
3 KiB
Ruby

# frozen_string_literal: true
module Lfs
# Usage: Calling `new_file` check to see if a file should be in LFS and
# return a transformed result with `content` and `encoding` to commit.
#
# The `repository` passed to the initializer can be a Repository or
# a DesignManagement::Repository (an EE-specific class that inherits
# from Repository).
#
# The `repository_type` property will be one of the types named in
# `Gitlab::GlRepository.types`, and is recorded on the `LfsObjectsProject`
# in order to identify the repository location of the blob.
#
# For LFS an LfsObject linked to the project is stored and an LFS
# pointer returned. If the file isn't in LFS the untransformed content
# is returned to save in the commit.
#
# transformer = Lfs::FileTransformer.new(project, repository, @branch_name)
# content_or_lfs_pointer = transformer.new_file(file_path, content).content
# create_transformed_commit(content_or_lfs_pointer)
#
class FileTransformer
attr_reader :project, :repository, :repository_type, :branch_name
def initialize(project, repository, branch_name)
@project = project
@repository = repository
@repository_type = repository.repo_type.name
@branch_name = branch_name
end
def new_file(file_path, file_content, encoding: nil)
if project.lfs_enabled? && lfs_file?(file_path)
file_content = parse_file_content(file_content, encoding: encoding)
lfs_pointer_file = Gitlab::Git::LfsPointerFile.new(file_content)
lfs_object = create_lfs_object!(lfs_pointer_file, file_content)
link_lfs_object!(lfs_object)
Result.new(content: lfs_pointer_file.pointer, encoding: 'text')
else
Result.new(content: file_content, encoding: encoding)
end
end
class Result
attr_reader :content, :encoding
def initialize(content:, encoding:)
@content = content
@encoding = encoding
end
end
private
def lfs_file?(file_path)
cached_attributes.attributes(file_path)['filter'] == 'lfs'
end
def cached_attributes
@cached_attributes ||= repository.attributes_at(branch_name)
end
# rubocop: disable CodeReuse/ActiveRecord
def create_lfs_object!(lfs_pointer_file, file_content)
LfsObject.find_or_create_by(oid: lfs_pointer_file.sha256, size: lfs_pointer_file.size) do |lfs_object|
lfs_object.file = CarrierWaveStringFile.new(file_content)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def link_lfs_object!(lfs_object)
LfsObjectsProject.safe_find_or_create_by!(
project: project,
lfs_object: lfs_object,
repository_type: repository_type
)
end
def parse_file_content(file_content, encoding: nil)
return file_content.read if file_content.respond_to?(:read)
return Base64.decode64(file_content) if encoding == 'base64'
file_content
end
end
end