Commit 88295e07 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'project-specific-lfs' into 'master'

Added project specific enable/disable setting for LFS

## What does this MR do?

Adds project specific enable/disable setting for LFS

## What are the relevant issue numbers?

Needed for #18092

See merge request !5997
parents 1e08429d cf37d623
...@@ -25,6 +25,7 @@ v 8.12.0 (unreleased) ...@@ -25,6 +25,7 @@ v 8.12.0 (unreleased)
- Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps) - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
- Fix markdown help references (ClemMakesApps) - Fix markdown help references (ClemMakesApps)
- Add last commit time to repo view (ClemMakesApps) - Add last commit time to repo view (ClemMakesApps)
- Added project specific enable/disable setting for LFS !5997
- Added tests for diff notes - Added tests for diff notes
- Add a button to download latest successful artifacts for branches and tags !5142 - Add a button to download latest successful artifacts for branches and tags !5142
- Add delimiter to project stars and forks count (ClemMakesApps) - Add delimiter to project stars and forks count (ClemMakesApps)
......
...@@ -311,6 +311,14 @@ a.deploy-project-label { ...@@ -311,6 +311,14 @@ a.deploy-project-label {
color: $gl-success; color: $gl-success;
} }
.lfs-enabled {
color: $gl-success;
}
.lfs-disabled {
color: $gl-warning;
}
.breadcrumb.repo-breadcrumb { .breadcrumb.repo-breadcrumb {
padding: 0; padding: 0;
background: transparent; background: transparent;
......
...@@ -309,7 +309,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -309,7 +309,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch, :issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled, :lfs_enabled
) )
end end
......
...@@ -23,10 +23,14 @@ module LfsHelper ...@@ -23,10 +23,14 @@ module LfsHelper
end end
def lfs_download_access? def lfs_download_access?
return false unless project.lfs_enabled?
project.public? || ci? || (user && user.can?(:download_code, project)) project.public? || ci? || (user && user.can?(:download_code, project))
end end
def lfs_upload_access? def lfs_upload_access?
return false unless project.lfs_enabled?
user && user.can?(:push_code, project) user && user.can?(:push_code, project)
end end
......
...@@ -187,6 +187,18 @@ module ProjectsHelper ...@@ -187,6 +187,18 @@ module ProjectsHelper
nav_tabs.flatten nav_tabs.flatten
end end
def project_lfs_status(project)
if project.lfs_enabled?
content_tag(:span, class: 'lfs-enabled') do
'Enabled'
end
else
content_tag(:span, class: 'lfs-disabled') do
'Disabled'
end
end
end
def git_user_name def git_user_name
if current_user if current_user
current_user.name current_user.name
......
...@@ -390,6 +390,13 @@ class Project < ActiveRecord::Base ...@@ -390,6 +390,13 @@ class Project < ActiveRecord::Base
end end
end end
def lfs_enabled?
return false unless Gitlab.config.lfs.enabled
return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
self[:lfs_enabled]
end
def repository_storage_path def repository_storage_path
Gitlab.config.repositories.storages[repository_storage] Gitlab.config.repositories.storages[repository_storage]
end end
......
...@@ -73,6 +73,12 @@ ...@@ -73,6 +73,12 @@
%span.light last commit: %span.light last commit:
%strong %strong
= last_commit(@project) = last_commit(@project)
%li
%span.light Git LFS status:
%strong
= project_lfs_status(@project)
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- else - else
%li %li
%span.light repository: %span.light repository:
......
...@@ -80,6 +80,16 @@ ...@@ -80,6 +80,16 @@
%strong Snippets %strong Snippets
%br %br
%span.descr Share code pastes with others out of git repository %span.descr Share code pastes with others out of git repository
- if Gitlab.config.lfs.enabled && current_user.admin?
.form-group
.checkbox
= f.label :lfs_enabled do
= f.check_box :lfs_enabled, checked: @project.lfs_enabled?
%strong LFS
%br
%span.descr
Git Large File Storage
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled
.form-group .form-group
.checkbox .checkbox
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLfsEnabledToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :projects, :lfs_enabled, :boolean
end
end
...@@ -825,6 +825,7 @@ ActiveRecord::Schema.define(version: 20160827011312) do ...@@ -825,6 +825,7 @@ ActiveRecord::Schema.define(version: 20160827011312) do
t.string "repository_storage", default: "default", null: false t.string "repository_storage", default: "default", null: false
t.boolean "request_access_enabled", default: true, null: false t.boolean "request_access_enabled", default: true, null: false
t.boolean "has_external_wiki" t.boolean "has_external_wiki"
t.boolean "lfs_enabled"
end end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
......
...@@ -452,6 +452,7 @@ Parameters: ...@@ -452,6 +452,7 @@ Parameters:
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
### Create project for user ### Create project for user
...@@ -478,6 +479,7 @@ Parameters: ...@@ -478,6 +479,7 @@ Parameters:
- `import_url` (optional) - `import_url` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
### Edit project ### Edit project
...@@ -505,6 +507,7 @@ Parameters: ...@@ -505,6 +507,7 @@ Parameters:
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional) - `public_builds` (optional)
- `only_allow_merge_if_build_succeeds` (optional) - `only_allow_merge_if_build_succeeds` (optional)
- `lfs_enabled` (optional)
On success, method returns 200 with the updated project. If parameters are On success, method returns 200 with the updated project. If parameters are
invalid, 400 is returned. invalid, 400 is returned.
......
...@@ -32,4 +32,12 @@ Snippets are little bits of code or text. ...@@ -32,4 +32,12 @@ Snippets are little bits of code or text.
This is a nice place to put code or text that is used semi-regularly within the project, but does not belong in source control. This is a nice place to put code or text that is used semi-regularly within the project, but does not belong in source control.
For example, a specific config file that is used by > the team that is only valid for the people that work on the code. For example, a specific config file that is used by the team that is only valid for the people that work on the code.
## Git LFS
>**Note:** Project-specific LFS setting was added on 8.12 and is available only to admins.
Git Large File Storage allows you to easily manage large binary files with Git.
With this setting admins can better control which projects are allowed to use
LFS.
...@@ -78,7 +78,7 @@ module API ...@@ -78,7 +78,7 @@ module API
expose :path, :path_with_namespace expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled
expose :created_at, :last_activity_at expose :created_at, :last_activity_at
expose :shared_runners_enabled expose :shared_runners_enabled, :lfs_enabled
expose :creator_id expose :creator_id
expose :namespace expose :namespace
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
......
...@@ -105,6 +105,7 @@ module API ...@@ -105,6 +105,7 @@ module API
# visibility_level (optional) - 0 by default # visibility_level (optional) - 0 by default
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# POST /projects # POST /projects
post do post do
...@@ -124,7 +125,8 @@ module API ...@@ -124,7 +125,8 @@ module API
:visibility_level, :visibility_level,
:import_url, :import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(current_user, attrs).execute @project = ::Projects::CreateService.new(current_user, attrs).execute
if @project.saved? if @project.saved?
...@@ -156,6 +158,7 @@ module API ...@@ -156,6 +158,7 @@ module API
# visibility_level (optional) # visibility_level (optional)
# import_url (optional) # import_url (optional)
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# POST /projects/user/:user_id # POST /projects/user/:user_id
post "user/:user_id" do post "user/:user_id" do
...@@ -174,7 +177,8 @@ module API ...@@ -174,7 +177,8 @@ module API
:visibility_level, :visibility_level,
:import_url, :import_url,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
@project = ::Projects::CreateService.new(user, attrs).execute @project = ::Projects::CreateService.new(user, attrs).execute
if @project.saved? if @project.saved?
...@@ -220,6 +224,7 @@ module API ...@@ -220,6 +224,7 @@ module API
# public (optional) - if true same as setting visibility_level = 20 # public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project # visibility_level (optional) - visibility level of a project
# public_builds (optional) # public_builds (optional)
# lfs_enabled (optional)
# Example Request # Example Request
# PUT /projects/:id # PUT /projects/:id
put ':id' do put ':id' do
...@@ -237,7 +242,8 @@ module API ...@@ -237,7 +242,8 @@ module API
:public, :public,
:visibility_level, :visibility_level,
:public_builds, :public_builds,
:only_allow_merge_if_build_succeeds] :only_allow_merge_if_build_succeeds,
:lfs_enabled]
attrs = map_public_to_visibility_level(attrs) attrs = map_public_to_visibility_level(attrs)
authorize_admin_project authorize_admin_project
authorize! :rename_project, user_project if attrs[:name].present? authorize! :rename_project, user_project if attrs[:name].present?
......
...@@ -44,6 +44,113 @@ describe 'Git LFS API and storage' do ...@@ -44,6 +44,113 @@ describe 'Git LFS API and storage' do
end end
end end
context 'project specific LFS settings' do
let(:project) { create(:empty_project) }
let(:body) do
{
'objects' => [
{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
'size' => 1575078
},
{ 'oid' => sample_oid,
'size' => sample_size
}
],
'operation' => 'upload'
}
end
let(:authorization) { authorize_user }
context 'with LFS disabled globally' do
before do
project.team << [user, :master]
allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 501 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(501)
end
it 'responds with a 501 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(501)
end
end
end
context 'with LFS enabled globally' do
before do
project.team << [user, :master]
enable_lfs
end
describe 'LFS disabled in project' do
before do
project.update_attribute(:lfs_enabled, false)
end
it 'responds with a 403 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
it 'responds with a 403 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(403)
expect(json_response).to include('message' => 'Access forbidden. Check your access level.')
end
end
describe 'LFS enabled in project' do
before do
project.update_attribute(:lfs_enabled, true)
end
it 'responds with a 200 message on upload' do
post_lfs_json "#{project.http_url_to_repo}/info/lfs/objects/batch", body, headers
expect(response).to have_http_status(200)
expect(json_response['objects'].first['size']).to eq(1575078)
end
it 'responds with a 200 message on download' do
get "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", nil, headers
expect(response).to have_http_status(200)
end
end
end
end
describe 'deprecated API' do describe 'deprecated API' do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
......
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