Commit 0e9323bd authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'fix/import-export-project-avatar' into 'master'

Project avatar import/export functionality

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/19851

Adds logic to export and import a project avatar

- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- ~~[ ] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)~~
- Tests
  - [x] Added for this feature/bug
  - [x] All builds are passing
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

See merge request !5273
parents 6593e3ce f85b78c1
...@@ -130,6 +130,7 @@ v 8.10.0 (unreleased) ...@@ -130,6 +130,7 @@ v 8.10.0 (unreleased)
- Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska) - Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska)
- Allow bulk (un)subscription from issues in issue index - Allow bulk (un)subscription from issues in issue index
- Fix MR diff encoding issues exporting GitLab projects - Fix MR diff encoding issues exporting GitLab projects
- Export and import avatar as part of project import/export
v 8.9.6 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
......
...@@ -9,7 +9,7 @@ module Projects ...@@ -9,7 +9,7 @@ module Projects
private private
def save_all def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save) if [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(project: project, shared: @shared) Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success notify_success
else else
...@@ -21,6 +21,10 @@ module Projects ...@@ -21,6 +21,10 @@ module Projects
Gitlab::ImportExport::VersionSaver.new(shared: @shared) Gitlab::ImportExport::VersionSaver.new(shared: @shared)
end end
def avatar_saver
Gitlab::ImportExport::AvatarSaver.new(project: project, shared: @shared)
end
def project_tree_saver def project_tree_saver
Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared) Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
end end
......
...@@ -14,4 +14,8 @@ class AvatarUploader < CarrierWave::Uploader::Base ...@@ -14,4 +14,8 @@ class AvatarUploader < CarrierWave::Uploader::Base
def reset_events_cache(file) def reset_events_cache(file)
model.reset_events_cache if model.is_a?(User) model.reset_events_cache if model.is_a?(User)
end end
def exists?
model.avatar.file && model.avatar.file.exists?
end
end end
module Gitlab
module ImportExport
class AvatarRestorer
def initialize(project:, shared:)
@project = project
@shared = shared
end
def restore
return true unless avatar_export_file
@project.avatar = File.open(avatar_export_file)
@project.save!
rescue => e
@shared.error(e)
false
end
private
def avatar_export_file
@avatar_export_file ||= Dir["#{avatar_export_path}/*"].first
end
def avatar_export_path
File.join(@shared.export_path, 'avatar')
end
end
end
end
module Gitlab
module ImportExport
class AvatarSaver
include Gitlab::ImportExport::CommandLineUtil
def initialize(project:, shared:)
@project = project
@shared = shared
end
def save
return true unless @project.avatar.exists?
copy_files(avatar_path, avatar_export_path)
rescue => e
@shared.error(e)
false
end
private
def avatar_export_path
File.join(@shared.export_path, 'avatar', @project.avatar_identifier)
end
def avatar_path
@project.avatar.path
end
end
end
end
...@@ -36,6 +36,15 @@ module Gitlab ...@@ -36,6 +36,15 @@ module Gitlab
def git_bin_path def git_bin_path
Gitlab.config.git.bin_path Gitlab.config.git.bin_path
end end
def copy_files(source, destination)
# if we are copying files, create the destination folder
destination_folder = File.file?(source) ? File.dirname(destination) : destination
FileUtils.mkdir_p(destination_folder)
FileUtils.copy_entry(source, destination)
true
end
end end
end end
end end
...@@ -9,7 +9,7 @@ module Gitlab ...@@ -9,7 +9,7 @@ module Gitlab
end end
def execute def execute
if import_file && check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore) if import_file && check_version! && [project_tree, avatar_restorer, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
project_tree.restored_project project_tree.restored_project
else else
raise Projects::ImportService::Error.new(@shared.errors.join(', ')) raise Projects::ImportService::Error.new(@shared.errors.join(', '))
...@@ -35,6 +35,10 @@ module Gitlab ...@@ -35,6 +35,10 @@ module Gitlab
project: @project) project: @project)
end end
def avatar_restorer
Gitlab::ImportExport::AvatarRestorer.new(project: project_tree.restored_project, shared: @shared)
end
def repo_restorer def repo_restorer
Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path, Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path,
shared: @shared, shared: @shared,
......
module Gitlab module Gitlab
module ImportExport module ImportExport
class UploadsSaver class UploadsSaver
include Gitlab::ImportExport::CommandLineUtil
def initialize(project:, shared:) def initialize(project:, shared:)
@project = project @project = project
@shared = shared @shared = shared
...@@ -17,12 +19,6 @@ module Gitlab ...@@ -17,12 +19,6 @@ module Gitlab
private private
def copy_files(source, destination)
FileUtils.mkdir_p(destination)
FileUtils.copy_entry(source, destination)
true
end
def uploads_export_path def uploads_export_path
File.join(@shared.export_path, 'uploads') File.join(@shared.export_path, 'uploads')
end end
......
require 'spec_helper'
describe Gitlab::ImportExport::AvatarRestorer, lib: true do
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
let(:project) { create(:empty_project) }
before do
allow_any_instance_of(described_class).to receive(:avatar_export_file)
.and_return(Rails.root + "spec/fixtures/dk.png")
end
after do
project.remove_avatar!
end
it 'restores a project avatar' do
expect(described_class.new(project: project, shared: shared).restore).to be true
end
it 'saves the avatar into the project' do
described_class.new(project: project, shared: shared).restore
expect(project.reload.avatar.file.exists?).to be true
end
end
require 'spec_helper'
describe Gitlab::ImportExport::AvatarSaver, lib: true do
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
let(:project) { create(:empty_project) }
before do
FileUtils.mkdir_p("#{shared.export_path}/avatar/")
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end
after do
FileUtils.rm_rf("#{shared.export_path}/avatar")
end
it 'saves a project avatar' do
described_class.new(project: project_with_avatar, shared: shared).save
expect(File).to exist("#{shared.export_path}/avatar/dk.png")
end
it 'is fine not to have an avatar' do
expect(described_class.new(project: project, shared: shared).save).to be true
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment