From 20037e61122a688366060f9427506962048e77ed Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 7 Jul 2016 21:03:21 +0800 Subject: Introduce Project#builds_for(build_name, ref = 'HEAD'): So that we could find the particular builds according to build_name and ref. It would be used to find the latest build artifacts from a particular branch or tag. --- app/models/commit_status.rb | 8 +++++++- app/models/project.rb | 8 ++++++++ spec/models/build_spec.rb | 12 ++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index e437e3417a8..6828705dbc8 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -16,7 +16,13 @@ class CommitStatus < ActiveRecord::Base alias_attribute :author, :user - scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :commit_id)) } + scope :latest, -> do + id = unscope(:select). + select("max(#{table_name}.id)"). + group(:name, :commit_id) + + where(id: id) + end scope :retried, -> { where.not(id: latest) } scope :ordered, -> { order(:name) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } diff --git a/app/models/project.rb b/app/models/project.rb index 029026a4e56..293dbd52359 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -429,6 +429,14 @@ class Project < ActiveRecord::Base repository.commit(id) end + def builds_for(build_name, ref = 'HEAD') + sha = commit(ref).sha + + builds.joins(:pipeline). + merge(Ci::Pipeline.where(sha: sha)). + where(name: build_name) + end + def merge_base_commit(first_commit_id, second_commit_id) sha = repository.merge_base(first_commit_id, second_commit_id) repository.commit(sha) if sha diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index e8171788872..8e3c9672fd5 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -673,7 +673,7 @@ describe Ci::Build, models: true do context 'when build is running' do before { build.run! } - it 'should return false' do + it 'returns false' do expect(build.retryable?).to be false end end @@ -681,9 +681,17 @@ describe Ci::Build, models: true do context 'when build is finished' do before { build.success! } - it 'should return true' do + it 'returns true' do expect(build.retryable?).to be true end end end + + describe 'Project#builds_for' do + it 'returns builds from ref and build name' do + latest_build = project.builds_for(build.name, 'HEAD').latest.first + + expect(latest_build.id).to eq(build.id) + end + end end -- cgit v1.2.1 From 1e3ff09cf3cd78755e83288559cfb1cf0ff6539f Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 7 Jul 2016 21:18:10 +0800 Subject: Avoid ambiguous syntax --- spec/models/build_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 8e3c9672fd5..cb432a99cd2 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -674,7 +674,7 @@ describe Ci::Build, models: true do before { build.run! } it 'returns false' do - expect(build.retryable?).to be false + expect(build.retryable?).to be(false) end end @@ -682,7 +682,7 @@ describe Ci::Build, models: true do before { build.success! } it 'returns true' do - expect(build.retryable?).to be true + expect(build.retryable?).to be(true) end end end -- cgit v1.2.1 From 8f469c33cc8b90e1bcae8ddd5599ce2a2957a3af Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 7 Jul 2016 21:18:54 +0800 Subject: Multiline for before block --- spec/models/build_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index cb432a99cd2..47ba4931460 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -671,7 +671,9 @@ describe Ci::Build, models: true do describe '#retryable?' do context 'when build is running' do - before { build.run! } + before do + build.run! + end it 'returns false' do expect(build.retryable?).to be(false) @@ -679,7 +681,9 @@ describe Ci::Build, models: true do end context 'when build is finished' do - before { build.success! } + before do + build.success! + end it 'returns true' do expect(build.retryable?).to be(true) -- cgit v1.2.1 From f601ec54fcfad7f365d3488c0a48575862c48958 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 18:17:32 +0800 Subject: Introduce Projects::ArtifactsController#search: So we redirect from ref and build_name to the particular build, namely: * /u/r/artifacts/ref/build_name/* -> /u/r/builds/:build_id/artifacts/* For: * download * browse * file --- app/controllers/projects/artifacts_controller.rb | 24 ++++++++++++++++++++++-- config/routes.rb | 6 ++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index f11c8321464..c00295cd3b5 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -35,14 +35,34 @@ class Projects::ArtifactsController < Projects::ApplicationController redirect_to namespace_project_build_path(project.namespace, project, build) end + def search + url = namespace_project_build_url(project.namespace, project, build) + + if params[:path] + redirect_to "#{url}/artifacts/#{params[:path]}" + else + render_404 + end + end + private def validate_artifacts! - render_404 unless build.artifacts? + render_404 unless build && build.artifacts? end def build - @build ||= project.builds.find_by!(id: params[:build_id]) + @build ||= build_from_id || build_from_ref + end + + def build_from_id + project.builds.find_by(id: params[:build_id]) if params[:build_id] + end + + def build_from_ref + if params[:ref] + project.builds_for(params[:build_name], params[:ref]).latest.first + end end def artifacts_file diff --git a/config/routes.rb b/config/routes.rb index 1572656b8c5..0a4b8609252 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -733,6 +733,12 @@ Rails.application.routes.draw do resources :environments, only: [:index, :show, :new, :create, :destroy] + resources :artifacts, only: [] do + collection do + get :search, path: ':ref/:build_name(/*path)', format: false + end + end + resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do collection do post :cancel_all -- cgit v1.2.1 From b14d40f0b0b46aba95d15b139345674c6a3dbd09 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 18:40:45 +0800 Subject: Handle branches with / in the name --- config/routes.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 0a4b8609252..5c1460b0e75 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -735,7 +735,8 @@ Rails.application.routes.draw do resources :artifacts, only: [] do collection do - get :search, path: ':ref/:build_name(/*path)', format: false + get :search, path: ':ref/:build_name/*path', format: false, + constraints: { ref: %r{.+(?=/)} } # ref could have / end end -- cgit v1.2.1 From ef833a220508f6f8a692b74e7fe593c68981d6f5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 18:51:23 +0800 Subject: Give latest succeeded one, don't give pending/running ones --- app/controllers/projects/artifacts_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index c00295cd3b5..f71499be4f7 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -61,7 +61,9 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref if params[:ref] - project.builds_for(params[:build_name], params[:ref]).latest.first + builds = project.builds_for(params[:build_name], params[:ref]) + + builds.latest.success.first end end -- cgit v1.2.1 From df5b78676e914ad8e14e96322b1dce383ae26875 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 19:06:14 +0800 Subject: Using plain if/else is much easier to understand --- app/controllers/projects/artifacts_controller.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index f71499be4f7..944fde11b09 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -5,11 +5,11 @@ class Projects::ArtifactsController < Projects::ApplicationController before_action :validate_artifacts! def download - unless artifacts_file.file_storage? - return redirect_to artifacts_file.url + if artifacts_file.file_storage? + send_file artifacts_file.path, disposition: 'attachment' + else + redirect_to artifacts_file.url end - - send_file artifacts_file.path, disposition: 'attachment' end def browse -- cgit v1.2.1 From 1f733a95c73ca767287fcad180e4fa367b4a2354 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 19:06:40 +0800 Subject: Remove redundant return --- app/controllers/projects/artifacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 944fde11b09..7d6ba80b965 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -16,7 +16,7 @@ class Projects::ArtifactsController < Projects::ApplicationController directory = params[:path] ? "#{params[:path]}/" : '' @entry = build.artifacts_metadata_entry(directory) - return render_404 unless @entry.exists? + render_404 unless @entry.exists? end def file -- cgit v1.2.1 From 2c646bb22593dc377c278622b35f79f1063725ad Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 11 Jul 2016 19:47:45 +0800 Subject: Move tests to respect to modules and extract artifacts tests --- spec/features/builds_spec.rb | 284 ------------------------------- spec/features/projects/artifacts_spec.rb | 31 ++++ spec/features/projects/builds_spec.rb | 267 +++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+), 284 deletions(-) delete mode 100644 spec/features/builds_spec.rb create mode 100644 spec/features/projects/artifacts_spec.rb create mode 100644 spec/features/projects/builds_spec.rb diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb deleted file mode 100644 index 16832c297ac..00000000000 --- a/spec/features/builds_spec.rb +++ /dev/null @@ -1,284 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } - - before do - login_as(:user) - @commit = FactoryGirl.create :ci_pipeline - @build = FactoryGirl.create :ci_build, pipeline: @commit - @build2 = FactoryGirl.create :ci_build - @project = @commit.project - @project.team << [@user, :developer] - end - - describe "GET /:project/builds" do - context "Running scope" do - before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project, scope: :running) - end - - it { expect(page).to have_selector('.nav-links li.active', text: 'Running') } - it { expect(page).to have_link 'Cancel running' } - it { expect(page).to have_content @build.short_sha } - it { expect(page).to have_content @build.ref } - it { expect(page).to have_content @build.name } - end - - context "Finished scope" do - before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) - end - - it { expect(page).to have_selector('.nav-links li.active', text: 'Finished') } - it { expect(page).to have_content 'No builds to show' } - it { expect(page).to have_link 'Cancel running' } - end - - context "All builds" do - before do - @project.builds.running_or_pending.each(&:success) - visit namespace_project_builds_path(@project.namespace, @project) - end - - it { expect(page).to have_selector('.nav-links li.active', text: 'All') } - it { expect(page).to have_content @build.short_sha } - it { expect(page).to have_content @build.ref } - it { expect(page).to have_content @build.name } - it { expect(page).not_to have_link 'Cancel running' } - end - end - - describe "POST /:project/builds/:id/cancel_all" do - before do - @build.run! - visit namespace_project_builds_path(@project.namespace, @project) - click_link "Cancel running" - end - - it { expect(page).to have_selector('.nav-links li.active', text: 'All') } - it { expect(page).to have_content 'canceled' } - it { expect(page).to have_content @build.short_sha } - it { expect(page).to have_content @build.ref } - it { expect(page).to have_content @build.name } - it { expect(page).not_to have_link 'Cancel running' } - end - - describe "GET /:project/builds/:id" do - context "Build from project" do - before do - visit namespace_project_build_path(@project.namespace, @project, @build) - end - - it { expect(page.status_code).to eq(200) } - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } - end - - context "Build from other project" do - before do - visit namespace_project_build_path(@project.namespace, @project, @build2) - end - - it { expect(page.status_code).to eq(404) } - end - - context "Download artifacts" do - before do - @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@project.namespace, @project, @build) - end - - it 'has button to download artifacts' do - expect(page).to have_content 'Download' - end - end - - context 'Artifacts expire date' do - before do - @build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at) - visit namespace_project_build_path(@project.namespace, @project, @build) - end - - context 'no expire date defined' do - let(:expire_at) { nil } - - it 'does not have the Keep button' do - expect(page).not_to have_content 'Keep' - end - end - - context 'when expire date is defined' do - let(:expire_at) { Time.now + 7.days } - - it 'keeps artifacts when Keep button is clicked' do - expect(page).to have_content 'The artifacts will be removed' - click_link 'Keep' - - expect(page).not_to have_link 'Keep' - expect(page).not_to have_content 'The artifacts will be removed' - end - end - - context 'when artifacts expired' do - let(:expire_at) { Time.now - 7.days } - - it 'does not have the Keep button' do - expect(page).to have_content 'The artifacts were removed' - expect(page).not_to have_link 'Keep' - end - end - end - - context 'Build raw trace' do - before do - @build.run! - @build.trace = 'BUILD TRACE' - visit namespace_project_build_path(@project.namespace, @project, @build) - end - - it do - expect(page).to have_link 'Raw' - end - end - end - - describe "POST /:project/builds/:id/cancel" do - context "Build from project" do - before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) - click_link "Cancel" - end - - it { expect(page.status_code).to eq(200) } - it { expect(page).to have_content 'canceled' } - it { expect(page).to have_content 'Retry' } - end - - context "Build from other project" do - before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) - page.driver.post(cancel_namespace_project_build_path(@project.namespace, @project, @build2)) - end - - it { expect(page.status_code).to eq(404) } - end - end - - describe "POST /:project/builds/:id/retry" do - context "Build from project" do - before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) - click_link 'Cancel' - click_link 'Retry' - end - - it { expect(page.status_code).to eq(200) } - it { expect(page).to have_content 'pending' } - it { expect(page).to have_content 'Cancel' } - end - - context "Build from other project" do - before do - @build.run! - visit namespace_project_build_path(@project.namespace, @project, @build) - click_link 'Cancel' - page.driver.post(retry_namespace_project_build_path(@project.namespace, @project, @build2)) - end - - it { expect(page.status_code).to eq(404) } - end - end - - describe "GET /:project/builds/:id/download" do - before do - @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@project.namespace, @project, @build) - click_link 'Download' - end - - context "Build from other project" do - before do - @build2.update_attributes(artifacts_file: artifacts_file) - visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2) - end - - it { expect(page.status_code).to eq(404) } - end - end - - describe "GET /:project/builds/:id/raw" do - context "Build from project" do - before do - Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build.run! - @build.trace = 'BUILD TRACE' - visit namespace_project_build_path(@project.namespace, @project, @build) - page.within('.js-build-sidebar') { click_link 'Raw' } - end - - it 'sends the right headers' do - expect(page.status_code).to eq(200) - expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') - expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) - end - end - - context "Build from other project" do - before do - Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') - @build2.run! - @build2.trace = 'BUILD TRACE' - visit raw_namespace_project_build_path(@project.namespace, @project, @build2) - puts page.status_code - puts current_url - end - - it 'sends the right headers' do - expect(page.status_code).to eq(404) - end - end - end - - describe "GET /:project/builds/:id/trace.json" do - context "Build from project" do - before do - visit trace_namespace_project_build_path(@project.namespace, @project, @build, format: :json) - end - - it { expect(page.status_code).to eq(200) } - end - - context "Build from other project" do - before do - visit trace_namespace_project_build_path(@project.namespace, @project, @build2, format: :json) - end - - it { expect(page.status_code).to eq(404) } - end - end - - describe "GET /:project/builds/:id/status" do - context "Build from project" do - before do - visit status_namespace_project_build_path(@project.namespace, @project, @build) - end - - it { expect(page.status_code).to eq(200) } - end - - context "Build from other project" do - before do - visit status_namespace_project_build_path(@project.namespace, @project, @build2) - end - - it { expect(page.status_code).to eq(404) } - end - end -end diff --git a/spec/features/projects/artifacts_spec.rb b/spec/features/projects/artifacts_spec.rb new file mode 100644 index 00000000000..fc6e2b07d40 --- /dev/null +++ b/spec/features/projects/artifacts_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe 'Artifacts' do + let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + + before do + login_as(:user) + @commit = FactoryGirl.create :ci_pipeline + @build = FactoryGirl.create :ci_build, pipeline: @commit + @build2 = FactoryGirl.create :ci_build + @project = @commit.project + @project.team << [@user, :developer] + end + + describe "GET /:project/builds/:id/artifacts/download" do + before do + @build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(@project.namespace, @project, @build) + click_link 'Download' + end + + context "Build from other project" do + before do + @build2.update_attributes(artifacts_file: artifacts_file) + visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2) + end + + it { expect(page.status_code).to eq(404) } + end + end +end diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb new file mode 100644 index 00000000000..25689f1c6e8 --- /dev/null +++ b/spec/features/projects/builds_spec.rb @@ -0,0 +1,267 @@ +require 'spec_helper' + +describe "Builds" do + let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + + before do + login_as(:user) + @commit = FactoryGirl.create :ci_pipeline + @build = FactoryGirl.create :ci_build, pipeline: @commit + @build2 = FactoryGirl.create :ci_build + @project = @commit.project + @project.team << [@user, :developer] + end + + describe "GET /:project/builds" do + context "Running scope" do + before do + @build.run! + visit namespace_project_builds_path(@project.namespace, @project, scope: :running) + end + + it { expect(page).to have_selector('.nav-links li.active', text: 'Running') } + it { expect(page).to have_link 'Cancel running' } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + end + + context "Finished scope" do + before do + @build.run! + visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) + end + + it { expect(page).to have_selector('.nav-links li.active', text: 'Finished') } + it { expect(page).to have_content 'No builds to show' } + it { expect(page).to have_link 'Cancel running' } + end + + context "All builds" do + before do + @project.builds.running_or_pending.each(&:success) + visit namespace_project_builds_path(@project.namespace, @project) + end + + it { expect(page).to have_selector('.nav-links li.active', text: 'All') } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + it { expect(page).not_to have_link 'Cancel running' } + end + end + + describe "POST /:project/builds/:id/cancel_all" do + before do + @build.run! + visit namespace_project_builds_path(@project.namespace, @project) + click_link "Cancel running" + end + + it { expect(page).to have_selector('.nav-links li.active', text: 'All') } + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + it { expect(page).not_to have_link 'Cancel running' } + end + + describe "GET /:project/builds/:id" do + context "Build from project" do + before do + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + it { expect(page.status_code).to eq(200) } + it { expect(page).to have_content @commit.sha[0..7] } + it { expect(page).to have_content @commit.git_commit_message } + it { expect(page).to have_content @commit.git_author_name } + end + + context "Build from other project" do + before do + visit namespace_project_build_path(@project.namespace, @project, @build2) + end + + it { expect(page.status_code).to eq(404) } + end + + context "Download artifacts" do + before do + @build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + it 'has button to download artifacts' do + expect(page).to have_content 'Download' + end + end + + context 'Artifacts expire date' do + before do + @build.update_attributes(artifacts_file: artifacts_file, artifacts_expire_at: expire_at) + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + context 'no expire date defined' do + let(:expire_at) { nil } + + it 'does not have the Keep button' do + expect(page).not_to have_content 'Keep' + end + end + + context 'when expire date is defined' do + let(:expire_at) { Time.now + 7.days } + + it 'keeps artifacts when Keep button is clicked' do + expect(page).to have_content 'The artifacts will be removed' + click_link 'Keep' + + expect(page).not_to have_link 'Keep' + expect(page).not_to have_content 'The artifacts will be removed' + end + end + + context 'when artifacts expired' do + let(:expire_at) { Time.now - 7.days } + + it 'does not have the Keep button' do + expect(page).to have_content 'The artifacts were removed' + expect(page).not_to have_link 'Keep' + end + end + end + + context 'Build raw trace' do + before do + @build.run! + @build.trace = 'BUILD TRACE' + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + it do + expect(page).to have_link 'Raw' + end + end + end + + describe "POST /:project/builds/:id/cancel" do + context "Build from project" do + before do + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + click_link "Cancel" + end + + it { expect(page.status_code).to eq(200) } + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content 'Retry' } + end + + context "Build from other project" do + before do + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + page.driver.post(cancel_namespace_project_build_path(@project.namespace, @project, @build2)) + end + + it { expect(page.status_code).to eq(404) } + end + end + + describe "POST /:project/builds/:id/retry" do + context "Build from project" do + before do + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + click_link 'Cancel' + click_link 'Retry' + end + + it { expect(page.status_code).to eq(200) } + it { expect(page).to have_content 'pending' } + it { expect(page).to have_content 'Cancel' } + end + + context "Build from other project" do + before do + @build.run! + visit namespace_project_build_path(@project.namespace, @project, @build) + click_link 'Cancel' + page.driver.post(retry_namespace_project_build_path(@project.namespace, @project, @build2)) + end + + it { expect(page.status_code).to eq(404) } + end + end + + describe "GET /:project/builds/:id/raw" do + context "Build from project" do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + @build.trace = 'BUILD TRACE' + visit namespace_project_build_path(@project.namespace, @project, @build) + page.within('.js-build-sidebar') { click_link 'Raw' } + end + + it 'sends the right headers' do + expect(page.status_code).to eq(200) + expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) + end + end + + context "Build from other project" do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build2.run! + @build2.trace = 'BUILD TRACE' + visit raw_namespace_project_build_path(@project.namespace, @project, @build2) + puts page.status_code + puts current_url + end + + it 'sends the right headers' do + expect(page.status_code).to eq(404) + end + end + end + + describe "GET /:project/builds/:id/trace.json" do + context "Build from project" do + before do + visit trace_namespace_project_build_path(@project.namespace, @project, @build, format: :json) + end + + it { expect(page.status_code).to eq(200) } + end + + context "Build from other project" do + before do + visit trace_namespace_project_build_path(@project.namespace, @project, @build2, format: :json) + end + + it { expect(page.status_code).to eq(404) } + end + end + + describe "GET /:project/builds/:id/status" do + context "Build from project" do + before do + visit status_namespace_project_build_path(@project.namespace, @project, @build) + end + + it { expect(page.status_code).to eq(200) } + end + + context "Build from other project" do + before do + visit status_namespace_project_build_path(@project.namespace, @project, @build2) + end + + it { expect(page.status_code).to eq(404) } + end + end +end -- cgit v1.2.1 From d0451a050d5c4a3d343077d0820451af5058636b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 18:56:41 +0800 Subject: Test for Project#builds_for and return empty array for nothing --- app/models/project.rb | 3 +++ spec/models/project_spec.rb | 16 +++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index f3266a1b197..35ffb0a415d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -430,6 +430,9 @@ class Project < ActiveRecord::Base end def builds_for(build_name, ref = 'HEAD') + ct = commit(ref) + return [] unless ct + sha = commit(ref).sha builds.joins(:pipeline). diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5a27ccbab0a..06d99240708 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -359,11 +359,25 @@ describe Project, models: true do describe :repository do let(:project) { create(:project) } - it 'should return valid repo' do + it 'returns valid repo' do expect(project.repository).to be_kind_of(Repository) end end + describe '#builds_for' do + let(:project) { create(:project) } + let(:pipeline) do + create(:ci_pipeline, project: project, sha: project.commit.sha) + end + let(:build) { create(:ci_build, pipeline: pipeline) } + + it 'returns builds for a particular ref' do + build_ids = project.builds_for(build.name, build.sha).map(&:id) + + expect(build_ids).to eq([build.id]) + end + end + describe :default_issues_tracker? do let(:project) { create(:project) } let(:ext_project) { create(:redmine_project) } -- cgit v1.2.1 From 9604f12331ab0ce3a14b1780cc1245eb8df33038 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 19:14:44 +0800 Subject: Conforming the style --- spec/features/projects/artifacts_spec.rb | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/spec/features/projects/artifacts_spec.rb b/spec/features/projects/artifacts_spec.rb index fc6e2b07d40..5e60c2f3074 100644 --- a/spec/features/projects/artifacts_spec.rb +++ b/spec/features/projects/artifacts_spec.rb @@ -2,27 +2,33 @@ require 'spec_helper' describe 'Artifacts' do let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:pipeline) { create(:ci_pipeline) } + let(:build) { create(:ci_build, pipeline: pipeline) } + let(:build2) { create(:ci_build) } + let(:project) { pipeline.project } before do login_as(:user) - @commit = FactoryGirl.create :ci_pipeline - @build = FactoryGirl.create :ci_build, pipeline: @commit - @build2 = FactoryGirl.create :ci_build - @project = @commit.project - @project.team << [@user, :developer] + project.team << [@user, :developer] end - describe "GET /:project/builds/:id/artifacts/download" do + describe 'GET /:project/builds/:id/artifacts/download' do before do - @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@project.namespace, @project, @build) + build.update_attributes(artifacts_file: artifacts_file) + + visit namespace_project_build_path(project.namespace, project, build) + click_link 'Download' end - context "Build from other project" do + context 'Build from other project' do before do - @build2.update_attributes(artifacts_file: artifacts_file) - visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2) + build2.update_attributes(artifacts_file: artifacts_file) + + visit download_namespace_project_build_artifacts_path( + project.namespace, + project, + build2) end it { expect(page.status_code).to eq(404) } -- cgit v1.2.1 From a1eac5e4de95a4d27b30432c527ab410e9d77787 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 19:20:31 +0800 Subject: Doh. I already wrote that test and I forgot. --- spec/models/build_spec.rb | 4 ++-- spec/models/project_spec.rb | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 47ba4931460..c7c247189f5 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -693,9 +693,9 @@ describe Ci::Build, models: true do describe 'Project#builds_for' do it 'returns builds from ref and build name' do - latest_build = project.builds_for(build.name, 'HEAD').latest.first + build_ids = project.builds_for(build.name, 'HEAD').map(&:id) - expect(latest_build.id).to eq(build.id) + expect(build_ids).to eq([build.id]) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 06d99240708..143fd5167a4 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -364,20 +364,6 @@ describe Project, models: true do end end - describe '#builds_for' do - let(:project) { create(:project) } - let(:pipeline) do - create(:ci_pipeline, project: project, sha: project.commit.sha) - end - let(:build) { create(:ci_build, pipeline: pipeline) } - - it 'returns builds for a particular ref' do - build_ids = project.builds_for(build.name, build.sha).map(&:id) - - expect(build_ids).to eq([build.id]) - end - end - describe :default_issues_tracker? do let(:project) { create(:project) } let(:ext_project) { create(:redmine_project) } -- cgit v1.2.1 From 6597c213c341ae072216c125a97f94a174fc3dfa Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 19:28:21 +0800 Subject: Prefer empty relation rather than arrays --- app/models/project.rb | 2 +- spec/models/build_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 35ffb0a415d..bc15f8c4138 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,7 +431,7 @@ class Project < ActiveRecord::Base def builds_for(build_name, ref = 'HEAD') ct = commit(ref) - return [] unless ct + return builds.none unless ct sha = commit(ref).sha diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index c7c247189f5..b1354faa722 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -697,5 +697,11 @@ describe Ci::Build, models: true do expect(build_ids).to eq([build.id]) end + + it 'returns empty relation if the build cannot be found' do + builds = project.builds_for(build.name, 'TAIL').all + + expect(builds).to be_empty + end end end -- cgit v1.2.1 From c94cff3e13d3f5ab55816ba2e84f48a659462441 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 19:29:59 +0800 Subject: Prefer if over return --- app/models/project.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index bc15f8c4138..366817079bb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,13 +431,17 @@ class Project < ActiveRecord::Base def builds_for(build_name, ref = 'HEAD') ct = commit(ref) - return builds.none unless ct - sha = commit(ref).sha + if ct.nil? + builds.none - builds.joins(:pipeline). - merge(Ci::Pipeline.where(sha: sha)). - where(name: build_name) + else + sha = ct.sha + + builds.joins(:pipeline). + merge(Ci::Pipeline.where(sha: sha)). + where(name: build_name) + end end def merge_base_commit(first_commit_id, second_commit_id) -- cgit v1.2.1 From a96e9aa0d3f703e61a415d2b0532f3a96d90ba51 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 20:17:55 +0800 Subject: Make rubocop happy --- spec/features/projects/artifacts_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/projects/artifacts_spec.rb b/spec/features/projects/artifacts_spec.rb index 5e60c2f3074..f5356a8b701 100644 --- a/spec/features/projects/artifacts_spec.rb +++ b/spec/features/projects/artifacts_spec.rb @@ -26,9 +26,9 @@ describe 'Artifacts' do build2.update_attributes(artifacts_file: artifacts_file) visit download_namespace_project_build_artifacts_path( - project.namespace, - project, - build2) + project.namespace, + project, + build2) end it { expect(page.status_code).to eq(404) } -- cgit v1.2.1 From e383254070baf8a4701e3a10b7cc699f03cefff4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 23:15:08 +0800 Subject: Add all the tests and fix stuffs along the way: It turns out they are different: builds.success.latest.first and builds.latest.success.first If we put success first, that latest would also filter via success, and which is what we want here. --- app/controllers/projects/artifacts_controller.rb | 2 +- config/routes.rb | 2 +- .../requests/projects/artifacts_controller_spec.rb | 145 +++++++++++++++++++++ spec/spec_helper.rb | 6 +- 4 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 spec/requests/projects/artifacts_controller_spec.rb diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 7d6ba80b965..25a1c2ca7e1 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -63,7 +63,7 @@ class Projects::ArtifactsController < Projects::ApplicationController if params[:ref] builds = project.builds_for(params[:build_name], params[:ref]) - builds.latest.success.first + builds.success.latest.first end end diff --git a/config/routes.rb b/config/routes.rb index 10b497d05a0..32b00652bb3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -736,7 +736,7 @@ Rails.application.routes.draw do resources :artifacts, only: [] do collection do get :search, path: ':ref/:build_name/*path', format: false, - constraints: { ref: %r{.+(?=/)} } # ref could have / + constraints: { ref: /.+/ } # ref could have / end end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb new file mode 100644 index 00000000000..b11eb1aedcc --- /dev/null +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -0,0 +1,145 @@ +require 'spec_helper' + +describe Projects::ArtifactsController do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:pipeline) do + create(:ci_pipeline, project: project, sha: project.commit('fix').sha) + end + let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + before do + login_as(user) + project.team << [user, :developer] + end + + describe 'GET /:project/artifacts/:ref/:build_name/browse' do + context '404' do + it 'has no such ref' do + path = search_namespace_project_artifacts_path( + project.namespace, + project, + 'TAIL', + build.name, + 'browse') + + get path + + expect(response.status).to eq(404) + end + + it 'has no such build' do + get search_namespace_project_artifacts_path( + project.namespace, + project, + pipeline.sha, + 'NOBUILD', + 'browse') + + expect(response.status).to eq(404) + end + + it 'has no path' do + get search_namespace_project_artifacts_path( + project.namespace, + project, + pipeline.sha, + build.name, + '') + + expect(response.status).to eq(404) + end + end + + context '302' do + def path_from_sha + search_namespace_project_artifacts_path( + project.namespace, + project, + pipeline.sha, + build.name, + 'browse') + end + + shared_examples 'redirect to the build' do + it 'redirects' do + path = browse_namespace_project_build_artifacts_path( + project.namespace, + project, + build) + + expect(response).to redirect_to(path) + end + end + + context 'with sha' do + before do + get path_from_sha + end + + it_behaves_like 'redirect to the build' + end + + context 'with regular branch' do + before do + pipeline.update(sha: project.commit('master').sha) + end + + before do + get search_namespace_project_artifacts_path( + project.namespace, + project, + 'master', + build.name, + 'browse') + end + + it_behaves_like 'redirect to the build' + end + + context 'with branch name containing slash' do + before do + pipeline.update(sha: project.commit('improve/awesome').sha) + end + + before do + get search_namespace_project_artifacts_path( + project.namespace, + project, + 'improve/awesome', + build.name, + 'browse') + end + + it_behaves_like 'redirect to the build' + end + + context 'with latest build' do + before do + 3.times do # creating some old builds + create(:ci_build, :success, :artifacts, pipeline: pipeline) + end + end + + before do + get path_from_sha + end + + it_behaves_like 'redirect to the build' + end + + context 'with success build' do + before do + build # make sure build was old, but still the latest success one + create(:ci_build, :pending, :artifacts, pipeline: pipeline) + end + + before do + get path_from_sha + end + + it_behaves_like 'redirect to the build' + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 606da1b7605..62097de2768 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -28,9 +28,9 @@ RSpec.configure do |config| config.verbose_retry = true config.display_try_failure_messages = true - config.include Devise::TestHelpers, type: :controller - config.include LoginHelpers, type: :feature - config.include LoginHelpers, type: :request + config.include Devise::TestHelpers, type: :controller + config.include Warden::Test::Helpers, type: :request + config.include LoginHelpers, type: :feature config.include StubConfiguration config.include EmailHelpers config.include RelativeUrl, type: feature -- cgit v1.2.1 From 57c72cb0dfc980106163b313f850c5b8a5e31a70 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 23:26:30 +0800 Subject: Could be faster when params[:path] is missing --- app/controllers/projects/artifacts_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 25a1c2ca7e1..bf9d72ae90c 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -36,9 +36,9 @@ class Projects::ArtifactsController < Projects::ApplicationController end def search - url = namespace_project_build_url(project.namespace, project, build) - if params[:path] + url = namespace_project_build_url(project.namespace, project, build) + redirect_to "#{url}/artifacts/#{params[:path]}" else render_404 -- cgit v1.2.1 From d0b9112fefcf0ee01d9df2dd9c2f1076738a53f1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 12 Jul 2016 23:28:41 +0800 Subject: save some lines and a local variable --- app/models/project.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 366817079bb..2a9d68d10d2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -436,10 +436,8 @@ class Project < ActiveRecord::Base builds.none else - sha = ct.sha - builds.joins(:pipeline). - merge(Ci::Pipeline.where(sha: sha)). + merge(Ci::Pipeline.where(sha: ct.sha)). where(name: build_name) end end -- cgit v1.2.1 From 3336828152767bf22f9c59d54c1c13f5e3d88132 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 17:49:42 +0800 Subject: No need for a separate line now --- spec/requests/projects/artifacts_controller_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index b11eb1aedcc..2ab1c1ac428 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -16,15 +16,13 @@ describe Projects::ArtifactsController do describe 'GET /:project/artifacts/:ref/:build_name/browse' do context '404' do it 'has no such ref' do - path = search_namespace_project_artifacts_path( + get search_namespace_project_artifacts_path( project.namespace, project, 'TAIL', build.name, 'browse') - get path - expect(response.status).to eq(404) end -- cgit v1.2.1 From bb5f06718cd4fb344398a5fef19f3cf9b400de97 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 18:19:29 +0800 Subject: Rename to ref_name so it's aligning with API --- app/controllers/projects/artifacts_controller.rb | 4 ++-- config/routes.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index bf9d72ae90c..f1370efd64e 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -60,8 +60,8 @@ class Projects::ArtifactsController < Projects::ApplicationController end def build_from_ref - if params[:ref] - builds = project.builds_for(params[:build_name], params[:ref]) + if params[:ref_name] + builds = project.builds_for(params[:build_name], params[:ref_name]) builds.success.latest.first end diff --git a/config/routes.rb b/config/routes.rb index 32b00652bb3..203f679226e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -735,8 +735,8 @@ Rails.application.routes.draw do resources :artifacts, only: [] do collection do - get :search, path: ':ref/:build_name/*path', format: false, - constraints: { ref: /.+/ } # ref could have / + get :search, path: ':ref_name/:build_name/*path', format: false, + constraints: { ref_name: /.+/ } # ref could have / end end -- cgit v1.2.1 From 6c80b597f58aaaca514e45a7e83b811db301e651 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 21:44:27 +0800 Subject: Introduce path_from_ref and save some typing --- .../requests/projects/artifacts_controller_spec.rb | 26 +++++++--------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 2ab1c1ac428..4c4bacfcbda 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -50,12 +50,12 @@ describe Projects::ArtifactsController do end context '302' do - def path_from_sha + def path_from_ref(ref = pipeline.sha, build_name = build.name) search_namespace_project_artifacts_path( project.namespace, project, - pipeline.sha, - build.name, + ref, + build_name, 'browse') end @@ -72,7 +72,7 @@ describe Projects::ArtifactsController do context 'with sha' do before do - get path_from_sha + get path_from_ref end it_behaves_like 'redirect to the build' @@ -84,12 +84,7 @@ describe Projects::ArtifactsController do end before do - get search_namespace_project_artifacts_path( - project.namespace, - project, - 'master', - build.name, - 'browse') + get path_from_ref('master') end it_behaves_like 'redirect to the build' @@ -101,12 +96,7 @@ describe Projects::ArtifactsController do end before do - get search_namespace_project_artifacts_path( - project.namespace, - project, - 'improve/awesome', - build.name, - 'browse') + get path_from_ref('improve/awesome') end it_behaves_like 'redirect to the build' @@ -120,7 +110,7 @@ describe Projects::ArtifactsController do end before do - get path_from_sha + get path_from_ref end it_behaves_like 'redirect to the build' @@ -133,7 +123,7 @@ describe Projects::ArtifactsController do end before do - get path_from_sha + get path_from_ref end it_behaves_like 'redirect to the build' -- cgit v1.2.1 From 8735d95af16a6066e9f256a62f401d02c2c7e108 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 23:05:57 +0800 Subject: Implement API for downloading artifacts from ref and build name: Basically: GET /api/projects/:id/artifacts/:ref_name/:build_name Also added tests for it. --- lib/api/api.rb | 1 + lib/api/artifacts.rb | 34 ++++++ spec/requests/api/artifacts_spec.rb | 52 +++++++++ .../requests/projects/artifacts_controller_spec.rb | 122 ++++----------------- spec/requests/shared/artifacts_context.rb | 78 +++++++++++++ 5 files changed, 188 insertions(+), 99 deletions(-) create mode 100644 lib/api/artifacts.rb create mode 100644 spec/requests/api/artifacts_spec.rb create mode 100644 spec/requests/shared/artifacts_context.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index 3d7d67510a8..f18258bf5a3 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -26,6 +26,7 @@ module API # Ensure the namespace is right, otherwise we might load Grape::API::Helpers helpers ::API::Helpers + mount ::API::Artifacts mount ::API::AwardEmoji mount ::API::Branches mount ::API::Builds diff --git a/lib/api/artifacts.rb b/lib/api/artifacts.rb new file mode 100644 index 00000000000..6ce2bed8260 --- /dev/null +++ b/lib/api/artifacts.rb @@ -0,0 +1,34 @@ +module API + # Projects artifacts API + class Artifacts < Grape::API + before do + authenticate! + authorize!(:read_build, user_project) + end + + resource :projects do + # Download the artifacts file from ref_name and build_name + # + # Parameters: + # id (required) - The ID of a project + # ref_name (required) - The ref from repository + # build_name (required) - The name for the build + # Example Request: + # GET /projects/:id/artifacts/:ref_name/:build_name + get ':id/artifacts/:ref_name/:build_name', + requirements: { ref_name: /.+/ } do + builds = user_project.builds_for( + params[:build_name], params[:ref_name]) + + latest_build = builds.success.latest.first + + if latest_build + redirect( + "/projects/#{user_project.id}/builds/#{latest_build.id}/artifacts") + else + not_found! + end + end + end + end +end diff --git a/spec/requests/api/artifacts_spec.rb b/spec/requests/api/artifacts_spec.rb new file mode 100644 index 00000000000..2b84c1a2072 --- /dev/null +++ b/spec/requests/api/artifacts_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' +require_relative '../shared/artifacts_context' + +describe API::API, api: true do + include ApiHelpers + + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:pipeline) do + create(:ci_pipeline, project: project, sha: project.commit('fix').sha) + end + let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + before do + project.team << [user, :developer] + end + + describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do + def path_from_ref(ref = pipeline.sha, build_name = build.name, _ = '') + api("/projects/#{project.id}/artifacts/#{ref}/#{build_name}", user) + end + + context '401' do + let(:user) { nil } + + before do + get path_from_ref + end + + it 'gives 401 for unauthorized user' do + expect(response).to have_http_status(401) + end + end + + context '404' do + def verify + expect(response).to have_http_status(404) + end + + it_behaves_like 'artifacts from ref with 404' + end + + context '302' do + def verify + expect(response).to redirect_to( + "/projects/#{project.id}/builds/#{build.id}/artifacts") + end + + it_behaves_like 'artifacts from ref with 302' + end + end +end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 4c4bacfcbda..9722a9a1d64 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require_relative '../shared/artifacts_context' describe Projects::ArtifactsController do let(:user) { create(:user) } @@ -14,120 +15,43 @@ describe Projects::ArtifactsController do end describe 'GET /:project/artifacts/:ref/:build_name/browse' do - context '404' do - it 'has no such ref' do - get search_namespace_project_artifacts_path( - project.namespace, - project, - 'TAIL', - build.name, - 'browse') + def path_from_ref(ref = pipeline.sha, build_name = build.name, + path = 'browse') + search_namespace_project_artifacts_path( + project.namespace, + project, + ref, + build_name, + path) + end + context '404' do + def verify expect(response.status).to eq(404) end - it 'has no such build' do - get search_namespace_project_artifacts_path( - project.namespace, - project, - pipeline.sha, - 'NOBUILD', - 'browse') - - expect(response.status).to eq(404) - end + it_behaves_like 'artifacts from ref with 404' - it 'has no path' do - get search_namespace_project_artifacts_path( - project.namespace, - project, - pipeline.sha, - build.name, - '') + context 'has no path' do + before do + get path_from_ref(pipeline.sha, build.name, '') + end - expect(response.status).to eq(404) + it('gives 404') { verify } end end context '302' do - def path_from_ref(ref = pipeline.sha, build_name = build.name) - search_namespace_project_artifacts_path( + def verify + path = browse_namespace_project_build_artifacts_path( project.namespace, project, - ref, - build_name, - 'browse') - end - - shared_examples 'redirect to the build' do - it 'redirects' do - path = browse_namespace_project_build_artifacts_path( - project.namespace, - project, - build) - - expect(response).to redirect_to(path) - end - end - - context 'with sha' do - before do - get path_from_ref - end - - it_behaves_like 'redirect to the build' - end - - context 'with regular branch' do - before do - pipeline.update(sha: project.commit('master').sha) - end - - before do - get path_from_ref('master') - end - - it_behaves_like 'redirect to the build' - end - - context 'with branch name containing slash' do - before do - pipeline.update(sha: project.commit('improve/awesome').sha) - end + build) - before do - get path_from_ref('improve/awesome') - end - - it_behaves_like 'redirect to the build' + expect(response).to redirect_to(path) end - context 'with latest build' do - before do - 3.times do # creating some old builds - create(:ci_build, :success, :artifacts, pipeline: pipeline) - end - end - - before do - get path_from_ref - end - - it_behaves_like 'redirect to the build' - end - - context 'with success build' do - before do - build # make sure build was old, but still the latest success one - create(:ci_build, :pending, :artifacts, pipeline: pipeline) - end - - before do - get path_from_ref - end - - it_behaves_like 'redirect to the build' - end + it_behaves_like 'artifacts from ref with 302' end end end diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb new file mode 100644 index 00000000000..4333be6e1cd --- /dev/null +++ b/spec/requests/shared/artifacts_context.rb @@ -0,0 +1,78 @@ +shared_context 'artifacts from ref with 404' do + context 'has no such ref' do + before do + get path_from_ref('TAIL', build.name) + end + + it('gives 404') { verify } + end + + context 'has no such build' do + before do + get path_from_ref(pipeline.sha, 'NOBUILD') + end + + it('gives 404') { verify } + end +end + +shared_context 'artifacts from ref with 302' do + context 'with sha' do + before do + get path_from_ref + end + + it('redirects') { verify } + end + + context 'with regular branch' do + before do + pipeline.update(sha: project.commit('master').sha) + end + + before do + get path_from_ref('master') + end + + it('redirects') { verify } + end + + context 'with branch name containing slash' do + before do + pipeline.update(sha: project.commit('improve/awesome').sha) + end + + before do + get path_from_ref('improve/awesome') + end + + it('redirects') { verify } + end + + context 'with latest build' do + before do + 3.times do # creating some old builds + create(:ci_build, :success, :artifacts, pipeline: pipeline) + end + end + + before do + get path_from_ref + end + + it('redirects') { verify } + end + + context 'with success build' do + before do + build # make sure build was old, but still the latest success one + create(:ci_build, :pending, :artifacts, pipeline: pipeline) + end + + before do + get path_from_ref + end + + it('redirects') { verify } + end +end -- cgit v1.2.1 From b043729d4959ab5cbfd4aff02ce8c8c4c8a9d26f Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 23:23:05 +0800 Subject: Share more stuffs --- spec/requests/api/artifacts_spec.rb | 13 ++----------- spec/requests/projects/artifacts_controller_spec.rb | 16 +++++----------- spec/requests/shared/artifacts_context.rb | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/spec/requests/api/artifacts_spec.rb b/spec/requests/api/artifacts_spec.rb index 2b84c1a2072..393b8f85402 100644 --- a/spec/requests/api/artifacts_spec.rb +++ b/spec/requests/api/artifacts_spec.rb @@ -4,18 +4,9 @@ require_relative '../shared/artifacts_context' describe API::API, api: true do include ApiHelpers - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:pipeline) do - create(:ci_pipeline, project: project, sha: project.commit('fix').sha) - end - let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } - - before do - project.team << [user, :developer] - end - describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do + include_context 'artifacts from ref and build name' + def path_from_ref(ref = pipeline.sha, build_name = build.name, _ = '') api("/projects/#{project.id}/artifacts/#{ref}/#{build_name}", user) end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 9722a9a1d64..26f6ee8d252 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -2,19 +2,13 @@ require 'spec_helper' require_relative '../shared/artifacts_context' describe Projects::ArtifactsController do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:pipeline) do - create(:ci_pipeline, project: project, sha: project.commit('fix').sha) - end - let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + describe 'GET /:project/artifacts/:ref/:build_name/browse' do + include_context 'artifacts from ref and build name' - before do - login_as(user) - project.team << [user, :developer] - end + before do + login_as(user) + end - describe 'GET /:project/artifacts/:ref/:build_name/browse' do def path_from_ref(ref = pipeline.sha, build_name = build.name, path = 'browse') search_namespace_project_artifacts_path( diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb index 4333be6e1cd..03f7f248773 100644 --- a/spec/requests/shared/artifacts_context.rb +++ b/spec/requests/shared/artifacts_context.rb @@ -1,4 +1,17 @@ -shared_context 'artifacts from ref with 404' do +shared_context 'artifacts from ref and build name' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:pipeline) do + create(:ci_pipeline, project: project, sha: project.commit('fix').sha) + end + let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + + before do + project.team << [user, :developer] + end +end + +shared_examples 'artifacts from ref with 404' do context 'has no such ref' do before do get path_from_ref('TAIL', build.name) @@ -16,7 +29,7 @@ shared_context 'artifacts from ref with 404' do end end -shared_context 'artifacts from ref with 302' do +shared_examples 'artifacts from ref with 302' do context 'with sha' do before do get path_from_ref -- cgit v1.2.1 From 5b227f351f8b3568e948d705fb1e2bb571cfdcdd Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 13 Jul 2016 23:39:03 +0800 Subject: rubocop likes this better --- spec/requests/projects/artifacts_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 26f6ee8d252..d44901289d8 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -9,8 +9,8 @@ describe Projects::ArtifactsController do login_as(user) end - def path_from_ref(ref = pipeline.sha, build_name = build.name, - path = 'browse') + def path_from_ref( + ref = pipeline.sha, build_name = build.name, path = 'browse') search_namespace_project_artifacts_path( project.namespace, project, -- cgit v1.2.1 From 66b91ce9ac70025093d52247ecfaa3f47536809f Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 14:24:17 +0800 Subject: Avoid shadowing CommitStatus#id, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13047163 --- app/models/commit_status.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 6828705dbc8..baabbd785cc 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -17,11 +17,11 @@ class CommitStatus < ActiveRecord::Base alias_attribute :author, :user scope :latest, -> do - id = unscope(:select). + max_id = unscope(:select). select("max(#{table_name}.id)"). group(:name, :commit_id) - where(id: id) + where(id: max_id) end scope :retried, -> { where.not(id: latest) } scope :ordered, -> { order(:name) } -- cgit v1.2.1 From 5544852825d637dfe24b53e93b1e95d21767783c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 14:25:07 +0800 Subject: Remove blank line between if/else clause, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13047184 --- app/models/project.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 2a9d68d10d2..48bb9743439 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -434,7 +434,6 @@ class Project < ActiveRecord::Base if ct.nil? builds.none - else builds.joins(:pipeline). merge(Ci::Pipeline.where(sha: ct.sha)). -- cgit v1.2.1 From fab8bc5313a56c5f22e56903de2cb9c86df79fe4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 14:26:04 +0800 Subject: More descriptive local variable, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13047190 --- app/models/project.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 48bb9743439..793cf2d70fb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -430,13 +430,13 @@ class Project < ActiveRecord::Base end def builds_for(build_name, ref = 'HEAD') - ct = commit(ref) + commit_object = commit(ref) - if ct.nil? + if commit_object.nil? builds.none else builds.joins(:pipeline). - merge(Ci::Pipeline.where(sha: ct.sha)). + merge(Ci::Pipeline.where(sha: commit_object.sha)). where(name: build_name) end end -- cgit v1.2.1 From 1c7871e92f679f65e5b5d065d7478dd2e77f9b77 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 14:39:26 +0800 Subject: Introduce get_build! so we could omit one early return --- lib/api/builds.rb | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index d36047acd1f..f6e96ee7f3a 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -52,8 +52,7 @@ module API get ':id/builds/:build_id' do authorize_read_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) present build, with: Entities::Build, user_can_download_artifacts: can?(current_user, :read_build, user_project) @@ -69,8 +68,7 @@ module API get ':id/builds/:build_id/artifacts' do authorize_read_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) artifacts_file = build.artifacts_file @@ -97,8 +95,7 @@ module API get ':id/builds/:build_id/trace' do authorize_read_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" content_type 'text/plain' @@ -118,8 +115,7 @@ module API post ':id/builds/:build_id/cancel' do authorize_update_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) build.cancel @@ -137,8 +133,7 @@ module API post ':id/builds/:build_id/retry' do authorize_update_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) return forbidden!('Build is not retryable') unless build.retryable? build = Ci::Build.retry(build, current_user) @@ -157,8 +152,7 @@ module API post ':id/builds/:build_id/erase' do authorize_update_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build + build = get_build!(params[:build_id]) return forbidden!('Build is not erasable!') unless build.erasable? build.erase(erased_by: current_user) @@ -176,8 +170,8 @@ module API post ':id/builds/:build_id/artifacts/keep' do authorize_update_builds! - build = get_build(params[:build_id]) - return not_found!(build) unless build && build.artifacts? + build = get_build!(params[:build_id]) + return not_found!(build) unless build.artifacts? build.keep_artifacts! @@ -192,6 +186,10 @@ module API user_project.builds.find_by(id: id.to_i) end + def get_build!(id) + get_build(id) || not_found! + end + def filter_builds(builds, scope) return builds if scope.nil? || scope.empty? -- cgit v1.2.1 From e01c421b911a46774f8c5be92d383d8da14750c3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 16:36:09 +0800 Subject: Prefer if so it's more clear what's going on --- lib/api/builds.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index f6e96ee7f3a..b3b28541382 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -69,16 +69,17 @@ module API authorize_read_builds! build = get_build!(params[:build_id]) - artifacts_file = build.artifacts_file - unless artifacts_file.file_storage? - return redirect_to build.artifacts_file.url - end + if !artifacts_file.file_storage? + redirect_to(build.artifacts_file.url) - return not_found! unless artifacts_file.exists? + elsif artifacts_file.exists? + present_file!(artifacts_file.path, artifacts_file.filename) - present_file!(artifacts_file.path, artifacts_file.filename) + else + not_found! + end end # Get a trace of a specific build of a project -- cgit v1.2.1 From d7bbee7593ee54a9685c9eded00b121cca3913be Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 16:45:46 +0800 Subject: Update routes based on feedback from: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13058785 And note that job/build_name could contain `/` --- app/controllers/projects/artifacts_controller.rb | 2 +- config/routes.rb | 15 ++++++++------- spec/requests/projects/artifacts_controller_spec.rb | 8 ++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index f1370efd64e..3e487c24cbd 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -61,7 +61,7 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref if params[:ref_name] - builds = project.builds_for(params[:build_name], params[:ref_name]) + builds = project.builds_for(params[:job], params[:ref_name]) builds.success.latest.first end diff --git a/config/routes.rb b/config/routes.rb index 203f679226e..ea6465038df 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -733,16 +733,17 @@ Rails.application.routes.draw do resources :environments, only: [:index, :show, :new, :create, :destroy] - resources :artifacts, only: [] do - collection do - get :search, path: ':ref_name/:build_name/*path', format: false, - constraints: { ref_name: /.+/ } # ref could have / - end - end - resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do collection do post :cancel_all + + resources :artifacts, only: [] do + collection do + get :search, path: ':ref_name/*path', + format: false, + constraints: { ref_name: /.+/ } # could have / + end + end end member do diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index d44901289d8..574b7028617 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require_relative '../shared/artifacts_context' describe Projects::ArtifactsController do - describe 'GET /:project/artifacts/:ref/:build_name/browse' do + describe 'GET /:project/builds/artifacts/:ref_name/browse?job=name' do include_context 'artifacts from ref and build name' before do @@ -10,13 +10,13 @@ describe Projects::ArtifactsController do end def path_from_ref( - ref = pipeline.sha, build_name = build.name, path = 'browse') + ref = pipeline.sha, job = build.name, path = 'browse') search_namespace_project_artifacts_path( project.namespace, project, ref, - build_name, - path) + path, + job: job) end context '404' do -- cgit v1.2.1 From a9a8ceebcbe25cbe27bebe9fc63ab364b1dd41ee Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 17:35:54 +0800 Subject: Merge features/projects/artifacts_spec.rb back It doesn't make too much sense having this standalone --- spec/features/projects/artifacts_spec.rb | 37 -------------------------------- spec/features/projects/builds_spec.rb | 17 +++++++++++++++ 2 files changed, 17 insertions(+), 37 deletions(-) delete mode 100644 spec/features/projects/artifacts_spec.rb diff --git a/spec/features/projects/artifacts_spec.rb b/spec/features/projects/artifacts_spec.rb deleted file mode 100644 index f5356a8b701..00000000000 --- a/spec/features/projects/artifacts_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'spec_helper' - -describe 'Artifacts' do - let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } - let(:pipeline) { create(:ci_pipeline) } - let(:build) { create(:ci_build, pipeline: pipeline) } - let(:build2) { create(:ci_build) } - let(:project) { pipeline.project } - - before do - login_as(:user) - project.team << [@user, :developer] - end - - describe 'GET /:project/builds/:id/artifacts/download' do - before do - build.update_attributes(artifacts_file: artifacts_file) - - visit namespace_project_build_path(project.namespace, project, build) - - click_link 'Download' - end - - context 'Build from other project' do - before do - build2.update_attributes(artifacts_file: artifacts_file) - - visit download_namespace_project_build_artifacts_path( - project.namespace, - project, - build2) - end - - it { expect(page.status_code).to eq(404) } - end - end -end diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index 25689f1c6e8..16832c297ac 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -196,6 +196,23 @@ describe "Builds" do end end + describe "GET /:project/builds/:id/download" do + before do + @build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(@project.namespace, @project, @build) + click_link 'Download' + end + + context "Build from other project" do + before do + @build2.update_attributes(artifacts_file: artifacts_file) + visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build2) + end + + it { expect(page.status_code).to eq(404) } + end + end + describe "GET /:project/builds/:id/raw" do context "Build from project" do before do -- cgit v1.2.1 From 70f508f5d48a3541a430539d7f8b41dfa99127a1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 18:50:34 +0800 Subject: Serve artifacts from Builds --- lib/api/api.rb | 1 - lib/api/artifacts.rb | 34 ---------------------------------- lib/api/builds.rb | 35 +++++++++++++++++++++++++++++++---- spec/requests/api/artifacts_spec.rb | 6 +++--- spec/requests/api/builds_spec.rb | 2 +- 5 files changed, 35 insertions(+), 43 deletions(-) delete mode 100644 lib/api/artifacts.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index f18258bf5a3..3d7d67510a8 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -26,7 +26,6 @@ module API # Ensure the namespace is right, otherwise we might load Grape::API::Helpers helpers ::API::Helpers - mount ::API::Artifacts mount ::API::AwardEmoji mount ::API::Branches mount ::API::Builds diff --git a/lib/api/artifacts.rb b/lib/api/artifacts.rb deleted file mode 100644 index 6ce2bed8260..00000000000 --- a/lib/api/artifacts.rb +++ /dev/null @@ -1,34 +0,0 @@ -module API - # Projects artifacts API - class Artifacts < Grape::API - before do - authenticate! - authorize!(:read_build, user_project) - end - - resource :projects do - # Download the artifacts file from ref_name and build_name - # - # Parameters: - # id (required) - The ID of a project - # ref_name (required) - The ref from repository - # build_name (required) - The name for the build - # Example Request: - # GET /projects/:id/artifacts/:ref_name/:build_name - get ':id/artifacts/:ref_name/:build_name', - requirements: { ref_name: /.+/ } do - builds = user_project.builds_for( - params[:build_name], params[:ref_name]) - - latest_build = builds.success.latest.first - - if latest_build - redirect( - "/projects/#{user_project.id}/builds/#{latest_build.id}/artifacts") - else - not_found! - end - end - end - end -end diff --git a/lib/api/builds.rb b/lib/api/builds.rb index b3b28541382..505ba1a66fe 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -71,12 +71,27 @@ module API build = get_build!(params[:build_id]) artifacts_file = build.artifacts_file - if !artifacts_file.file_storage? - redirect_to(build.artifacts_file.url) + present_artifact!(artifacts_file) + end - elsif artifacts_file.exists? - present_file!(artifacts_file.path, artifacts_file.filename) + # Download the artifacts file from ref_name and build_name + # + # Parameters: + # id (required) - The ID of a project + # ref_name (required) - The ref from repository + # job (required) - The name for the build + # Example Request: + # GET /projects/:id/artifacts/:ref_name/:build_name + get ':id/builds/artifacts/:ref_name', + requirements: { ref_name: /.+/ } do + builds = user_project.builds_for( + params[:job], params[:ref_name]) + latest_build = builds.success.latest.first + + if latest_build + redirect( + "/projects/#{user_project.id}/builds/#{latest_build.id}/artifacts") else not_found! end @@ -191,6 +206,18 @@ module API get_build(id) || not_found! end + def present_artifact!(artifacts_file) + if !artifacts_file.file_storage? + redirect_to(build.artifacts_file.url) + + elsif artifacts_file.exists? + present_file!(artifacts_file.path, artifacts_file.filename) + + else + not_found! + end + end + def filter_builds(builds, scope) return builds if scope.nil? || scope.empty? diff --git a/spec/requests/api/artifacts_spec.rb b/spec/requests/api/artifacts_spec.rb index 393b8f85402..f1461f7bc53 100644 --- a/spec/requests/api/artifacts_spec.rb +++ b/spec/requests/api/artifacts_spec.rb @@ -1,14 +1,14 @@ require 'spec_helper' require_relative '../shared/artifacts_context' -describe API::API, api: true do +describe API::API, api: true do include ApiHelpers describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do include_context 'artifacts from ref and build name' - def path_from_ref(ref = pipeline.sha, build_name = build.name, _ = '') - api("/projects/#{project.id}/artifacts/#{ref}/#{build_name}", user) + def path_from_ref(ref = pipeline.sha, job = build.name) + api("/projects/#{project.id}/builds/artifacts/#{ref}?job=#{job}", user) end context '401' do diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index f5b39c3d698..b661bf71545 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe API::API, api: true do +describe API::API, api: true do include ApiHelpers let(:user) { create(:user) } -- cgit v1.2.1 From c4496de8bf90ded13245fedc6b760805f15f6942 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 14 Jul 2016 19:02:07 +0800 Subject: Provide the file directly rather than redirecting --- lib/api/builds.rb | 6 ++---- spec/requests/api/artifacts_spec.rb | 11 ++++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 505ba1a66fe..5c14444f91a 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -69,9 +69,8 @@ module API authorize_read_builds! build = get_build!(params[:build_id]) - artifacts_file = build.artifacts_file - present_artifact!(artifacts_file) + present_artifact!(build.artifacts_file) end # Download the artifacts file from ref_name and build_name @@ -90,8 +89,7 @@ module API latest_build = builds.success.latest.first if latest_build - redirect( - "/projects/#{user_project.id}/builds/#{latest_build.id}/artifacts") + present_artifact!(latest_build.artifacts_file) else not_found! end diff --git a/spec/requests/api/artifacts_spec.rb b/spec/requests/api/artifacts_spec.rb index f1461f7bc53..56d0965b0be 100644 --- a/spec/requests/api/artifacts_spec.rb +++ b/spec/requests/api/artifacts_spec.rb @@ -31,10 +31,15 @@ describe API::API, api: true do it_behaves_like 'artifacts from ref with 404' end - context '302' do + context '200' do def verify - expect(response).to redirect_to( - "/projects/#{project.id}/builds/#{build.id}/artifacts") + download_headers = + { 'Content-Transfer-Encoding' => 'binary', + 'Content-Disposition' => + "attachment; filename=#{build.artifacts_file.filename}" } + + expect(response).to have_http_status(200) + expect(response.headers).to include(download_headers) end it_behaves_like 'artifacts from ref with 302' -- cgit v1.2.1 From 640fc8264d6adce67e89236e23cd83656a84c505 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 00:53:27 +0800 Subject: Avoid using let! --- spec/requests/api/builds_spec.rb | 62 ++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index b661bf71545..350d9cb5c7e 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -6,16 +6,21 @@ describe API::API, api: true do let(:user) { create(:user) } let(:api_user) { user } let(:user2) { create(:user) } - let!(:project) { create(:project, creator_id: user.id) } - let!(:developer) { create(:project_member, :developer, user: user, project: project) } - let!(:reporter) { create(:project_member, :reporter, user: user2, project: project) } - let!(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.id) } - let!(:build) { create(:ci_build, pipeline: pipeline) } + let(:project) { create(:project, creator_id: user.id) } + let(:developer) { create(:project_member, :developer, user: user, project: project) } + let(:reporter) { create(:project_member, :reporter, user: user2, project: project) } + let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.id) } + let(:build) { create(:ci_build, pipeline: pipeline) } describe 'GET /projects/:id/builds ' do let(:query) { '' } - before { get api("/projects/#{project.id}/builds?#{query}", api_user) } + before do + developer + build + + get api("/projects/#{project.id}/builds?#{query}", api_user) + end context 'authorized user' do it 'should return project builds' do @@ -77,9 +82,9 @@ describe API::API, api: true do context 'when user is authorized' do context 'when pipeline has builds' do before do - create(:ci_pipeline, project: project, sha: project.commit.id) + developer + build create(:ci_build, pipeline: pipeline) - create(:ci_build) get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) end @@ -93,6 +98,8 @@ describe API::API, api: true do context 'when pipeline has no builds' do before do + developer + branch_head = project.commit('feature').id get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) end @@ -107,8 +114,7 @@ describe API::API, api: true do context 'when user is not authorized' do before do - create(:ci_pipeline, project: project, sha: project.commit.id) - create(:ci_build, pipeline: pipeline) + build get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) end @@ -122,7 +128,11 @@ describe API::API, api: true do end describe 'GET /projects/:id/builds/:build_id' do - before { get api("/projects/#{project.id}/builds/#{build.id}", api_user) } + before do + developer + + get api("/projects/#{project.id}/builds/#{build.id}", api_user) + end context 'authorized user' do it 'should return specific build data' do @@ -141,7 +151,11 @@ describe API::API, api: true do end describe 'GET /projects/:id/builds/:build_id/artifacts' do - before { get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) } + before do + developer + + get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) + end context 'build with artifacts' do let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) } @@ -175,7 +189,11 @@ describe API::API, api: true do describe 'GET /projects/:id/builds/:build_id/trace' do let(:build) { create(:ci_build, :trace, pipeline: pipeline) } - before { get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) } + before do + developer + + get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user) + end context 'authorized user' do it 'should return specific build trace' do @@ -194,7 +212,12 @@ describe API::API, api: true do end describe 'POST /projects/:id/builds/:build_id/cancel' do - before { post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) } + before do + developer + reporter + + post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) + end context 'authorized user' do context 'user with :update_build persmission' do @@ -225,7 +248,12 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/retry' do let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } - before { post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) } + before do + developer + reporter + + post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) + end context 'authorized user' do context 'user with :update_build permission' do @@ -256,6 +284,8 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/erase' do before do + developer + post api("/projects/#{project.id}/builds/#{build.id}/erase", user) end @@ -286,6 +316,8 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do before do + developer + post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user) end -- cgit v1.2.1 From 398de9f1de8d2c0bf1bba57a4737a4518975dc85 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 01:07:41 +0800 Subject: now we're able to merge the spec --- spec/requests/api/artifacts_spec.rb | 48 ------------------------------------- spec/requests/api/builds_spec.rb | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 48 deletions(-) delete mode 100644 spec/requests/api/artifacts_spec.rb diff --git a/spec/requests/api/artifacts_spec.rb b/spec/requests/api/artifacts_spec.rb deleted file mode 100644 index 56d0965b0be..00000000000 --- a/spec/requests/api/artifacts_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'spec_helper' -require_relative '../shared/artifacts_context' - -describe API::API, api: true do - include ApiHelpers - - describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do - include_context 'artifacts from ref and build name' - - def path_from_ref(ref = pipeline.sha, job = build.name) - api("/projects/#{project.id}/builds/artifacts/#{ref}?job=#{job}", user) - end - - context '401' do - let(:user) { nil } - - before do - get path_from_ref - end - - it 'gives 401 for unauthorized user' do - expect(response).to have_http_status(401) - end - end - - context '404' do - def verify - expect(response).to have_http_status(404) - end - - it_behaves_like 'artifacts from ref with 404' - end - - context '200' do - def verify - download_headers = - { 'Content-Transfer-Encoding' => 'binary', - 'Content-Disposition' => - "attachment; filename=#{build.artifacts_file.filename}" } - - expect(response).to have_http_status(200) - expect(response.headers).to include(download_headers) - end - - it_behaves_like 'artifacts from ref with 302' - end - end -end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 350d9cb5c7e..5d2e2293236 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require_relative '../shared/artifacts_context' describe API::API, api: true do include ApiHelpers @@ -186,6 +187,48 @@ describe API::API, api: true do end end + describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do + include_context 'artifacts from ref and build name' + + def path_from_ref(ref = pipeline.sha, job = build.name) + api("/projects/#{project.id}/builds/artifacts/#{ref}?job=#{job}", user) + end + + context '401' do + let(:user) { nil } + + before do + get path_from_ref + end + + it 'gives 401 for unauthorized user' do + expect(response).to have_http_status(401) + end + end + + context '404' do + def verify + expect(response).to have_http_status(404) + end + + it_behaves_like 'artifacts from ref with 404' + end + + context '200' do + def verify + download_headers = + { 'Content-Transfer-Encoding' => 'binary', + 'Content-Disposition' => + "attachment; filename=#{build.artifacts_file.filename}" } + + expect(response).to have_http_status(200) + expect(response.headers).to include(download_headers) + end + + it_behaves_like 'artifacts from ref with 302' + end + end + describe 'GET /projects/:id/builds/:build_id/trace' do let(:build) { create(:ci_build, :trace, pipeline: pipeline) } -- cgit v1.2.1 From e96401f097e3d3bffe3a34d6e053af356109370b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 01:24:18 +0800 Subject: Add a download prefix so that we could add file prefix in the future --- lib/api/builds.rb | 7 +++---- spec/requests/api/builds_spec.rb | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 5c14444f91a..6792afb064e 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -80,11 +80,10 @@ module API # ref_name (required) - The ref from repository # job (required) - The name for the build # Example Request: - # GET /projects/:id/artifacts/:ref_name/:build_name - get ':id/builds/artifacts/:ref_name', + # GET /projects/:id/artifacts/download/:ref_name?job=name + get ':id/builds/artifacts/download/:ref_name', requirements: { ref_name: /.+/ } do - builds = user_project.builds_for( - params[:job], params[:ref_name]) + builds = user_project.builds_for(params[:job], params[:ref_name]) latest_build = builds.success.latest.first diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 5d2e2293236..246d273073a 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -191,7 +191,9 @@ describe API::API, api: true do include_context 'artifacts from ref and build name' def path_from_ref(ref = pipeline.sha, job = build.name) - api("/projects/#{project.id}/builds/artifacts/#{ref}?job=#{job}", user) + api( + "/projects/#{project.id}/builds/artifacts/download/#{ref}?job=#{job}", + user) end context '401' do -- cgit v1.2.1 From 2e90abf254096253ac0201de48210c9cabdb2db4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 01:45:04 +0800 Subject: It could be redirecting or downloading in Rails or API --- spec/requests/api/builds_spec.rb | 2 +- spec/requests/projects/artifacts_controller_spec.rb | 2 +- spec/requests/shared/artifacts_context.rb | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 246d273073a..d226646e439 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -227,7 +227,7 @@ describe API::API, api: true do expect(response.headers).to include(download_headers) end - it_behaves_like 'artifacts from ref with 302' + it_behaves_like 'artifacts from ref successfully' end end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 574b7028617..c8a21f7d0ec 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -45,7 +45,7 @@ describe Projects::ArtifactsController do expect(response).to redirect_to(path) end - it_behaves_like 'artifacts from ref with 302' + it_behaves_like 'artifacts from ref successfully' end end end diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb index 03f7f248773..0c9f33bfcb2 100644 --- a/spec/requests/shared/artifacts_context.rb +++ b/spec/requests/shared/artifacts_context.rb @@ -29,13 +29,13 @@ shared_examples 'artifacts from ref with 404' do end end -shared_examples 'artifacts from ref with 302' do +shared_examples 'artifacts from ref successfully' do context 'with sha' do before do get path_from_ref end - it('redirects') { verify } + it('gives the file') { verify } end context 'with regular branch' do @@ -47,7 +47,7 @@ shared_examples 'artifacts from ref with 302' do get path_from_ref('master') end - it('redirects') { verify } + it('gives the file') { verify } end context 'with branch name containing slash' do @@ -59,7 +59,7 @@ shared_examples 'artifacts from ref with 302' do get path_from_ref('improve/awesome') end - it('redirects') { verify } + it('gives the file') { verify } end context 'with latest build' do @@ -73,7 +73,7 @@ shared_examples 'artifacts from ref with 302' do get path_from_ref end - it('redirects') { verify } + it('gives the file') { verify } end context 'with success build' do @@ -86,6 +86,6 @@ shared_examples 'artifacts from ref with 302' do get path_from_ref end - it('redirects') { verify } + it('gives the file') { verify } end end -- cgit v1.2.1 From c23f2aa53099c6c906a8c6457183502450fa5703 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 01:58:13 +0800 Subject: Fix outdated comment --- lib/api/builds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 6792afb064e..e65dfa88746 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -73,7 +73,7 @@ module API present_artifact!(build.artifacts_file) end - # Download the artifacts file from ref_name and build_name + # Download the artifacts file from ref_name and job # # Parameters: # id (required) - The ID of a project -- cgit v1.2.1 From 4bb3787eee282434263c37194a443edf6a93c1b7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 02:01:10 +0800 Subject: Try to make the URL more consistent between Rails and API --- lib/api/builds.rb | 4 ++-- spec/requests/api/builds_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index e65dfa88746..237a88adcc7 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -80,8 +80,8 @@ module API # ref_name (required) - The ref from repository # job (required) - The name for the build # Example Request: - # GET /projects/:id/artifacts/download/:ref_name?job=name - get ':id/builds/artifacts/download/:ref_name', + # GET /projects/:id/artifacts/:ref_name/download?job=name + get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do builds = user_project.builds_for(params[:job], params[:ref_name]) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index d226646e439..59ea7992023 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -187,12 +187,12 @@ describe API::API, api: true do end end - describe 'GET /projects/:id/artifacts/:ref_name/:build_name' do + describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do include_context 'artifacts from ref and build name' def path_from_ref(ref = pipeline.sha, job = build.name) api( - "/projects/#{project.id}/builds/artifacts/download/#{ref}?job=#{job}", + "/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", user) end -- cgit v1.2.1 From 53a9dee6cb54b75fa2999b4a33a59928b3b73ec3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 15 Jul 2016 02:22:29 +0800 Subject: Introduce Project#latest_success_builds_for: So it's more accessible for views to access the names of jobs. Only filter Build#name from where we really need to download it. --- app/controllers/projects/artifacts_controller.rb | 4 ++-- app/models/project.rb | 9 ++++++--- lib/api/builds.rb | 5 ++--- spec/models/build_spec.rb | 12 ++++++++---- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 3e487c24cbd..1b3c4ec9bd8 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -61,9 +61,9 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref if params[:ref_name] - builds = project.builds_for(params[:job], params[:ref_name]) + builds = project.latest_success_builds_for(params[:ref_name]) - builds.success.latest.first + builds.where(name: params[:job]).first end end diff --git a/app/models/project.rb b/app/models/project.rb index 793cf2d70fb..384841dbb9a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -429,15 +429,18 @@ class Project < ActiveRecord::Base repository.commit(ref) end - def builds_for(build_name, ref = 'HEAD') + def latest_success_builds_for(ref = 'HEAD') + builds_for(ref).success.latest + end + + def builds_for(ref = 'HEAD') commit_object = commit(ref) if commit_object.nil? builds.none else builds.joins(:pipeline). - merge(Ci::Pipeline.where(sha: commit_object.sha)). - where(name: build_name) + merge(Ci::Pipeline.where(sha: commit_object.sha)) end end diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 237a88adcc7..53774b5c10f 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -83,9 +83,8 @@ module API # GET /projects/:id/artifacts/:ref_name/download?job=name get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do - builds = user_project.builds_for(params[:job], params[:ref_name]) - - latest_build = builds.success.latest.first + builds = user_project.latest_success_builds_for(params[:ref_name]) + latest_build = builds.where(name: params[:job]).first if latest_build present_artifact!(latest_build.artifacts_file) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index b1354faa722..7c95463a571 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -691,15 +691,19 @@ describe Ci::Build, models: true do end end - describe 'Project#builds_for' do - it 'returns builds from ref and build name' do - build_ids = project.builds_for(build.name, 'HEAD').map(&:id) + describe 'Project#latest_success_builds_for' do + before do + build.update(status: 'success') + end + + it 'returns builds from ref' do + build_ids = project.latest_success_builds_for('HEAD').map(&:id) expect(build_ids).to eq([build.id]) end it 'returns empty relation if the build cannot be found' do - builds = project.builds_for(build.name, 'TAIL').all + builds = project.latest_success_builds_for('TAIL').all expect(builds).to be_empty end -- cgit v1.2.1 From e313fd124225a0d89f3ad86a0cbb62b93c855898 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Sat, 16 Jul 2016 17:44:34 -0500 Subject: Add artifacts button --- app/views/projects/tags/_download.html.haml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 8a11dbfa9f4..3f335b84c52 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -1,14 +1,32 @@ -%span.btn-group - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do - %span Source code - %a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' } +.dropdown.inline + %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } + = icon('download') %span.caret %span.sr-only Select Archive Format %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Source code %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do %span Download zip %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz + - job_names = project.latest_success_builds_for('v8.9.6').pluck(:name) + %li.dropdown-header Artifacts + + + +-# +-# - artifacts = pipeline.builds.latest.select { |b| b.artifacts? } +-# - if artifacts.present? +-# .dropdown.inline.build-artifacts +-# %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} +-# = icon('download') +-# %b.caret +-# %ul.dropdown-menu.dropdown-menu-align-right +-# - artifacts.each do |build| +-# %li +-# = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do +-# = icon("download") +-# %span Download '#{build.name}' artifacts -- cgit v1.2.1 From faeaeda60e5601914338899f6b23b677d37a2ab5 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Sun, 17 Jul 2016 13:22:53 -0500 Subject: Add artifacts to tags --- app/views/projects/tags/_download.html.haml | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 3f335b84c52..1f4c6c9ec08 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,21 +12,10 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - job_names = project.latest_success_builds_for('v8.9.6').pluck(:name) - %li.dropdown-header Artifacts - - - --# --# - artifacts = pipeline.builds.latest.select { |b| b.artifacts? } --# - if artifacts.present? --# .dropdown.inline.build-artifacts --# %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} --# = icon('download') --# %b.caret --# %ul.dropdown-menu.dropdown-menu-align-right --# - artifacts.each do |build| --# %li --# = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do --# = icon("download") --# %span Download '#{build.name}' artifacts + - artifacts = project.builds_for(ref).latest.with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(project.namespace, project, job), rel: 'nofollow' do + %span Download '#{job.name}' -- cgit v1.2.1 From 5c28f16a01dd2c5f3e5b4e97d70ee3a9b0cdad3f Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Sun, 17 Jul 2016 19:04:45 -0500 Subject: Add artifacts download button on project page and branches page --- app/assets/stylesheets/framework/lists.scss | 4 ++++ app/views/projects/branches/_branch.html.haml | 15 +++++++++++++++ app/views/projects/buttons/_download.html.haml | 23 +++++++++++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 2c40ec430ca..95a56ee0e95 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -157,6 +157,10 @@ ul.content-list { margin-right: 0; } } + + .artifacts-btn { + margin-right: 10px; + } } // When dragging a list item diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 4bd85061240..78b3de46f58 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,6 +27,21 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare + - artifacts = @project.builds_for(@repository.root_ref).latest.with_artifacts + - if artifacts.any? + .dropdown.inline.artifacts-btn + %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } + = icon('download') + %span.caret + %span.sr-only + Select Archive Format + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Artifacts + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + %span Download '#{job.name}' + - if can_remove_branch?(@project, branch.name) = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = icon("trash-o") diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 58f43ecb5d5..c971420b16c 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,4 +1,23 @@ - unless @project.empty_repo? - if can? current_user, :download_code, @project - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do - = icon('download') + .dropdown.inline.btn-group + %button.btn{ 'data-toggle' => 'dropdown' } + = icon('download') + %span.caret + %span.sr-only + Select Archive Format + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Source code + %li + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), rel: 'nofollow' do + %span Download zip + %li + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do + %span Download tar.gz + - artifacts = @project.builds_for(@ref).latest.with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + %span Download '#{job.name}' -- cgit v1.2.1 From 39c1cabf27da7a082f4e3da669da6b91393016d9 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Sun, 17 Jul 2016 19:11:00 -0500 Subject: Fix dropdown caret --- app/views/projects/buttons/_download.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index c971420b16c..f504d514963 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -3,7 +3,7 @@ .dropdown.inline.btn-group %button.btn{ 'data-toggle' => 'dropdown' } = icon('download') - %span.caret + = icon('caret-down') %span.sr-only Select Archive Format %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } -- cgit v1.2.1 From 5151ebf4c68b3ac87d51474b49962f0e5ba6d3e7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 13:37:00 +0800 Subject: Use RSpec helpers, feedback from: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125543 --- spec/models/build_spec.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 7c95463a571..b2dcbd8da2e 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -628,7 +628,7 @@ describe Ci::Build, models: true do describe '#erasable?' do subject { build.erasable? } - it { is_expected.to eq true } + it { is_expected.to be_truthy } end describe '#erased?' do @@ -636,7 +636,7 @@ describe Ci::Build, models: true do subject { build.erased? } context 'build has not been erased' do - it { is_expected.to be false } + it { is_expected.to be_falsey } end context 'build has been erased' do @@ -644,12 +644,13 @@ describe Ci::Build, models: true do build.erase end - it { is_expected.to be true } + it { is_expected.to be_truthy } end end context 'metadata and build trace are not available' do let!(:build) { create(:ci_build, :success, :artifacts) } + before do build.remove_artifacts_metadata! end @@ -676,7 +677,7 @@ describe Ci::Build, models: true do end it 'returns false' do - expect(build.retryable?).to be(false) + expect(build).not_to be_retryable end end @@ -686,7 +687,7 @@ describe Ci::Build, models: true do end it 'returns true' do - expect(build.retryable?).to be(true) + expect(build).to be_retryable end end end -- cgit v1.2.1 From 78c37bdd773da9a41dc55e6915408ccae03186ff Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 13:39:53 +0800 Subject: Use find_by, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125486 --- app/controllers/projects/artifacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 1b3c4ec9bd8..bfe0781d735 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -63,7 +63,7 @@ class Projects::ArtifactsController < Projects::ApplicationController if params[:ref_name] builds = project.latest_success_builds_for(params[:ref_name]) - builds.where(name: params[:job]).first + builds.find_by(name: params[:job]) end end -- cgit v1.2.1 From 6dcb75f98515d8f3a723edc1900e80cf9427c486 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 13:41:37 +0800 Subject: Remove blank lines between clauses, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125597 --- lib/api/builds.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 53774b5c10f..d988c669cb1 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -205,10 +205,8 @@ module API def present_artifact!(artifacts_file) if !artifacts_file.file_storage? redirect_to(build.artifacts_file.url) - elsif artifacts_file.exists? present_file!(artifacts_file.path, artifacts_file.filename) - else not_found! end -- cgit v1.2.1 From 6de48227badd879f7e12803592daa2fc73656f91 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 13:58:48 +0800 Subject: Use single line even if they're more than 80 chars, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125628 --- spec/requests/api/builds_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 59ea7992023..bc6242a0d71 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -191,9 +191,7 @@ describe API::API, api: true do include_context 'artifacts from ref and build name' def path_from_ref(ref = pipeline.sha, job = build.name) - api( - "/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", - user) + api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", user) end context '401' do -- cgit v1.2.1 From 6830e2821f16d832963320aae571612f50a8aaa0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 14:00:39 +0800 Subject: Match against records rather than id, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125605 https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125611 --- spec/models/build_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index b2dcbd8da2e..a57f0b6886c 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -698,9 +698,9 @@ describe Ci::Build, models: true do end it 'returns builds from ref' do - build_ids = project.latest_success_builds_for('HEAD').map(&:id) + builds = project.latest_success_builds_for('HEAD') - expect(build_ids).to eq([build.id]) + expect(builds).to contain_exactly(build) end it 'returns empty relation if the build cannot be found' do -- cgit v1.2.1 From cc91f09ac39a7c201d527734e835d01dc13e5059 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 14:34:03 +0800 Subject: Just use find_by! and we're rescuing ActiveRecord::RecordNotFound Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125645 --- lib/api/builds.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index d988c669cb1..a27397a82f7 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -84,13 +84,9 @@ module API get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do builds = user_project.latest_success_builds_for(params[:ref_name]) - latest_build = builds.where(name: params[:job]).first + latest_build = builds.find_by!(name: params[:job]) - if latest_build - present_artifact!(latest_build.artifacts_file) - else - not_found! - end + present_artifact!(latest_build.artifacts_file) end # Get a trace of a specific build of a project -- cgit v1.2.1 From 5c1f75e983c88d4c884a15e9f84550fd256fb07f Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 16:47:47 +0800 Subject: Use ci_commits.gl_project_id instead of ci_builds.gl_project_id: Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13125513 --- app/models/project.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 384841dbb9a..d6e37e66a8b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -437,10 +437,11 @@ class Project < ActiveRecord::Base commit_object = commit(ref) if commit_object.nil? - builds.none + Ci::Build.none else - builds.joins(:pipeline). - merge(Ci::Pipeline.where(sha: commit_object.sha)) + Ci::Build.joins(:pipeline). + merge(Ci::Pipeline.where(sha: commit_object.sha, + project: self)) end end -- cgit v1.2.1 From 85409a5a10d22bebbc54a9c7b7c76e7c0e11b208 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 20:10:50 +0800 Subject: Use ci_commits.ref (Pipeline#ref) to find builds --- app/models/project.rb | 11 ++--------- spec/models/build_spec.rb | 5 +++-- spec/requests/api/builds_spec.rb | 2 +- spec/requests/projects/artifacts_controller_spec.rb | 2 +- spec/requests/shared/artifacts_context.rb | 19 ++++++++----------- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index d6e37e66a8b..770ec1c8a68 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -434,15 +434,8 @@ class Project < ActiveRecord::Base end def builds_for(ref = 'HEAD') - commit_object = commit(ref) - - if commit_object.nil? - Ci::Build.none - else - Ci::Build.joins(:pipeline). - merge(Ci::Pipeline.where(sha: commit_object.sha, - project: self)) - end + Ci::Build.joins(:pipeline). + merge(Ci::Pipeline.where(ref: ref, project: self)) end def merge_base_commit(first_commit_id, second_commit_id) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index a57f0b6886c..53064138a50 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -5,7 +5,8 @@ describe Ci::Build, models: true do let(:pipeline) do create(:ci_pipeline, project: project, - sha: project.commit.id) + sha: project.commit.id, + ref: 'fix') end let(:build) { create(:ci_build, pipeline: pipeline) } @@ -698,7 +699,7 @@ describe Ci::Build, models: true do end it 'returns builds from ref' do - builds = project.latest_success_builds_for('HEAD') + builds = project.latest_success_builds_for('fix') expect(builds).to contain_exactly(build) end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index bc6242a0d71..fb0f066498a 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -190,7 +190,7 @@ describe API::API, api: true do describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do include_context 'artifacts from ref and build name' - def path_from_ref(ref = pipeline.sha, job = build.name) + def path_from_ref(ref = pipeline.ref, job = build.name) api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", user) end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index c8a21f7d0ec..1782d37008a 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -10,7 +10,7 @@ describe Projects::ArtifactsController do end def path_from_ref( - ref = pipeline.sha, job = build.name, path = 'browse') + ref = pipeline.ref, job = build.name, path = 'browse') search_namespace_project_artifacts_path( project.namespace, project, diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb index 0c9f33bfcb2..ff74b72a0b3 100644 --- a/spec/requests/shared/artifacts_context.rb +++ b/spec/requests/shared/artifacts_context.rb @@ -2,7 +2,10 @@ shared_context 'artifacts from ref and build name' do let(:user) { create(:user) } let(:project) { create(:project) } let(:pipeline) do - create(:ci_pipeline, project: project, sha: project.commit('fix').sha) + create(:ci_pipeline, + project: project, + sha: project.commit('fix').sha, + ref: 'fix') end let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } @@ -30,17 +33,10 @@ shared_examples 'artifacts from ref with 404' do end shared_examples 'artifacts from ref successfully' do - context 'with sha' do - before do - get path_from_ref - end - - it('gives the file') { verify } - end - context 'with regular branch' do before do - pipeline.update(sha: project.commit('master').sha) + pipeline.update(ref: 'master', + sha: project.commit('master').sha) end before do @@ -52,7 +48,8 @@ shared_examples 'artifacts from ref successfully' do context 'with branch name containing slash' do before do - pipeline.update(sha: project.commit('improve/awesome').sha) + pipeline.update(ref: 'improve/awesome', + sha: project.commit('improve/awesome').sha) end before do -- cgit v1.2.1 From af86b8c2c2b6fb08ea55eb89f1dd20aba81862ae Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 21:11:53 +0800 Subject: Latest success pipelines (rather than builds) --- app/models/ci/pipeline.rb | 8 +++++++ app/models/commit_status.rb | 4 ++-- app/models/project.rb | 7 ++----- spec/models/build_spec.rb | 35 ++++++++++++++++++++++++------- spec/requests/shared/artifacts_context.rb | 19 ++++++++++------- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index fa4071e2482..431a91004cd 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -18,6 +18,14 @@ module Ci after_touch :update_state after_save :keep_around_commits + scope :latest, -> do + max_id = unscope(:select). + select("max(#{table_name}.id)"). + group(:ref) + + where(id: max_id) + end + def self.truncate_sha(sha) sha[0...8] end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index baabbd785cc..3e97fe68d4b 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -18,8 +18,8 @@ class CommitStatus < ActiveRecord::Base scope :latest, -> do max_id = unscope(:select). - select("max(#{table_name}.id)"). - group(:name, :commit_id) + select("max(#{table_name}.id)"). + group(:name, :commit_id) where(id: max_id) end diff --git a/app/models/project.rb b/app/models/project.rb index 770ec1c8a68..1578fe67581 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -430,12 +430,9 @@ class Project < ActiveRecord::Base end def latest_success_builds_for(ref = 'HEAD') - builds_for(ref).success.latest - end - - def builds_for(ref = 'HEAD') Ci::Build.joins(:pipeline). - merge(Ci::Pipeline.where(ref: ref, project: self)) + merge(pipelines.where(ref: ref).success.latest). + with_artifacts end def merge_base_commit(first_commit_id, second_commit_id) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 53064138a50..8a8a4b46f08 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -6,7 +6,8 @@ describe Ci::Build, models: true do let(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.id, - ref: 'fix') + ref: 'fix', + status: 'success') end let(:build) { create(:ci_build, pipeline: pipeline) } @@ -694,20 +695,38 @@ describe Ci::Build, models: true do end describe 'Project#latest_success_builds_for' do + let(:build) do + create(:ci_build, :artifacts, :success, pipeline: pipeline) + end + before do - build.update(status: 'success') + build end - it 'returns builds from ref' do - builds = project.latest_success_builds_for('fix') + context 'with succeed pipeline' do + it 'returns builds from ref' do + builds = project.latest_success_builds_for('fix') + + expect(builds).to contain_exactly(build) + end + + it 'returns empty relation if the build cannot be found' do + builds = project.latest_success_builds_for('TAIL').all - expect(builds).to contain_exactly(build) + expect(builds).to be_empty + end end - it 'returns empty relation if the build cannot be found' do - builds = project.latest_success_builds_for('TAIL').all + context 'with pending pipeline' do + before do + pipeline.update(status: 'pending') + end - expect(builds).to be_empty + it 'returns empty relation' do + builds = project.latest_success_builds_for('fix').all + + expect(builds).to be_empty + end end end end diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb index ff74b72a0b3..635c5646f91 100644 --- a/spec/requests/shared/artifacts_context.rb +++ b/spec/requests/shared/artifacts_context.rb @@ -25,7 +25,7 @@ shared_examples 'artifacts from ref with 404' do context 'has no such build' do before do - get path_from_ref(pipeline.sha, 'NOBUILD') + get path_from_ref(pipeline.ref, 'NOBUILD') end it('gives 404') { verify } @@ -33,6 +33,11 @@ shared_examples 'artifacts from ref with 404' do end shared_examples 'artifacts from ref successfully' do + def create_new_pipeline(status) + new_pipeline = create(:ci_pipeline, status: 'success') + create(:ci_build, status, :artifacts, pipeline: new_pipeline) + end + context 'with regular branch' do before do pipeline.update(ref: 'master', @@ -59,10 +64,10 @@ shared_examples 'artifacts from ref successfully' do it('gives the file') { verify } end - context 'with latest build' do + context 'with latest pipeline' do before do - 3.times do # creating some old builds - create(:ci_build, :success, :artifacts, pipeline: pipeline) + 3.times do # creating some old pipelines + create_new_pipeline(:success) end end @@ -73,10 +78,10 @@ shared_examples 'artifacts from ref successfully' do it('gives the file') { verify } end - context 'with success build' do + context 'with success pipeline' do before do - build # make sure build was old, but still the latest success one - create(:ci_build, :pending, :artifacts, pipeline: pipeline) + build # make sure pipeline was old, but still the latest success one + create_new_pipeline(:pending) end before do -- cgit v1.2.1 From e51d4a05b7195a98b48548c4c7260886f956b6d2 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 21:17:16 +0800 Subject: We should actually give latest success builds as well --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 1578fe67581..d26aa8073e8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -432,7 +432,7 @@ class Project < ActiveRecord::Base def latest_success_builds_for(ref = 'HEAD') Ci::Build.joins(:pipeline). merge(pipelines.where(ref: ref).success.latest). - with_artifacts + with_artifacts.success.latest end def merge_base_commit(first_commit_id, second_commit_id) -- cgit v1.2.1 From dcb436f58f16b20094d126bf0eb9e3403905c7cc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 22:07:26 +0800 Subject: Use Project#latest_success_builds_for --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 78b3de46f58..ced0380bb0b 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,7 +27,7 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - artifacts = @project.builds_for(@repository.root_ref).latest.with_artifacts + - artifacts = @project.latest_success_builds_for(@repository.root_ref) - if artifacts.any? .dropdown.inline.artifacts-btn %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index f504d514963..2eedc0d9a42 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -14,7 +14,7 @@ %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - artifacts = @project.builds_for(@ref).latest.with_artifacts + - artifacts = @project.latest_success_builds_for(@ref) - if artifacts.any? %li.dropdown-header Artifacts - artifacts.each do |job| diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 1f4c6c9ec08..4a8ef77ad8b 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,7 +12,7 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - artifacts = project.builds_for(ref).latest.with_artifacts + - artifacts = project.latest_success_builds_for(ref) - if artifacts.any? %li.dropdown-header Artifacts - artifacts.each do |job| -- cgit v1.2.1 From 72699d9719a269a82be6af0812d86d84733c77bd Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 23:16:29 +0800 Subject: Should be branch.name, not root ref --- app/views/projects/branches/_branch.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index ced0380bb0b..c2d7237f142 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,7 +27,7 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - artifacts = @project.latest_success_builds_for(@repository.root_ref) + - artifacts = @project.latest_success_builds_for(branch.name) - if artifacts.any? .dropdown.inline.artifacts-btn %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } -- cgit v1.2.1 From 57a78c37c72b2d697bc863ebfb84d3ca61ba9d7b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 23:17:43 +0800 Subject: Show notice if builds are not from latest pipeline --- app/models/ci/build.rb | 3 +++ app/models/project.rb | 12 +++++++--- app/views/projects/branches/_branch.html.haml | 32 +++++++++++++++----------- app/views/projects/buttons/_download.html.haml | 18 +++++++++------ app/views/projects/tags/_download.html.haml | 18 +++++++++------ 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index ffac3a22efc..9af04964b85 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -15,6 +15,9 @@ module Ci scope :with_artifacts, ->() { where.not(artifacts_file: nil) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } + scope :latest_success_with_artifacts, ->() do + with_artifacts.success.latest + end mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_metadata, ArtifactUploader diff --git a/app/models/project.rb b/app/models/project.rb index 12851c5d0ec..77431c3f538 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -429,10 +429,16 @@ class Project < ActiveRecord::Base repository.commit(ref) end - def latest_success_builds_for(ref = 'HEAD') + # ref can't be HEAD or SHA, can only be branch/tag name + def latest_success_pipeline_for(ref = 'master') + pipelines.where(ref: ref).success.latest + end + + # ref can't be HEAD or SHA, can only be branch/tag name + def latest_success_builds_for(ref = 'master') Ci::Build.joins(:pipeline). - merge(pipelines.where(ref: ref).success.latest). - with_artifacts.success.latest + merge(latest_success_pipeline_for(ref)). + latest_success_with_artifacts end def merge_base_commit(first_commit_id, second_commit_id) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index c2d7237f142..084a8474c4f 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,20 +27,24 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - artifacts = @project.latest_success_builds_for(branch.name) - - if artifacts.any? - .dropdown.inline.artifacts-btn - %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } - = icon('download') - %span.caret - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - %li.dropdown-header Artifacts - - artifacts.each do |job| - %li - = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do - %span Download '#{job.name}' + - pipeline = @project.latest_success_pipeline_for(branch.name).first + - if pipeline + - artifacts = pipeline.builds.latest_success_with_artifacts + - if artifacts.any? + .dropdown.inline.artifacts-btn + %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } + = icon('download') + %span.caret + %span.sr-only + Select Archive Format + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Artifacts + - unless pipeline.latest? + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + %span Download '#{job.name}' - if can_remove_branch?(@project, branch.name) = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 2eedc0d9a42..24a11005e61 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -14,10 +14,14 @@ %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - artifacts = @project.latest_success_builds_for(@ref) - - if artifacts.any? - %li.dropdown-header Artifacts - - artifacts.each do |job| - %li - = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do - %span Download '#{job.name}' + - pipeline = @project.latest_success_pipeline_for(@ref).first + - if pipeline + - artifacts = pipeline.builds.latest_success_with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + %span Download '#{job.name}' diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 4a8ef77ad8b..d2650809a3e 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,10 +12,14 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - artifacts = project.latest_success_builds_for(ref) - - if artifacts.any? - %li.dropdown-header Artifacts - - artifacts.each do |job| - %li - = link_to download_namespace_project_build_artifacts_path(project.namespace, project, job), rel: 'nofollow' do - %span Download '#{job.name}' + - pipeline = project.latest_success_pipeline_for(ref).first + - if pipeline + - artifacts = pipeline.builds.latest_success_with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(project.namespace, project, pipeline.sha))})" + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(project.namespace, project, job), rel: 'nofollow' do + %span Download '#{job.name}' -- cgit v1.2.1 From 914336b2d9da688a7b6267425a408c0ce280f9b2 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 23:26:30 +0800 Subject: Links to search_namespace_project_artifacts_path instead --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 084a8474c4f..bb1f2ec9604 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -43,7 +43,7 @@ = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" - artifacts.each do |job| %li - = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + = link_to search_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' - if can_remove_branch?(@project, branch.name) diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 24a11005e61..558a9023ed2 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -23,5 +23,5 @@ = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" - artifacts.each do |job| %li - = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + = link_to search_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index d2650809a3e..2a8891e917f 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -21,5 +21,5 @@ = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(project.namespace, project, pipeline.sha))})" - artifacts.each do |job| %li - = link_to download_namespace_project_build_artifacts_path(project.namespace, project, job), rel: 'nofollow' do + = link_to search_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' -- cgit v1.2.1 From e00fb385b247f90f12ddadd090e87cd59f2ebb36 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 18 Jul 2016 23:34:31 +0800 Subject: Actually should use tree path --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index bb1f2ec9604..cbd6ab74128 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -40,7 +40,7 @@ %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 558a9023ed2..f96045e09f0 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -20,7 +20,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(@project.namespace, @project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 2a8891e917f..6a431bcf7b2 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -18,7 +18,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_commits_path(project.namespace, project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(project.namespace, project, pipeline.sha))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do -- cgit v1.2.1 From 5fa6af05eb42feed7e0ca69778019805f7780ea5 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Mon, 18 Jul 2016 21:02:18 -0700 Subject: Add artifacts to view branch page download dropdown --- .../repositories/_download_archive.html.haml | 29 ++++++++++------------ app/views/projects/tree/show.html.haml | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 24658319060..5b2ddce3e91 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -1,16 +1,14 @@ - ref = ref || nil - btn_class = btn_class || '' -- split_button = split_button || false -- if split_button == true - %span.btn-group{class: btn_class} - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do - %i.fa.fa-download - %span Download zip - %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } +%span.btn-group{class: btn_class} + .dropdown.inline + %button.btn{ 'data-toggle' => 'dropdown' } + = icon('download') %span.caret %span.sr-only Select Archive Format - %ul.col-xs-10.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + %li.dropdown-header Source code %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do %i.fa.fa-download @@ -27,11 +25,10 @@ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar -- else - %span.btn-group{class: btn_class} - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do - %i.fa.fa-download - %span zip - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do - %i.fa.fa-download - %span tar.gz + - artifacts = @project.latest_success_builds_for(@ref) + - if artifacts.any? + %li.dropdown-header Artifacts + - artifacts.each do |job| + %li + = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do + %span Download '#{job.name}' diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index bf5360b4dee..c68f86f1378 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -11,7 +11,7 @@ .tree-controls = render 'projects/find_file_link' - if can? current_user, :download_code, @project - = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped', split_button: true + = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped' #tree-holder.tree-holder.clearfix .nav-block -- cgit v1.2.1 From 1a41cb90175ac8d6f780bf4fd35e4862f5312574 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 19 Jul 2016 16:50:53 +0800 Subject: Fix links and add not latest notice --- .../projects/repositories/_download_archive.html.haml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 5b2ddce3e91..396fb8598ae 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -25,10 +25,14 @@ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar - - artifacts = @project.latest_success_builds_for(@ref) - - if artifacts.any? - %li.dropdown-header Artifacts - - artifacts.each do |job| - %li - = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, job), rel: 'nofollow' do - %span Download '#{job.name}' + - pipeline = @project.latest_success_pipeline_for(ref).first + - if pipeline + - artifacts = pipeline.builds.latest_success_with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" + - artifacts.each do |job| + %li + = link_to search_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do + %span Download '#{job.name}' -- cgit v1.2.1 From fba2ec45b3bf493611f2d7e7e13a21c39bc654e0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 19 Jul 2016 17:08:21 +0800 Subject: Just use order(id: :desc) for latest stuffs: We don't need that subquery for group by ref and alike here. --- app/models/ci/build.rb | 2 +- app/models/ci/pipeline.rb | 10 +--------- app/models/project.rb | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 9af04964b85..c048eff0f80 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -16,7 +16,7 @@ module Ci scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :latest_success_with_artifacts, ->() do - with_artifacts.success.latest + with_artifacts.success.order(id: :desc) end mount_uploader :artifacts_file, ArtifactUploader diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index a8e6a23e1c4..148b056789a 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -20,14 +20,6 @@ module Ci after_touch :update_state after_save :keep_around_commits - scope :latest, -> do - max_id = unscope(:select). - select("max(#{table_name}.id)"). - group(:ref) - - where(id: max_id) - end - def self.truncate_sha(sha) sha[0...8] end @@ -226,7 +218,7 @@ module Ci def keep_around_commits return unless project - + project.repository.keep_around(self.sha) project.repository.keep_around(self.before_sha) end diff --git a/app/models/project.rb b/app/models/project.rb index 77431c3f538..30e8ade99ff 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,7 +431,7 @@ class Project < ActiveRecord::Base # ref can't be HEAD or SHA, can only be branch/tag name def latest_success_pipeline_for(ref = 'master') - pipelines.where(ref: ref).success.latest + pipelines.where(ref: ref).success.order(id: :desc) end # ref can't be HEAD or SHA, can only be branch/tag name -- cgit v1.2.1 From 0538e1e934484e76575164314fe8451374e4a4c8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 19 Jul 2016 17:09:32 +0800 Subject: Support SHA for downloading artifacts: So if we also query against SHA, we could actually support SHA. If there's a branch or tag also named like SHA this could be ambiguous, but since we could already do that in Git, I think it's probably fine, people would be aware they shouldn't use the same name anyway. --- app/models/project.rb | 9 ++++++--- spec/requests/shared/artifacts_context.rb | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 30e8ade99ff..c1cb1558132 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -429,12 +429,15 @@ class Project < ActiveRecord::Base repository.commit(ref) end - # ref can't be HEAD or SHA, can only be branch/tag name + # ref can't be HEAD, can only be branch/tag name or SHA def latest_success_pipeline_for(ref = 'master') - pipelines.where(ref: ref).success.order(id: :desc) + table = Ci::Pipeline.quoted_table_name + # TODO: Use `where(ref: ref).or(sha: ref)` in Rails 5 + pipelines.where("#{table}.ref = ? OR #{table}.sha = ?", ref, ref). + success.order(id: :desc) end - # ref can't be HEAD or SHA, can only be branch/tag name + # ref can't be HEAD, can only be branch/tag name or SHA def latest_success_builds_for(ref = 'master') Ci::Build.joins(:pipeline). merge(latest_success_pipeline_for(ref)). diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb index 635c5646f91..102ae392844 100644 --- a/spec/requests/shared/artifacts_context.rb +++ b/spec/requests/shared/artifacts_context.rb @@ -38,6 +38,14 @@ shared_examples 'artifacts from ref successfully' do create(:ci_build, status, :artifacts, pipeline: new_pipeline) end + context 'with sha' do + before do + get path_from_ref(pipeline.sha) + end + + it('gives the file') { verify } + end + context 'with regular branch' do before do pipeline.update(ref: 'master', -- cgit v1.2.1 From 85ceb8b72f5a67d21bc9530fe835fdece98f3d4e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 19 Jul 2016 17:51:45 +0800 Subject: Rename latest_success* to latest_successful: Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13164642 --- app/controllers/projects/artifacts_controller.rb | 2 +- app/models/ci/build.rb | 2 +- app/models/project.rb | 8 ++++---- app/views/projects/branches/_branch.html.haml | 4 ++-- app/views/projects/buttons/_download.html.haml | 4 ++-- app/views/projects/repositories/_download_archive.html.haml | 4 ++-- app/views/projects/tags/_download.html.haml | 4 ++-- lib/api/builds.rb | 2 +- spec/models/build_spec.rb | 8 ++++---- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index f33cf238d88..05112571225 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -60,7 +60,7 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref if params[:ref_name] - builds = project.latest_success_builds_for(params[:ref_name]) + builds = project.latest_successful_builds_for(params[:ref_name]) builds.find_by(name: params[:job]) end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index c048eff0f80..65dfe4f0190 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -15,7 +15,7 @@ module Ci scope :with_artifacts, ->() { where.not(artifacts_file: nil) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } - scope :latest_success_with_artifacts, ->() do + scope :latest_successful_with_artifacts, ->() do with_artifacts.success.order(id: :desc) end diff --git a/app/models/project.rb b/app/models/project.rb index c1cb1558132..60928bf9922 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -430,7 +430,7 @@ class Project < ActiveRecord::Base end # ref can't be HEAD, can only be branch/tag name or SHA - def latest_success_pipeline_for(ref = 'master') + def latest_successful_pipeline_for(ref = 'master') table = Ci::Pipeline.quoted_table_name # TODO: Use `where(ref: ref).or(sha: ref)` in Rails 5 pipelines.where("#{table}.ref = ? OR #{table}.sha = ?", ref, ref). @@ -438,10 +438,10 @@ class Project < ActiveRecord::Base end # ref can't be HEAD, can only be branch/tag name or SHA - def latest_success_builds_for(ref = 'master') + def latest_successful_builds_for(ref = 'master') Ci::Build.joins(:pipeline). - merge(latest_success_pipeline_for(ref)). - latest_success_with_artifacts + merge(latest_successful_pipeline_for(ref)). + latest_successful_with_artifacts end def merge_base_commit(first_commit_id, second_commit_id) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index cbd6ab74128..8f6ddfd9044 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,9 +27,9 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - pipeline = @project.latest_success_pipeline_for(branch.name).first + - pipeline = @project.latest_successful_pipeline_for(branch.name).first - if pipeline - - artifacts = pipeline.builds.latest_success_with_artifacts + - artifacts = pipeline.builds.latest_successful_with_artifacts - if artifacts.any? .dropdown.inline.artifacts-btn %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index f96045e09f0..047931a7fa5 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -14,9 +14,9 @@ %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = @project.latest_success_pipeline_for(@ref).first + - pipeline = @project.latest_successful_pipeline_for(@ref).first - if pipeline - - artifacts = pipeline.builds.latest_success_with_artifacts + - artifacts = pipeline.builds.latest_successful_with_artifacts - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 396fb8598ae..1c03aa0a332 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -25,9 +25,9 @@ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar - - pipeline = @project.latest_success_pipeline_for(ref).first + - pipeline = @project.latest_successful_pipeline_for(ref).first - if pipeline - - artifacts = pipeline.builds.latest_success_with_artifacts + - artifacts = pipeline.builds.latest_successful_with_artifacts - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 6a431bcf7b2..8e5e5cb559b 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,9 +12,9 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = project.latest_success_pipeline_for(ref).first + - pipeline = project.latest_successful_pipeline_for(ref).first - if pipeline - - artifacts = pipeline.builds.latest_success_with_artifacts + - artifacts = pipeline.builds.latest_successful_with_artifacts - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? diff --git a/lib/api/builds.rb b/lib/api/builds.rb index a27397a82f7..bb9e8f1ae6e 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -83,7 +83,7 @@ module API # GET /projects/:id/artifacts/:ref_name/download?job=name get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do - builds = user_project.latest_success_builds_for(params[:ref_name]) + builds = user_project.latest_successful_builds_for(params[:ref_name]) latest_build = builds.find_by!(name: params[:job]) present_artifact!(latest_build.artifacts_file) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index d435cd745b3..355cb8fdfff 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -678,7 +678,7 @@ describe Ci::Build, models: true do end end - describe 'Project#latest_success_builds_for' do + describe 'Project#latest_successful_builds_for' do let(:build) do create(:ci_build, :artifacts, :success, pipeline: pipeline) end @@ -689,13 +689,13 @@ describe Ci::Build, models: true do context 'with succeed pipeline' do it 'returns builds from ref' do - builds = project.latest_success_builds_for('fix') + builds = project.latest_successful_builds_for('fix') expect(builds).to contain_exactly(build) end it 'returns empty relation if the build cannot be found' do - builds = project.latest_success_builds_for('TAIL').all + builds = project.latest_successful_builds_for('TAIL').all expect(builds).to be_empty end @@ -707,7 +707,7 @@ describe Ci::Build, models: true do end it 'returns empty relation' do - builds = project.latest_success_builds_for('fix').all + builds = project.latest_successful_builds_for('fix').all expect(builds).to be_empty end -- cgit v1.2.1 From e3ce02300bf90451b98479720d1093afe8b7eea8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 19 Jul 2016 18:03:21 +0800 Subject: Link to pipeline instead of source tree, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13164795 --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/repositories/_download_archive.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 8f6ddfd9044..e48b78f9eef 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -40,7 +40,7 @@ %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 047931a7fa5..32f911f6b31 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -20,7 +20,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 1c03aa0a332..e29d99371c8 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -31,7 +31,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(@project.namespace, @project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 8e5e5cb559b..8acf145049a 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -18,7 +18,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_tree_path(project.namespace, project, pipeline.sha))})" + = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(project.namespace, project, pipeline))})" - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do -- cgit v1.2.1 From 69d0671342f361f48001c1c9bb4571cd881fdca8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jul 2016 18:47:05 +0800 Subject: Restore to what it used to be --- spec/requests/api/builds_spec.rb | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 56eb9cd8f8d..0d9820df18f 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' -require_relative '../shared/artifacts_context' describe API::API, api: true do include ApiHelpers @@ -17,9 +16,6 @@ describe API::API, api: true do let(:query) { '' } before do - developer - build - get api("/projects/#{project.id}/builds?#{query}", api_user) end @@ -83,9 +79,9 @@ describe API::API, api: true do context 'when user is authorized' do context 'when pipeline has builds' do before do - developer - build + create(:ci_pipeline, project: project, sha: project.commit.id) create(:ci_build, pipeline: pipeline) + create(:ci_build) get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user) end @@ -99,8 +95,6 @@ describe API::API, api: true do context 'when pipeline has no builds' do before do - developer - branch_head = project.commit('feature').id get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user) end @@ -115,7 +109,8 @@ describe API::API, api: true do context 'when user is not authorized' do before do - build + create(:ci_pipeline, project: project, sha: project.commit.id) + create(:ci_build, pipeline: pipeline) get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil) end @@ -130,8 +125,6 @@ describe API::API, api: true do describe 'GET /projects/:id/builds/:build_id' do before do - developer - get api("/projects/#{project.id}/builds/#{build.id}", api_user) end @@ -153,8 +146,6 @@ describe API::API, api: true do describe 'GET /projects/:id/builds/:build_id/artifacts' do before do - developer - get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user) end @@ -304,9 +295,6 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/cancel' do before do - developer - reporter - post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user) end @@ -340,9 +328,6 @@ describe API::API, api: true do let(:build) { create(:ci_build, :canceled, pipeline: pipeline) } before do - developer - reporter - post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user) end @@ -375,8 +360,6 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/erase' do before do - developer - post api("/projects/#{project.id}/builds/#{build.id}/erase", user) end @@ -407,8 +390,6 @@ describe API::API, api: true do describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do before do - developer - post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user) end -- cgit v1.2.1 From fa02d8fcc588b3720640131e7cc9ef7b06470932 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jul 2016 19:09:46 +0800 Subject: Merge shared context into controller test and update accordingly --- .../requests/projects/artifacts_controller_spec.rb | 77 +++++++++++++--- spec/requests/shared/artifacts_context.rb | 101 --------------------- 2 files changed, 62 insertions(+), 116 deletions(-) delete mode 100644 spec/requests/shared/artifacts_context.rb diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 1782d37008a..b823676d9e1 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -1,9 +1,20 @@ require 'spec_helper' -require_relative '../shared/artifacts_context' describe Projects::ArtifactsController do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:pipeline) do + create(:ci_pipeline, + project: project, + sha: project.commit.sha, + ref: project.default_branch) + end + let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } + describe 'GET /:project/builds/artifacts/:ref_name/browse?job=name' do - include_context 'artifacts from ref and build name' + before do + project.team << [user, :developer] + end before do login_as(user) @@ -19,33 +30,69 @@ describe Projects::ArtifactsController do job: job) end - context '404' do - def verify - expect(response.status).to eq(404) + context 'cannot find the build' do + shared_examples 'not found' do + it { expect(response).to have_http_status(:not_found) } end - it_behaves_like 'artifacts from ref with 404' + context 'has no such ref' do + before do + get path_from_ref('TAIL', build.name) + end + + it_behaves_like 'not found' + end + + context 'has no such build' do + before do + get path_from_ref(pipeline.ref, 'NOBUILD') + end + + it_behaves_like 'not found' + end context 'has no path' do before do get path_from_ref(pipeline.sha, build.name, '') end - it('gives 404') { verify } + it_behaves_like 'not found' end end - context '302' do - def verify - path = browse_namespace_project_build_artifacts_path( - project.namespace, - project, - build) + context 'found the build and redirect' do + shared_examples 'redirect to the build' do + it 'redirects' do + path = browse_namespace_project_build_artifacts_path( + project.namespace, + project, + build) - expect(response).to redirect_to(path) + expect(response).to redirect_to(path) + end + end + + context 'with regular branch' do + before do + pipeline.update(ref: 'master', + sha: project.commit('master').sha) + + get path_from_ref('master') + end + + it_behaves_like 'redirect to the build' end - it_behaves_like 'artifacts from ref successfully' + context 'with branch name containing slash' do + before do + pipeline.update(ref: 'improve/awesome', + sha: project.commit('improve/awesome').sha) + + get path_from_ref('improve/awesome') + end + + it_behaves_like 'redirect to the build' + end end end end diff --git a/spec/requests/shared/artifacts_context.rb b/spec/requests/shared/artifacts_context.rb deleted file mode 100644 index 102ae392844..00000000000 --- a/spec/requests/shared/artifacts_context.rb +++ /dev/null @@ -1,101 +0,0 @@ -shared_context 'artifacts from ref and build name' do - let(:user) { create(:user) } - let(:project) { create(:project) } - let(:pipeline) do - create(:ci_pipeline, - project: project, - sha: project.commit('fix').sha, - ref: 'fix') - end - let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } - - before do - project.team << [user, :developer] - end -end - -shared_examples 'artifacts from ref with 404' do - context 'has no such ref' do - before do - get path_from_ref('TAIL', build.name) - end - - it('gives 404') { verify } - end - - context 'has no such build' do - before do - get path_from_ref(pipeline.ref, 'NOBUILD') - end - - it('gives 404') { verify } - end -end - -shared_examples 'artifacts from ref successfully' do - def create_new_pipeline(status) - new_pipeline = create(:ci_pipeline, status: 'success') - create(:ci_build, status, :artifacts, pipeline: new_pipeline) - end - - context 'with sha' do - before do - get path_from_ref(pipeline.sha) - end - - it('gives the file') { verify } - end - - context 'with regular branch' do - before do - pipeline.update(ref: 'master', - sha: project.commit('master').sha) - end - - before do - get path_from_ref('master') - end - - it('gives the file') { verify } - end - - context 'with branch name containing slash' do - before do - pipeline.update(ref: 'improve/awesome', - sha: project.commit('improve/awesome').sha) - end - - before do - get path_from_ref('improve/awesome') - end - - it('gives the file') { verify } - end - - context 'with latest pipeline' do - before do - 3.times do # creating some old pipelines - create_new_pipeline(:success) - end - end - - before do - get path_from_ref - end - - it('gives the file') { verify } - end - - context 'with success pipeline' do - before do - build # make sure pipeline was old, but still the latest success one - create_new_pipeline(:pending) - end - - before do - get path_from_ref - end - - it('gives the file') { verify } - end -end -- cgit v1.2.1 From 2d9e7468de7edfe1868b8d9dc6dcdaff116f0da8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 21 Jul 2016 20:00:33 +0800 Subject: They were moved to project_spec.rb --- spec/models/build_spec.rb | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 950580fdee3..13ef60b732a 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -915,40 +915,4 @@ describe Ci::Build, models: true do end end end - - describe 'Project#latest_successful_builds_for' do - let(:build) do - create(:ci_build, :artifacts, :success, pipeline: pipeline) - end - - before do - build - end - - context 'with succeed pipeline' do - it 'returns builds from ref' do - builds = project.latest_successful_builds_for('fix') - - expect(builds).to contain_exactly(build) - end - - it 'returns empty relation if the build cannot be found' do - builds = project.latest_successful_builds_for('TAIL').all - - expect(builds).to be_empty - end - end - - context 'with pending pipeline' do - before do - pipeline.update(status: 'pending') - end - - it 'returns empty relation' do - builds = project.latest_successful_builds_for('fix').all - - expect(builds).to be_empty - end - end - end end -- cgit v1.2.1 From 80c22e4c09d7808d2a971c78a6232d4843c8c4f7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 25 Jul 2016 20:52:18 +0800 Subject: Add four features tests for download buttons in different places --- .../projects/branches/download_buttons_spec.rb | 37 +++++++++++++++++++++ .../projects/files/download_buttons_spec.rb | 38 ++++++++++++++++++++++ .../projects/main/download_buttons_spec.rb | 37 +++++++++++++++++++++ .../projects/tags/download_buttons_spec.rb | 38 ++++++++++++++++++++++ 4 files changed, 150 insertions(+) create mode 100644 spec/features/projects/branches/download_buttons_spec.rb create mode 100644 spec/features/projects/files/download_buttons_spec.rb create mode 100644 spec/features/projects/main/download_buttons_spec.rb create mode 100644 spec/features/projects/tags/download_buttons_spec.rb diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb new file mode 100644 index 00000000000..d3f53b65699 --- /dev/null +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +feature 'Download buttons in branches page', feature: true do + given(:user) { create(:user) } + given(:role) { :developer } + given(:status) { 'success' } + given(:project) { create(:project) } + given(:pipeline) do + create(:ci_pipeline, project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) + end + given!(:build) do + create(:ci_build, :success, :artifacts, + pipeline: pipeline, + status: pipeline.status, + name: 'build') + end + + background do + login_as(user) + project.team << [user, role] + end + + describe 'when checking branches' do + context 'with artifacts' do + before do + visit namespace_project_branches_path(project.namespace, project) + end + + scenario 'shows download artifacts button' do + expect(page).to have_link "Download '#{build.name}'" + end + end + end +end diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb new file mode 100644 index 00000000000..60c2ffedce0 --- /dev/null +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +feature 'Download buttons in files tree', feature: true do + given(:user) { create(:user) } + given(:role) { :developer } + given(:status) { 'success' } + given(:project) { create(:project) } + given(:pipeline) do + create(:ci_pipeline, project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) + end + given!(:build) do + create(:ci_build, :success, :artifacts, + pipeline: pipeline, + status: pipeline.status, + name: 'build') + end + + background do + login_as(user) + project.team << [user, role] + end + + describe 'when files tree' do + context 'with artifacts' do + before do + visit namespace_project_tree_path( + project.namespace, project, project.default_branch) + end + + scenario 'shows download artifacts button' do + expect(page).to have_link "Download '#{build.name}'" + end + end + end +end diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb new file mode 100644 index 00000000000..62e56808558 --- /dev/null +++ b/spec/features/projects/main/download_buttons_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +feature 'Download buttons in project main page', feature: true do + given(:user) { create(:user) } + given(:role) { :developer } + given(:status) { 'success' } + given(:project) { create(:project) } + given(:pipeline) do + create(:ci_pipeline, project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) + end + given!(:build) do + create(:ci_build, :success, :artifacts, + pipeline: pipeline, + status: pipeline.status, + name: 'build') + end + + background do + login_as(user) + project.team << [user, role] + end + + describe 'when checking project main page' do + context 'with artifacts' do + before do + visit namespace_project_path(project.namespace, project) + end + + scenario 'shows download artifacts button' do + expect(page).to have_link "Download '#{build.name}'" + end + end + end +end diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb new file mode 100644 index 00000000000..d4c4cfe9c99 --- /dev/null +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +feature 'Download buttons in tags page', feature: true do + given(:user) { create(:user) } + given(:role) { :developer } + given(:status) { 'success' } + given(:tag) { 'v1.0.0' } + given(:project) { create(:project) } + given(:pipeline) do + create(:ci_pipeline, project: project, + sha: project.commit.sha, + ref: tag, + status: status) + end + given!(:build) do + create(:ci_build, :success, :artifacts, + pipeline: pipeline, + status: pipeline.status, + name: 'build') + end + + background do + login_as(user) + project.team << [user, role] + end + + describe 'when checking tags' do + context 'with artifacts' do + before do + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'shows download artifacts button' do + expect(page).to have_link "Download '#{build.name}'" + end + end + end +end -- cgit v1.2.1 From 3eae0641ef0708f9b223abbe0070e332ea0b20ac Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 10 Aug 2016 18:40:10 +0800 Subject: Introduce Pipeline#latest and Pipeline.latest_for: So that we could easily access it for the view --- app/models/ci/pipeline.rb | 12 +++++++++-- spec/models/ci/pipeline_spec.rb | 48 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index bce6a992af6..bc1190537da 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -21,8 +21,12 @@ module Ci after_save :keep_around_commits # ref can't be HEAD or SHA, can only be branch/tag name - scope :latest_successful_for, ->(ref = default_branch) do - where(ref: ref).success.order(id: :desc).limit(1) + scope :latest_successful_for, ->(ref) do + latest(ref).success + end + + scope :latest_for, ->(ref) do + where(ref: ref).order(id: :desc).limit(1) end def self.truncate_sha(sha) @@ -98,6 +102,10 @@ module Ci end end + def latest + project.pipelines.latest_for(ref).first + end + def latest? return false unless ref commit = project.commit(ref) diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ccee591cf7a..556a6e1b59a 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1,8 +1,8 @@ require 'spec_helper' describe Ci::Pipeline, models: true do - let(:project) { FactoryGirl.create :empty_project } - let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project } + let(:project) { create(:empty_project) } + let(:pipeline) { create(:ci_pipeline, project: project) } it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:user) } @@ -481,6 +481,50 @@ describe Ci::Pipeline, models: true do end end + context 'with non-empty project' do + let(:project) { create(:project) } + let(:pipeline) { create_pipeline } + + describe '#latest?' do + context 'with latest sha' do + it 'returns true' do + expect(pipeline).to be_latest + end + end + + context 'with not latest sha' do + before do + pipeline.update( + sha: project.commit("#{project.default_branch}~1").sha) + end + + it 'returns false' do + expect(pipeline).not_to be_latest + end + end + end + + describe '#latest' do + let(:previous_pipeline) { create_pipeline } + + before do + previous_pipeline + pipeline + end + + it 'gives the latest pipeline' do + expect(previous_pipeline.latest).to eq(pipeline) + end + end + + def create_pipeline + create(:ci_pipeline, + project: project, + ref: project.default_branch, + sha: project.commit.sha) + end + end + describe '#manual_actions' do subject { pipeline.manual_actions } -- cgit v1.2.1 From cc3dbf83f4c0cf21fee56398f27851981f0e98f6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 10 Aug 2016 18:57:51 +0800 Subject: Empty lines around blocks --- spec/features/projects/branches/download_buttons_spec.rb | 11 +++++++---- spec/features/projects/files/download_buttons_spec.rb | 11 +++++++---- spec/features/projects/main/download_buttons_spec.rb | 11 +++++++---- spec/features/projects/tags/download_buttons_spec.rb | 11 +++++++---- spec/requests/projects/artifacts_controller_spec.rb | 2 ++ 5 files changed, 30 insertions(+), 16 deletions(-) diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index d3f53b65699..a223786777b 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -5,12 +5,15 @@ feature 'Download buttons in branches page', feature: true do given(:role) { :developer } given(:status) { 'success' } given(:project) { create(:project) } + given(:pipeline) do - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: project.default_branch, - status: status) + create(:ci_pipeline, + project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) end + given!(:build) do create(:ci_build, :success, :artifacts, pipeline: pipeline, diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb index 60c2ffedce0..be5cebcd7c9 100644 --- a/spec/features/projects/files/download_buttons_spec.rb +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -5,12 +5,15 @@ feature 'Download buttons in files tree', feature: true do given(:role) { :developer } given(:status) { 'success' } given(:project) { create(:project) } + given(:pipeline) do - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: project.default_branch, - status: status) + create(:ci_pipeline, + project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) end + given!(:build) do create(:ci_build, :success, :artifacts, pipeline: pipeline, diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb index 62e56808558..b26c0ea7a14 100644 --- a/spec/features/projects/main/download_buttons_spec.rb +++ b/spec/features/projects/main/download_buttons_spec.rb @@ -5,12 +5,15 @@ feature 'Download buttons in project main page', feature: true do given(:role) { :developer } given(:status) { 'success' } given(:project) { create(:project) } + given(:pipeline) do - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: project.default_branch, - status: status) + create(:ci_pipeline, + project: project, + sha: project.commit.sha, + ref: project.default_branch, + status: status) end + given!(:build) do create(:ci_build, :success, :artifacts, pipeline: pipeline, diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb index d4c4cfe9c99..ebc5204cf1d 100644 --- a/spec/features/projects/tags/download_buttons_spec.rb +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -6,12 +6,15 @@ feature 'Download buttons in tags page', feature: true do given(:status) { 'success' } given(:tag) { 'v1.0.0' } given(:project) { create(:project) } + given(:pipeline) do - create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: tag, - status: status) + create(:ci_pipeline, + project: project, + sha: project.commit.sha, + ref: tag, + status: status) end + given!(:build) do create(:ci_build, :success, :artifacts, pipeline: pipeline, diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index b823676d9e1..952b9fb99b7 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -3,12 +3,14 @@ require 'spec_helper' describe Projects::ArtifactsController do let(:user) { create(:user) } let(:project) { create(:project) } + let(:pipeline) do create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) end + let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } describe 'GET /:project/builds/artifacts/:ref_name/browse?job=name' do -- cgit v1.2.1 From 729ad5d53f59b18f14f9c0c7ed306b34ae70c4b5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 10 Aug 2016 18:59:06 +0800 Subject: This might be fixed on master already, but well --- spec/lib/gitlab/redis_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/lib/gitlab/redis_spec.rb b/spec/lib/gitlab/redis_spec.rb index 879ed30841c..e54f5ffb312 100644 --- a/spec/lib/gitlab/redis_spec.rb +++ b/spec/lib/gitlab/redis_spec.rb @@ -67,7 +67,6 @@ describe Gitlab::Redis do expect(subject).to receive(:fetch_config) { 'redis://myredis:6379' } expect(subject.send(:raw_config_hash)).to eq(url: 'redis://myredis:6379') end - end describe '#fetch_config' do -- cgit v1.2.1 From 517249858e41694f51b67461b313d5a34c2a466c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 10 Aug 2016 21:07:26 +0800 Subject: It's latest_for, not just latest --- app/models/ci/pipeline.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index bc1190537da..50f9ee7fc66 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -22,7 +22,7 @@ module Ci # ref can't be HEAD or SHA, can only be branch/tag name scope :latest_successful_for, ->(ref) do - latest(ref).success + latest_for(ref).success end scope :latest_for, ->(ref) do -- cgit v1.2.1 From 4b559c9afb34b80b910efec514653c6ea65adba8 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 17:26:04 +0800 Subject: Reverse ref and sha in args and rename pipeline to pipeline_for --- app/models/merge_request.rb | 3 ++- app/models/project.rb | 7 ++++--- app/views/projects/issues/_related_branches.html.haml | 2 +- db/fixtures/development/14_builds.rb | 2 +- lib/api/commit_statuses.rb | 2 +- spec/models/project_spec.rb | 2 +- spec/requests/api/commits_spec.rb | 2 +- spec/services/ci/image_for_build_service_spec.rb | 2 +- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b1fb3ce5d69..7c8e938df75 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -666,7 +666,8 @@ class MergeRequest < ActiveRecord::Base end def pipeline - @pipeline ||= source_project.pipeline(diff_head_sha, source_branch) if diff_head_sha && source_project + return unless diff_head_sha && source_project + @pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha) end def merge_commit diff --git a/app/models/project.rb b/app/models/project.rb index d306f86f783..dc9b4b38a10 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1086,12 +1086,13 @@ class Project < ActiveRecord::Base !namespace.share_with_group_lock end - def pipeline(sha, ref) + def pipeline_for(ref, sha) pipelines.order(id: :desc).find_by(sha: sha, ref: ref) end - def ensure_pipeline(sha, ref, current_user = nil) - pipeline(sha, ref) || pipelines.create(sha: sha, ref: ref, user: current_user) + def ensure_pipeline(ref, sha, current_user = nil) + pipeline_for(ref, sha) || + pipelines.create(sha: sha, ref: ref, user: current_user) end def enable_ci diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml index 6ea9f612d13..a8eeab3e55e 100644 --- a/app/views/projects/issues/_related_branches.html.haml +++ b/app/views/projects/issues/_related_branches.html.haml @@ -5,7 +5,7 @@ - @related_branches.each do |branch| %li - target = @project.repository.find_branch(branch).target - - pipeline = @project.pipeline(target.sha, branch) if target + - pipeline = @project.pipeline_for(branch, target.sha) if target - if pipeline %span.related-branch-ci-status = render_pipeline_status(pipeline) diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb index e65abe4ef77..6cc18bf51ed 100644 --- a/db/fixtures/development/14_builds.rb +++ b/db/fixtures/development/14_builds.rb @@ -40,7 +40,7 @@ class Gitlab::Seeder::Builds commits = @project.repository.commits('master', limit: 5) commits_sha = commits.map { |commit| commit.raw.id } commits_sha.map do |sha| - @project.ensure_pipeline(sha, 'master') + @project.ensure_pipeline('master', sha) end rescue [] diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 4df6ca8333e..5e3c9563703 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -64,7 +64,7 @@ module API ref = branches.first end - pipeline = @project.ensure_pipeline(commit.sha, ref, current_user) + pipeline = @project.ensure_pipeline(ref, commit.sha, current_user) name = params[:name] || params[:context] status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref]) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9c3b4712cab..60819fe02be 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -688,7 +688,7 @@ describe Project, models: true do let(:project) { create :project } let(:pipeline) { create :ci_pipeline, project: project, ref: 'master' } - subject { project.pipeline(pipeline.sha, 'master') } + subject { project.pipeline_for('master', pipeline.sha) } it { is_expected.to eq(pipeline) } diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 4379fcb3c1e..60c2f14bd3c 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -94,7 +94,7 @@ describe API::API, api: true do end it "returns status for CI" do - pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master') + pipeline = project.ensure_pipeline('master', project.repository.commit.sha) get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) expect(response).to have_http_status(200) expect(json_response['status']).to eq(pipeline.status) diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index 3a3e3efe709..21e00b11a12 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -5,7 +5,7 @@ module Ci let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:empty_project) } let(:commit_sha) { '01234567890123456789' } - let(:commit) { project.ensure_pipeline(commit_sha, 'master') } + let(:commit) { project.ensure_pipeline('master', commit_sha) } let(:build) { FactoryGirl.create(:ci_build, pipeline: commit) } describe '#execute' do -- cgit v1.2.1 From 0a9d9f7d5096aa742564e704a96fa7c40eeaf007 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 17:55:23 +0800 Subject: Fetch the current SHA if SHA was not passed --- app/models/project.rb | 3 ++- spec/models/project_spec.rb | 38 ++++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index dc9b4b38a10..2969bec0bf7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1086,7 +1086,8 @@ class Project < ActiveRecord::Base !namespace.share_with_group_lock end - def pipeline_for(ref, sha) + def pipeline_for(ref, sha = commit(ref).try(:sha)) + return unless sha pipelines.order(id: :desc).find_by(sha: sha, ref: ref) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 60819fe02be..1ce306c4f39 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -684,23 +684,37 @@ describe Project, models: true do end end - describe '#pipeline' do - let(:project) { create :project } - let(:pipeline) { create :ci_pipeline, project: project, ref: 'master' } - - subject { project.pipeline_for('master', pipeline.sha) } + describe '#pipeline_for' do + let(:project) { create(:project) } + let!(:pipeline) { create_pipeline } - it { is_expected.to eq(pipeline) } + shared_examples 'giving the correct pipeline' do + it { is_expected.to eq(pipeline) } - context 'return latest' do - let(:pipeline2) { create :ci_pipeline, project: project, ref: 'master' } + context 'return latest' do + let!(:pipeline2) { create_pipeline } - before do - pipeline - pipeline2 + it { is_expected.to eq(pipeline2) } end + end + + context 'with explicit sha' do + subject { project.pipeline_for('master', pipeline.sha) } + + it_behaves_like 'giving the correct pipeline' + end + + context 'with implicit sha' do + subject { project.pipeline_for('master') } + + it_behaves_like 'giving the correct pipeline' + end - it { is_expected.to eq(pipeline2) } + def create_pipeline + create(:ci_pipeline, + project: project, + ref: 'master', + sha: project.commit('master').sha) end end -- cgit v1.2.1 From 71046cc62919846b52d3724f7277ca14bb3a3a81 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 17:56:55 +0800 Subject: Fix mock --- spec/models/merge_request_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 3270b877c1a..11cfc7cace1 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -463,8 +463,8 @@ describe MergeRequest, models: true do allow(subject).to receive(:diff_head_sha).and_return('123abc') - expect(subject.source_project).to receive(:pipeline). - with('123abc', 'master'). + expect(subject.source_project).to receive(:pipeline_for). + with('master', '123abc'). and_return(pipeline) expect(subject.pipeline).to eq(pipeline) -- cgit v1.2.1 From 2a435e1d116065496fcb82f9b2182f7037d4c8b3 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 18:04:52 +0800 Subject: Remove Pipeline#latest in favour of Project#pipeline_for(ref) --- app/models/ci/pipeline.rb | 4 ---- spec/models/ci/pipeline_spec.rb | 28 +++++++--------------------- 2 files changed, 7 insertions(+), 25 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 50f9ee7fc66..9621bddf8dc 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -102,10 +102,6 @@ module Ci end end - def latest - project.pipelines.latest_for(ref).first - end - def latest? return false unless ref commit = project.commit(ref) diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 556a6e1b59a..2dbc3d985b0 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -483,7 +483,13 @@ describe Ci::Pipeline, models: true do context 'with non-empty project' do let(:project) { create(:project) } - let(:pipeline) { create_pipeline } + + let(:pipeline) do + create(:ci_pipeline, + project: project, + ref: project.default_branch, + sha: project.commit.sha) + end describe '#latest?' do context 'with latest sha' do @@ -503,26 +509,6 @@ describe Ci::Pipeline, models: true do end end end - - describe '#latest' do - let(:previous_pipeline) { create_pipeline } - - before do - previous_pipeline - pipeline - end - - it 'gives the latest pipeline' do - expect(previous_pipeline.latest).to eq(pipeline) - end - end - - def create_pipeline - create(:ci_pipeline, - project: project, - ref: project.default_branch, - sha: project.commit.sha) - end end describe '#manual_actions' do -- cgit v1.2.1 From d84aa560331a646016880e4d2c5c0a3b3d4b32a6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 18:09:26 +0800 Subject: Make Pipeline.latest_successful_for return the record --- app/models/ci/pipeline.rb | 8 ++------ app/models/project.rb | 2 +- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/repositories/_download_archive.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 9621bddf8dc..0289a51eedd 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -21,12 +21,8 @@ module Ci after_save :keep_around_commits # ref can't be HEAD or SHA, can only be branch/tag name - scope :latest_successful_for, ->(ref) do - latest_for(ref).success - end - - scope :latest_for, ->(ref) do - where(ref: ref).order(id: :desc).limit(1) + def self.latest_successful_for(ref) + where(ref: ref).order(id: :desc).success.first end def self.truncate_sha(sha) diff --git a/app/models/project.rb b/app/models/project.rb index 2969bec0bf7..7aa34cdeec8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -432,7 +432,7 @@ class Project < ActiveRecord::Base # ref can't be HEAD, can only be branch/tag name or SHA def latest_successful_builds_for(ref = default_branch) - latest_pipeline = pipelines.latest_successful_for(ref).first + latest_pipeline = pipelines.latest_successful_for(ref) if latest_pipeline latest_pipeline.builds.latest.with_artifacts diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index b096516c627..2029758f30d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,7 +27,7 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - pipeline = @project.pipelines.latest_successful_for(branch.name).first + - pipeline = @project.pipelines.latest_successful_for(branch.name) - if pipeline - artifacts = pipeline.builds.latest.with_artifacts - if artifacts.any? diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index d135b448dd7..e7ef0cbaa91 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -14,7 +14,7 @@ %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = @project.pipelines.latest_successful_for(@ref).first + - pipeline = @project.pipelines.latest_successful_for(@ref) - if pipeline - artifacts = pipeline.builds.latest.with_artifacts - if artifacts.any? diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 183daebfc3a..0ef9ad5f789 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -25,7 +25,7 @@ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar - - pipeline = @project.pipelines.latest_successful_for(ref).first + - pipeline = @project.pipelines.latest_successful_for(ref) - if pipeline - artifacts = pipeline.builds.latest.with_artifacts - if artifacts.any? diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index bb2e5224306..ba3fd4627af 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,7 +12,7 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = project.pipelines.latest_successful_for(ref).first + - pipeline = project.pipelines.latest_successful_for(ref) - if pipeline - artifacts = pipeline.builds.latest.with_artifacts - if artifacts.any? -- cgit v1.2.1 From 62d991c80f0b5716945912ebf2031f56da75a12b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 19:04:45 +0800 Subject: Show latest pipeline status if what provided artifacts aren't latest --- app/views/projects/branches/_branch.html.haml | 5 ++++- app/views/projects/buttons/_download.html.haml | 5 ++++- app/views/projects/repositories/_download_archive.html.haml | 5 ++++- app/views/projects/tags/_download.html.haml | 5 ++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 2029758f30d..8a33cd1502c 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -40,7 +40,10 @@ %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" + - latest_pipeline = @project.pipeline_for(branch.name) + %li + %span= latest_pipeline.status.humanize + %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index e7ef0cbaa91..84135a6d049 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -20,7 +20,10 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" + - latest_pipeline = @project.pipeline_for(@ref) + %li + %span= latest_pipeline.status.humanize + %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 0ef9ad5f789..c9a0b74e47b 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -31,7 +31,10 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(@project.namespace, @project, pipeline))})" + - latest_pipeline = @project.pipeline_for(ref) + %li + %span= latest_pipeline.status.humanize + %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index ba3fd4627af..10e8f77ac0c 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -18,7 +18,10 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - = " (not latest, but #{link_to(pipeline.short_sha, namespace_project_pipeline_path(project.namespace, project, pipeline))})" + - latest_pipeline = @project.pipeline_for(ref) + %li + %span= latest_pipeline.status.humanize + %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li = link_to search_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do -- cgit v1.2.1 From 44b29b5b3b908ee5acd1d35fdf8e75333e7e50c1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 11 Aug 2016 19:15:22 +0800 Subject: Add a CHANGELOG entry --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 42d32e53685..407d183c17d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.11.0 (unreleased) - Fix CI status icon link underline (ClemMakesApps) - The Repository class is now instrumented - Cache the commit author in RequestStore to avoid extra lookups in PostReceive + - Add a button to download latest successful artifacts for branches and tags - Expand commit message width in repo view (ClemMakesApps) - Cache highlighted diff lines for merge requests - Fix of 'Commits being passed to custom hooks are already reachable when using the UI' -- cgit v1.2.1 From d79fb3e3ca47f6d6cd7aa81811d884340a0b0a64 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 00:43:06 +0800 Subject: Fix tests which broke in the merge --- spec/requests/api/commits_spec.rb | 4 ++-- spec/services/ci/image_for_build_service_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 7ca75d77673..5b3dc60aba2 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -95,7 +95,7 @@ describe API::API, api: true do end it "returns status for CI" do - pipeline = project.ensure_pipeline(project.repository.commit.sha, 'master') + pipeline = project.ensure_pipeline('master', project.repository.commit.sha) pipeline.update(status: 'success') get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) @@ -105,7 +105,7 @@ describe API::API, api: true do end it "returns status for CI when pipeline is created" do - project.ensure_pipeline(project.repository.commit.sha, 'master') + project.ensure_pipeline('master', project.repository.commit.sha) get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index c931c3e4829..b3e0a7b9b58 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -5,7 +5,7 @@ module Ci let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:empty_project) } let(:commit_sha) { '01234567890123456789' } - let(:pipeline) { project.ensure_pipeline(commit_sha, 'master') } + let(:pipeline) { project.ensure_pipeline('master', commit_sha) } let(:build) { FactoryGirl.create(:ci_build, pipeline: pipeline) } describe '#execute' do -- cgit v1.2.1 From abf1cffff8afd6dcb181e532378ed1548dd62078 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 00:46:51 +0800 Subject: Fix tests, explicitly set the status --- spec/requests/projects/artifacts_controller_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 952b9fb99b7..61d5e3d9a7d 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -8,7 +8,8 @@ describe Projects::ArtifactsController do create(:ci_pipeline, project: project, sha: project.commit.sha, - ref: project.default_branch) + ref: project.default_branch, + status: 'success') end let(:build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) } -- cgit v1.2.1 From 73bbaffbfcfb24942111726ae6e04170f1b61ccc Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 01:03:08 +0800 Subject: Use URL helper, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13880889 --- app/controllers/projects/artifacts_controller.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 05112571225..b7c395a01a3 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -35,10 +35,14 @@ class Projects::ArtifactsController < Projects::ApplicationController end def search - if params[:path] - url = namespace_project_build_url(project.namespace, project, build) + path = params[:path] - redirect_to "#{url}/artifacts/#{params[:path]}" + if %w[download browse file].include?(path) + redirect_to send( + "#{path}_namespace_project_build_artifacts_url", + project.namespace, + project, + build) else render_404 end -- cgit v1.2.1 From 16b63664f9d73a2ab83feac6eadf959f530208c9 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 01:15:29 +0800 Subject: It's project, not @project --- app/views/projects/tags/_download.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 10e8f77ac0c..27e0cd7b1f9 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -18,7 +18,7 @@ - if artifacts.any? %li.dropdown-header Artifacts - unless pipeline.latest? - - latest_pipeline = @project.pipeline_for(ref) + - latest_pipeline = project.pipeline_for(ref) %li %span= latest_pipeline.status.humanize %li.dropdown-header Previous Artifacts -- cgit v1.2.1 From d8dfa56e95c794a91c0a1185e5e6c0017e144b25 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 01:43:58 +0800 Subject: Fix test by assigning the proper SHA --- spec/features/projects/tags/download_buttons_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb index ebc5204cf1d..6e0022c179f 100644 --- a/spec/features/projects/tags/download_buttons_spec.rb +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -10,7 +10,7 @@ feature 'Download buttons in tags page', feature: true do given(:pipeline) do create(:ci_pipeline, project: project, - sha: project.commit.sha, + sha: project.commit(tag).sha, ref: tag, status: status) end -- cgit v1.2.1 From 1501b1abc4be78eaa4cb4c28969d9bc59bcf284c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 15 Aug 2016 23:48:43 +0200 Subject: Fixed bug when a pipeline for latest SHA does not exist --- app/helpers/ci_status_helper.rb | 5 +++++ app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/repositories/_download_archive.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index ea2f5f9281a..0f7fcc0416c 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -25,6 +25,11 @@ module CiStatusHelper end end + def ci_status_for_statuseable(subject) + status = subject.try(:status) || 'not found' + status.humanize + end + def ci_icon_for_status(status) icon_name = case status diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 8a33cd1502c..402e37f4ec6 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -42,7 +42,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(branch.name) %li - %span= latest_pipeline.status.humanize + %span= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 84135a6d049..a86561ca90b 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -22,7 +22,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(@ref) %li - %span= latest_pipeline.status.humanize + %span= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index c9a0b74e47b..e482bb72df4 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -33,7 +33,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(ref) %li - %span= latest_pipeline.status.humanize + %span= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 27e0cd7b1f9..b3bf18cfa50 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -20,7 +20,7 @@ - unless pipeline.latest? - latest_pipeline = project.pipeline_for(ref) %li - %span= latest_pipeline.status.humanize + %span= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li -- cgit v1.2.1 From ad320577595610ee1cb8f945cdfe6f739e9a0ebb Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Mon, 15 Aug 2016 19:07:42 -0500 Subject: Add unclickable state to running build artifacts --- app/assets/stylesheets/framework/dropdowns.scss | 6 ++++++ app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/repositories/_download_archive.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index e8eafa15899..cbbff49ad9c 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -176,6 +176,12 @@ .separator + .dropdown-header { padding-top: 2px; } + + .unclickable { + cursor: not-allowed; + padding: 5px 8px; + color: $dropdown-header-color; + } } .dropdown-menu-large { diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 402e37f4ec6..12b78be4be0 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -42,7 +42,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(branch.name) %li - %span= ci_status_for_statuseable(latest_pipeline) + .unclickable= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index a86561ca90b..45998343fda 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -22,7 +22,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(@ref) %li - %span= ci_status_for_statuseable(latest_pipeline) + .unclickable= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index e482bb72df4..48502498171 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -33,7 +33,7 @@ - unless pipeline.latest? - latest_pipeline = @project.pipeline_for(ref) %li - %span= ci_status_for_statuseable(latest_pipeline) + .unclickable= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index b3bf18cfa50..60f1e2cd2ee 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -20,7 +20,7 @@ - unless pipeline.latest? - latest_pipeline = project.pipeline_for(ref) %li - %span= ci_status_for_statuseable(latest_pipeline) + .unclickable= ci_status_for_statuseable(latest_pipeline) %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li -- cgit v1.2.1 From 11f840bfa5b93fdd0687c9c4f2a5a2e7abbc17ac Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 21:04:06 +0800 Subject: An empty line after guard, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13904931 --- app/models/merge_request.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index cb60b626e75..96fecf7712a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -676,6 +676,7 @@ class MergeRequest < ActiveRecord::Base def pipeline return unless diff_head_sha && source_project + @pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha) end -- cgit v1.2.1 From f86a507745695f3b073d6edb6029836ab115765a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 22:10:10 +0800 Subject: Rename to latest_succeeded, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13908017 --- app/controllers/projects/artifacts_controller.rb | 2 +- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/buttons/_download.html.haml | 2 +- app/views/projects/tags/_download.html.haml | 2 +- config/routes.rb | 7 ++++--- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index b7c395a01a3..5cc6d643b64 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -34,7 +34,7 @@ class Projects::ArtifactsController < Projects::ApplicationController redirect_to namespace_project_build_path(project.namespace, project, build) end - def search + def latest_succeeded path = params[:path] if %w[download browse file].include?(path) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 12b78be4be0..5634abf3641 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -46,7 +46,7 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to search_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do + = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' - if can_remove_branch?(@project, branch.name) diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 45998343fda..177946dcd42 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -26,5 +26,5 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to search_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do + = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 60f1e2cd2ee..316d03dd12a 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -24,5 +24,5 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to search_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do + = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' diff --git a/config/routes.rb b/config/routes.rb index 70bdb1d5beb..da10c5609f6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -765,9 +765,10 @@ Rails.application.routes.draw do resources :artifacts, only: [] do collection do - get :search, path: ':ref_name/*path', - format: false, - constraints: { ref_name: /.+/ } # could have / + get :latest_succeeded, + path: ':ref_name/*path', + format: false, + constraints: { ref_name: /.+/ } # could have / end end end -- cgit v1.2.1 From 1c88ed7a515141b7468b238a679e2ff5cf86e3f9 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 16 Aug 2016 22:14:27 +0800 Subject: Not sure why missed this one --- app/views/projects/repositories/_download_archive.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 48502498171..c0a5909d3a6 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -37,5 +37,5 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to search_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do + = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do %span Download '#{job.name}' -- cgit v1.2.1 From 75df5f6c73670def1912150c6f3390ffdfadb17a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 17 Aug 2016 13:42:06 +0800 Subject: Fixed a missing rename --- spec/requests/projects/artifacts_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 61d5e3d9a7d..1c68ec9117b 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -25,7 +25,7 @@ describe Projects::ArtifactsController do def path_from_ref( ref = pipeline.ref, job = build.name, path = 'browse') - search_namespace_project_artifacts_path( + latest_succeeded_namespace_project_artifacts_path( project.namespace, project, ref, -- cgit v1.2.1 From ee33b3e6e84bb566e84062e70d45c6d84ace4ee7 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 17 Aug 2016 17:06:31 +0800 Subject: Use partials for downloading artifacts button --- app/views/projects/branches/_branch.html.haml | 30 +++++++--------------- app/views/projects/buttons/_artifacts.html.haml | 14 ++++++++++ app/views/projects/buttons/_download.html.haml | 15 +---------- .../repositories/_download_archive.html.haml | 15 +---------- app/views/projects/tags/_download.html.haml | 15 +---------- 5 files changed, 26 insertions(+), 63 deletions(-) create mode 100644 app/views/projects/buttons/_artifacts.html.haml diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 5634abf3641..21ac675ffe0 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,27 +27,15 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - pipeline = @project.pipelines.latest_successful_for(branch.name) - - if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - .dropdown.inline.artifacts-btn - %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } - = icon('download') - %span.caret - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = @project.pipeline_for(branch.name) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, branch.name, 'download', job: job.name), rel: 'nofollow' do - %span Download '#{job.name}' + - if @project.latest_successful_builds_for(branch.name).any? + .dropdown.inline.artifacts-btn + %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } + = icon('download') + %span.caret + %span.sr-only + Select Archive Format + %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } + = render 'projects/buttons/artifacts', project: @project, ref: branch.name - if can_remove_branch?(@project, branch.name) = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do diff --git a/app/views/projects/buttons/_artifacts.html.haml b/app/views/projects/buttons/_artifacts.html.haml new file mode 100644 index 00000000000..a52677ebf0a --- /dev/null +++ b/app/views/projects/buttons/_artifacts.html.haml @@ -0,0 +1,14 @@ +- pipeline = project.pipelines.latest_successful_for(ref) +- if pipeline + - artifacts = pipeline.builds.latest.with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + - latest_pipeline = project.pipeline_for(ref) + %li + .unclickable= ci_status_for_statuseable(latest_pipeline) + %li.dropdown-header Previous Artifacts + - artifacts.each do |job| + %li + = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do + %span Download '#{job.name}' diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 177946dcd42..5e748c44b08 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -14,17 +14,4 @@ %li = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = @project.pipelines.latest_successful_for(@ref) - - if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = @project.pipeline_for(@ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, @ref, 'download', job: job.name), rel: 'nofollow' do - %span Download '#{job.name}' + = render 'projects/buttons/artifacts', project: @project, ref: @ref diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index c0a5909d3a6..4f40696a190 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -25,17 +25,4 @@ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do %i.fa.fa-download %span Download tar - - pipeline = @project.pipelines.latest_successful_for(ref) - - if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = @project.pipeline_for(ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(@project.namespace, @project, ref, 'download', job: job.name), rel: 'nofollow' do - %span Download '#{job.name}' + = render 'projects/buttons/artifacts', project: @project, ref: ref diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml index 316d03dd12a..6985eb74ca7 100644 --- a/app/views/projects/tags/_download.html.haml +++ b/app/views/projects/tags/_download.html.haml @@ -12,17 +12,4 @@ %li = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do %span Download tar.gz - - pipeline = project.pipelines.latest_successful_for(ref) - - if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = project.pipeline_for(ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do - %span Download '#{job.name}' + = render 'projects/buttons/artifacts', project: project, ref: ref -- cgit v1.2.1 From 4fbe044b74aa6a24133732ef8a9bc5063ecef5dd Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 17 Aug 2016 17:38:38 +0800 Subject: Use switch case in a helper, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_13988401 --- app/controllers/projects/artifacts_controller.rb | 10 +++------- app/helpers/gitlab_routing_helper.rb | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 5cc6d643b64..8261a73c642 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -35,14 +35,10 @@ class Projects::ArtifactsController < Projects::ApplicationController end def latest_succeeded - path = params[:path] + target_url = artifacts_action_url(params[:path], project, build) - if %w[download browse file].include?(path) - redirect_to send( - "#{path}_namespace_project_build_artifacts_url", - project.namespace, - project, - build) + if target_url + redirect_to(target_url) else render_404 end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index 5386ddadc62..bc4d976ae68 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -149,4 +149,19 @@ module GitlabRoutingHelper def resend_invite_group_member_path(group_member, *args) resend_invite_group_group_member_path(group_member.source, group_member) end + + # Artifacts + + def artifacts_action_url(path, project, build) + args = [project.namespace, project, build] + + case path + when 'download' + download_namespace_project_build_artifacts_url(*args) + when 'browse' + browse_namespace_project_build_artifacts_url(*args) + when 'file' + file_namespace_project_build_artifacts_url(*args) + end + end end -- cgit v1.2.1 From e8b03baf6b67a14c0db6dbf3a1abaa4a6a173213 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 18 Aug 2016 15:31:20 +0800 Subject: Use path rather than URL because it should work for http 302: Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14035941 --- app/controllers/projects/artifacts_controller.rb | 4 ++-- app/helpers/gitlab_routing_helper.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 8261a73c642..16ab7ec409d 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -35,10 +35,10 @@ class Projects::ArtifactsController < Projects::ApplicationController end def latest_succeeded - target_url = artifacts_action_url(params[:path], project, build) + target_path = artifacts_action_path(params[:path], project, build) if target_url - redirect_to(target_url) + redirect_to(target_path) else render_404 end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index bc4d976ae68..cd526f17b99 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -152,16 +152,16 @@ module GitlabRoutingHelper # Artifacts - def artifacts_action_url(path, project, build) + def artifacts_action_path(path, project, build) args = [project.namespace, project, build] case path when 'download' - download_namespace_project_build_artifacts_url(*args) + download_namespace_project_build_artifacts_path(*args) when 'browse' - browse_namespace_project_build_artifacts_url(*args) + browse_namespace_project_build_artifacts_path(*args) when 'file' - file_namespace_project_build_artifacts_url(*args) + file_namespace_project_build_artifacts_path(*args) end end end -- cgit v1.2.1 From 17d0406546885bedf2196c61a5991092b3fbe7c0 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 18 Aug 2016 19:30:20 +0800 Subject: Not sure why I missed this renaming --- app/controllers/projects/artifacts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 16ab7ec409d..60e432d68d8 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -37,7 +37,7 @@ class Projects::ArtifactsController < Projects::ApplicationController def latest_succeeded target_path = artifacts_action_path(params[:path], project, build) - if target_url + if target_path redirect_to(target_path) else render_404 -- cgit v1.2.1 From 1aba3a5c0e7c2b727f4317aab19bbc662f0fd727 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 19 Aug 2016 17:42:49 +0800 Subject: Unify pipeline_for(ref, nil) and pipeline_for(ref), feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14073464 --- app/models/project.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 678fca7afd1..e97e6abfef9 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1094,8 +1094,11 @@ class Project < ActiveRecord::Base !namespace.share_with_group_lock end - def pipeline_for(ref, sha = commit(ref).try(:sha)) + def pipeline_for(ref, sha = nil) + sha ||= commit(ref).try(:sha) + return unless sha + pipelines.order(id: :desc).find_by(sha: sha, ref: ref) end -- cgit v1.2.1 From eadb1dc547ac1d0e3eb964d077f0b1580d588a95 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 19 Aug 2016 18:46:15 +0800 Subject: Make sure the branch we're testing is on the 1st page! --- spec/features/projects/branches/download_buttons_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index a223786777b..04058300570 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -9,8 +9,8 @@ feature 'Download buttons in branches page', feature: true do given(:pipeline) do create(:ci_pipeline, project: project, - sha: project.commit.sha, - ref: project.default_branch, + sha: project.commit('binary-encoding').sha, + ref: 'binary-encoding', # make sure the branch is in the 1st page! status: status) end -- cgit v1.2.1 From cbcf00e1b972265713bb68cc679216398a44c993 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Fri, 19 Aug 2016 12:12:50 +0100 Subject: Keep committing ... spinner when conflict resolved When a conflict is resolved, we don't want to change the spinner back to the normal button before the new page load happens, so only do this on failure. --- app/assets/javascripts/merge_conflict_resolver.js.es6 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/assets/javascripts/merge_conflict_resolver.js.es6 b/app/assets/javascripts/merge_conflict_resolver.js.es6 index 77bffbcb403..b56fd5aa658 100644 --- a/app/assets/javascripts/merge_conflict_resolver.js.es6 +++ b/app/assets/javascripts/merge_conflict_resolver.js.es6 @@ -75,10 +75,8 @@ class MergeConflictResolver { window.location.href = data.redirect_to; }) .error(() => { - new Flash('Something went wrong!'); - }) - .always(() => { this.vue.isSubmitting = false; + new Flash('Something went wrong!'); }); } -- cgit v1.2.1 From e4e03d946e1c830973db776a570fc89f66917ff3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 19 Aug 2016 17:21:00 +0200 Subject: Added performance guidelines for new MRs These guidelines cover the performance requirement for newly submitted merge requests. These guidelines are put in to place to prevent merge requests from negatively impacting GitLab performance as much as possible. --- CONTRIBUTING.md | 2 + doc/development/README.md | 2 + .../merge_request_performance_guidelines.md | 171 +++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 doc/development/merge_request_performance_guidelines.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fbc8e15bebf..4f10f90a82d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -340,6 +340,8 @@ request is as follows: migrations on a fresh database before the MR is reviewed. If the review leads to large changes in the MR, do this again once the review is complete. 1. For more complex migrations, write tests. +1. Merge requests **must** adhere to the [merge request performance + guidelines](doc/development/merge_request_performance_guidelines.md). The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. This is the best time to submit an MR and get diff --git a/doc/development/README.md b/doc/development/README.md index 57f37da6f80..58c00f618fa 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -18,6 +18,8 @@ ## Process - [Code review guidelines](code_review.md) for reviewing code and having code reviewed. +- [Merge request performance guidelines](merge_request_performance_guidelines.md) + for ensuring merge requests do not negatively impact GitLab performance ## Backend howtos diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md new file mode 100644 index 00000000000..0363bf8c1d5 --- /dev/null +++ b/doc/development/merge_request_performance_guidelines.md @@ -0,0 +1,171 @@ +# Merge Request Performance Guidelines + +To ensure a merge request does not negatively impact performance of GitLab +_every_ merge request **must** adhere to the guidelines outlined in this +document. There are no exceptions to this rule unless specifically discussed +with and agreed upon by merge request endbosses and performance specialists. + +To measure the impact of a merge request you can use +[Sherlock](profiling.md#sherlock). It's also highly recommended that you read +the following guides: + +* [Performance Guidelines](performance.md) +* [What requires downtime?](what_requires_downtime.md) + +## Impact Analysis + +**Summary:** think about the impact your merge request may have on performance +and those maintaining a GitLab setup. + +Any change submitted can have an impact not only on the application itself but +also those maintaining it and those keeping it up and running (e.g. production +engineers). As a result you should think carefully about the impact of your +merge request on not only the application but also on the people keeping it up +and running. + +Can the queries used potentially take down any critical services and result in +engineers being woken up in the night? Can a malicious user abuse the code to +take down a GitLab instance? Will my changes simply make loading a certain page +slower? Will execution time grow exponentially given enough load or data in the +database? + +These are all questions one should ask themselves before submitting a merge +request. It may sometimes be difficult to assess the impact, in which case you +should ask a performance specialist to review your code. See the "Reviewing" +section below for more information. + +## Performance Review + +**Summary:** ask performance specialists to review your code if you're not sure +about the impact. + +Sometimes it's hard to assess the impact of a merge request. In this case you +should ask one of the merge request (mini) endbosses to review your changes. You +can find a list of these endbosses at . An +endboss in turn can request a performance specialist to review the changes. + +## Query Counts + +**Summary:** a merge request **should not** increase the number of executed SQL +queries unless absolutely necessary. + +The number of queries executed by the code modified or added by a merge request +must not increase unless absolutely necessary. When building features it's +entirely possible you will need some extra queries, but you should try to keep +this at a minimum. + +As an example, say you introduce a feature that updates a number of database +rows with the same value. It may be very tempting (and easy) to write this using +the following pseudo code: + +```ruby +objects_to_update.each do |object| + object.some_field = some_value + object.save +end +``` + +This will end up running one query for every object to update. This code can +easily overload a database given enough rows to update or many instances of this +code running in parallel. This particular problem is known as the +["N+1 query problem"](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations). + +In this particular case the workaround is fairly easy: + +```ruby +objects_to_update.update_all(some_field: some_value) +``` + +This uses ActiveRecord's `update_all` method to update all rows in a single +query. This in turn makes it much harder for this code to overload a database. + +## Executing Queries in Loops + +**Summary:** SQL queries **must not** be executed in a loop unless absolutely +necessary. + +Executing SQL queries in a loop can result in many queries being executed +depending on the number of iterations in a loop. This may work fine for a +development environment with little data, but in a production environment this +can quickly spiral out of control. + +There are some cases where this may be needed. If this is the case this should +be clearly mentioned in the merge request description. + +## Eager Loading + +**Summary:** always eager load associations when retrieving more than one row. + +When retrieving multiple database records for which you need to use any +associations you **must** eager load these associations. For example, if you're +retrieving a list of blog posts and you want to display their authors you +**must** eager load the author associations. + +In other words, instead of this: + +```ruby +Post.all.each do |post| + puts post.author.name +end +``` + +You should use this: + +```ruby +Post.all.includes(:author).each do |post| + puts post.author.name +end +``` + +## Memory Usage + +**Summary:** merge requests **must not** increase memory usage unless absolutely +necessary. + +A merge request must not increase the memory usage of GitLab by more than the +absolute bare minimum required by the code. This means that if you have to parse +some large document (e.g. an HTML document) it's best to parse it as a stream +whenever possible, instead of loading the entire input into memory. Sometimes +this isn't possible, in that case this should be stated explicitly in the merge +request. + +## Lazy Rendering of UI Elements + +**Summary:** only render UI elements when they're actually needed. + +Certain UI elements may not always be needed. For example, when hovering over a +diff line there's a small icon displayed that can be used to create a new +comment. Instead of always rendering these kind of elements they should only be +rendered when actually needed. This ensures we don't spend time generating +Haml/HTML when it's not going to be used. + +## Instrumenting New Code + +**Summary:** always add instrumentation for new classes, modules, and methods. + +Newly added classes, modules, and methods must be instrumented. This ensures +we can track the performance of this code over time. + +For more information see [Instrumentation](instrumentation.md). This guide +describes how to add instrumentation and where to add it. + +## Use of Caching + +**Summary:** cache data in memory or in Redis when it's needed multiple times in +a transaction or has to be kept around for a certain time period. + +Sometimes certain bits of data have to be re-used in different places during a +transaction. In these cases this data should be cached in memory to remove the +need for running complex operations to fetch the data. You should use Redis if +data should be cached for a certain time period instead of the duration of the +transaction. + +For example, say you process multiple snippets of text containiner username +mentions (e.g. `Hello @alice` and `How are you doing @alice?`). By caching the +user objects for every username we can remove the need for running the same +query for every mention of `@alice`. + +Caching data per transaction can be done using +[RequestStore](https://github.com/steveklabnik/request_store). Caching data in +Redis can be done using [Rails' caching +system](http://guides.rubyonrails.org/caching_with_rails.html). -- cgit v1.2.1 From 2c909a5a3c219cad6e6ef8c78a4984ab311d14cb Mon Sep 17 00:00:00 2001 From: Bryce Date: Tue, 23 Aug 2016 10:39:31 +0200 Subject: Check for pipelines when setting page_gutter_class. --- CHANGELOG | 1 + app/helpers/nav_helper.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 0ea728cf89b..5740ba89791 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ v 8.12.0 (unreleased) - Optimistic locking for Issues and Merge Requests (title and description overriding prevention) v 8.11.0 (unreleased) + - Fix pipelines tab layout regression (brycepj) - Use test coverage value from the latest successful pipeline in badge. !5862 - Add test coverage report badge. !5708 - Remove the http_parser.rb dependency by removing the tinder gem. !5758 (tbalthazar) diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 6c1cc6ef072..2b0ff6c0d00 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -25,6 +25,8 @@ module NavHelper current_path?('merge_requests#commits') || current_path?('merge_requests#builds') || current_path?('merge_requests#conflicts') || + current_path?('merge_requests#pipelines') || + current_path?('issues#show') if cookies[:collapsed_gutter] == 'true' "page-gutter right-sidebar-collapsed" -- cgit v1.2.1 From 4b40767c0e4899188bb86edac1b05ab9f7403d4f Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Tue, 23 Aug 2016 11:28:11 +0100 Subject: First pass for community issue templates * Include "Bug" template from the contribution guide * Include "Feature Proposal" template from the contribution guide --- .gitlab/issue_templates/Bug.md | 40 +++++++++++++++++++++++++++++ .gitlab/issue_templates/Feature Proposal.md | 6 +++++ 2 files changed, 46 insertions(+) create mode 100644 .gitlab/issue_templates/Bug.md create mode 100644 .gitlab/issue_templates/Feature Proposal.md diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md new file mode 100644 index 00000000000..139d97034a2 --- /dev/null +++ b/.gitlab/issue_templates/Bug.md @@ -0,0 +1,40 @@ +## Summary + +(Summarize your issue in one sentence - what goes wrong, what did you expect to happen) + +## Steps to reproduce + +(How one can reproduce the issue - this is very important) + +## Expected behavior + +(What you should see instead) + +## Relevant logs and/or screenshots + +(Paste any relevant logs - please use code blocks (```) to format console output, +logs, and code as it's very hard to read otherwise.) + +## Output of checks + +### Results of GitLab Application Check + +(For installations with omnibus-gitlab package run and paste the output of: +sudo gitlab-rake gitlab:check SANITIZE=true) + +(For installations from source run and paste the output of: +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true) + +(we will only investigate if the tests are passing) + +### Results of GitLab Environment Info + +(For installations with omnibus-gitlab package run and paste the output of: +sudo gitlab-rake gitlab:env:info) + +(For installations from source run and paste the output of: +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production) + +## Possible fixes + +(If you can, link to the line of code that might be responsible for the problem) diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature Proposal.md new file mode 100644 index 00000000000..ea009c32296 --- /dev/null +++ b/.gitlab/issue_templates/Feature Proposal.md @@ -0,0 +1,6 @@ +## Description +Include problem, use cases, benefits, and/or goals + +## Proposal + +## Links / references -- cgit v1.2.1 From cc94714c05a5088db7e30b9582a187e5ee52068b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Tue, 23 Aug 2016 08:45:18 -0500 Subject: Add body as data-container on pipeline graph tooltip --- app/helpers/ci_status_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index bb285a17baf..0327b476d18 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -66,10 +66,10 @@ module CiStatusHelper Ci::Runner.shared.blank? end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '') + def render_status_with_link(type, status, path = nil, tooltip_placement: 'auto left', cssclass: '', container: 'body') klass = "ci-status-link ci-status-icon-#{status.dasherize} #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" - data = { toggle: 'tooltip', placement: tooltip_placement } + data = { toggle: 'tooltip', placement: tooltip_placement, container: container } if path link_to ci_icon_for_status(status), path, -- cgit v1.2.1 From bc3493f9474d2557c1c30bf30a61e4cd51ece0f1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 24 Aug 2016 14:40:18 +0800 Subject: Use only one before block, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142/diffs#note_14347758 --- spec/requests/projects/artifacts_controller_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 1c68ec9117b..3ba6725efc3 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -17,9 +17,7 @@ describe Projects::ArtifactsController do describe 'GET /:project/builds/artifacts/:ref_name/browse?job=name' do before do project.team << [user, :developer] - end - before do login_as(user) end -- cgit v1.2.1 From e65bc0f175c54d9df66fd4950972c0b0b08d448e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 24 Aug 2016 16:02:56 +0800 Subject: Path could also have slashes! Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14347729 --- app/controllers/projects/artifacts_controller.rb | 14 +++++++++++--- app/helpers/gitlab_routing_helper.rb | 5 +++-- config/routes.rb | 5 ++--- .../requests/projects/artifacts_controller_spec.rb | 22 ++++++++++++++++++++-- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 60e432d68d8..17c6d56c8b9 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -1,4 +1,6 @@ class Projects::ArtifactsController < Projects::ApplicationController + include ExtractsPath + layout 'project' before_action :authorize_read_build! before_action :authorize_update_build!, only: [:keep] @@ -35,7 +37,8 @@ class Projects::ArtifactsController < Projects::ApplicationController end def latest_succeeded - target_path = artifacts_action_path(params[:path], project, build) + path = ref_name_and_path.last + target_path = artifacts_action_path(path, project, build) if target_path redirect_to(target_path) @@ -59,13 +62,18 @@ class Projects::ArtifactsController < Projects::ApplicationController end def build_from_ref - if params[:ref_name] - builds = project.latest_successful_builds_for(params[:ref_name]) + if params[:ref_name_and_path] + ref_name = ref_name_and_path.first + builds = project.latest_successful_builds_for(ref_name) builds.find_by(name: params[:job]) end end + def ref_name_and_path + @ref_name_and_path ||= extract_ref(params[:ref_name_and_path]) + end + def artifacts_file @artifacts_file ||= build.artifacts_file end diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index cd526f17b99..a322a90cc4e 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -153,9 +153,10 @@ module GitlabRoutingHelper # Artifacts def artifacts_action_path(path, project, build) - args = [project.namespace, project, build] + action, path_params = path.split('/', 2) + args = [project.namespace, project, build, path_params] - case path + case action when 'download' download_namespace_project_build_artifacts_path(*args) when 'browse' diff --git a/config/routes.rb b/config/routes.rb index 606181ff837..879cd61a02f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -787,9 +787,8 @@ Rails.application.routes.draw do resources :artifacts, only: [] do collection do get :latest_succeeded, - path: ':ref_name/*path', - format: false, - constraints: { ref_name: /.+/ } # could have / + path: '*ref_name_and_path', + format: false end end end diff --git a/spec/requests/projects/artifacts_controller_spec.rb b/spec/requests/projects/artifacts_controller_spec.rb index 3ba6725efc3..e02f0eacc93 100644 --- a/spec/requests/projects/artifacts_controller_spec.rb +++ b/spec/requests/projects/artifacts_controller_spec.rb @@ -26,8 +26,7 @@ describe Projects::ArtifactsController do latest_succeeded_namespace_project_artifacts_path( project.namespace, project, - ref, - path, + [ref, path].join('/'), job: job) end @@ -94,6 +93,25 @@ describe Projects::ArtifactsController do it_behaves_like 'redirect to the build' end + + context 'with branch name and path containing slashes' do + before do + pipeline.update(ref: 'improve/awesome', + sha: project.commit('improve/awesome').sha) + + get path_from_ref('improve/awesome', build.name, 'file/README.md') + end + + it 'redirects' do + path = file_namespace_project_build_artifacts_path( + project.namespace, + project, + build, + 'README.md') + + expect(response).to redirect_to(path) + end + end end end end -- cgit v1.2.1 From 8f197315b3ec354cb0cc0af4acbe54d6aa01d71b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 24 Aug 2016 19:09:10 +0800 Subject: Aggressively merge views, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14347679 https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14347470 --- app/assets/stylesheets/framework/lists.scss | 4 --- app/views/projects/branches/_branch.html.haml | 10 +----- app/views/projects/buttons/_artifacts.html.haml | 14 -------- app/views/projects/buttons/_download.html.haml | 38 ++++++++++++++++++---- .../repositories/_download_archive.html.haml | 28 ---------------- app/views/projects/show.html.haml | 4 +-- app/views/projects/tags/_download.html.haml | 15 --------- app/views/projects/tags/_tag.html.haml | 3 +- app/views/projects/tags/show.html.haml | 3 +- app/views/projects/tree/show.html.haml | 3 +- 10 files changed, 37 insertions(+), 85 deletions(-) delete mode 100644 app/views/projects/buttons/_artifacts.html.haml delete mode 100644 app/views/projects/repositories/_download_archive.html.haml delete mode 100644 app/views/projects/tags/_download.html.haml diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index a88c7906f5d..965fcc06518 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -162,10 +162,6 @@ ul.content-list { margin-right: 0; } } - - .artifacts-btn { - margin-right: 10px; - } } // When dragging a list item diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 21ac675ffe0..c5549f86e38 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -27,15 +27,7 @@ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-default', method: :post, title: "Compare" do Compare - - if @project.latest_successful_builds_for(branch.name).any? - .dropdown.inline.artifacts-btn - %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } - = icon('download') - %span.caret - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - = render 'projects/buttons/artifacts', project: @project, ref: branch.name + = render 'projects/buttons/download', project: @project, ref: branch.name - if can_remove_branch?(@project, branch.name) = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do diff --git a/app/views/projects/buttons/_artifacts.html.haml b/app/views/projects/buttons/_artifacts.html.haml deleted file mode 100644 index a52677ebf0a..00000000000 --- a/app/views/projects/buttons/_artifacts.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- pipeline = project.pipelines.latest_successful_for(ref) -- if pipeline - - artifacts = pipeline.builds.latest.with_artifacts - - if artifacts.any? - %li.dropdown-header Artifacts - - unless pipeline.latest? - - latest_pipeline = project.pipeline_for(ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts - - artifacts.each do |job| - %li - = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do - %span Download '#{job.name}' diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 5e748c44b08..73dcb9c079e 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,17 +1,41 @@ -- unless @project.empty_repo? - - if can? current_user, :download_code, @project - .dropdown.inline.btn-group +- if !project.empty_repo? && can?(current_user, :download_code, project) + %span.btn-group{class: 'hidden-xs hidden-sm btn-grouped'} + .dropdown.inline %button.btn{ 'data-toggle' => 'dropdown' } = icon('download') - = icon('caret-down') + %span.caret %span.sr-only Select Archive Format %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } %li.dropdown-header Source code %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do + %i.fa.fa-download %span Download zip %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'tar.gz'), rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do + %i.fa.fa-download %span Download tar.gz - = render 'projects/buttons/artifacts', project: @project, ref: @ref + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar.bz2 + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar + + - pipeline = project.pipelines.latest_successful_for(ref) + - if pipeline + - artifacts = pipeline.builds.latest.with_artifacts + - if artifacts.any? + %li.dropdown-header Artifacts + - unless pipeline.latest? + - latest_pipeline = project.pipeline_for(ref) + %li + .unclickable= ci_status_for_statuseable(latest_pipeline) + %li.dropdown-header Previous Artifacts + - artifacts.each do |job| + %li + = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do + %span Download '#{job.name}' diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml deleted file mode 100644 index 4f40696a190..00000000000 --- a/app/views/projects/repositories/_download_archive.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -- ref = ref || nil -- btn_class = btn_class || '' -%span.btn-group{class: btn_class} - .dropdown.inline - %button.btn{ 'data-toggle' => 'dropdown' } - = icon('download') - %span.caret - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - %li.dropdown-header Source code - %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do - %i.fa.fa-download - %span Download zip - %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar.gz - %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar.bz2 - %li - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do - %i.fa.fa-download - %span Download tar - = render 'projects/buttons/artifacts', project: @project, ref: ref diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a666d07e9eb..7130ebaa743 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -70,7 +70,7 @@ = render 'shared/members/access_request_buttons', source: @project .btn-group.project-repo-btn-group - = render "projects/buttons/download" + = render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/dropdown' = render 'shared/notifications/button', notification_setting: @notification_setting @@ -86,4 +86,4 @@ Archived project! Repository is read-only %div{class: "project-show-#{default_project_view}"} - = render default_project_view \ No newline at end of file + = render default_project_view diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml deleted file mode 100644 index 6985eb74ca7..00000000000 --- a/app/views/projects/tags/_download.html.haml +++ /dev/null @@ -1,15 +0,0 @@ -.dropdown.inline - %a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } - = icon('download') - %span.caret - %span.sr-only - Select Archive Format - %ul.dropdown-menu.dropdown-menu-align-right{ role: 'menu' } - %li.dropdown-header Source code - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do - %span Download zip - %li - = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do - %span Download tar.gz - = render 'projects/buttons/artifacts', project: project, ref: ref diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 2c11c0e5b21..a156d98bab8 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -11,8 +11,7 @@ = strip_gpg_signature(tag.message) .controls - - if can?(current_user, :download_code, @project) - = render 'projects/tags/download', ref: tag.name, project: @project + = render 'projects/buttons/download', project: @project, ref: tag.name - if can?(current_user, :push_code, @project) = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 395d7af6cbb..4dd7439b2d0 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -12,8 +12,7 @@ = icon('files-o') = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do = icon('history') - - if can? current_user, :download_code, @project - = render 'projects/tags/download', ref: @tag.name, project: @project + = render 'projects/buttons/download', project: @project, ref: @tag.name - if can?(current_user, :admin_project, @project) .pull-right = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index c68f86f1378..37d341212af 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -10,8 +10,7 @@ %div{ class: container_class } .tree-controls = render 'projects/find_file_link' - - if can? current_user, :download_code, @project - = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped' + = render 'projects/buttons/download', project: @project, ref: @ref #tree-holder.tree-holder.clearfix .nav-block -- cgit v1.2.1 From 6953d988ab141863cba4c38c52b6d1af23c9af3e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 24 Aug 2016 20:57:47 +0800 Subject: Update CHANGELOG from v8.11.0 to v8.12.0 --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 549b86f76ea..33eecc2c9fd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.12.0 (unreleased) - Change merge_error column from string to text type - Optimistic locking for Issues and Merge Requests (title and description overriding prevention) - Added tests for diff notes + - Add a button to download latest successful artifacts for branches and tags !5142 v 8.11.1 (unreleased) - Fix file links on project page when default view is Files !5933 @@ -42,7 +43,6 @@ v 8.11.0 - Do not escape URI when extracting path !5878 (winniehell) - Fix filter label tooltip HTML rendering (ClemMakesApps) - Cache the commit author in RequestStore to avoid extra lookups in PostReceive - - Add a button to download latest successful artifacts for branches and tags - Expand commit message width in repo view (ClemMakesApps) - Cache highlighted diff lines for merge requests - Pre-create all builds for a Pipeline when the new Pipeline is created !5295 -- cgit v1.2.1 From b1add60648554d5f5ae6990b95405d2b97aac627 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Wed, 24 Aug 2016 14:56:32 +0100 Subject: Reflect template addition in the contributing guide Adjust bug template to adhere to standard bug report format * https://developer.mozilla.org/en-US/docs/Mozilla/QA/Bug_writing_guidelines * Guidelines use the following format - Summary - Steps to Reproduce - Expected Behaviour - Actual Behaviour --- .gitlab/issue_templates/Bug.md | 6 ++++- CONTRIBUTING.md | 59 +++--------------------------------------- 2 files changed, 8 insertions(+), 57 deletions(-) diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index 139d97034a2..f6f5879c21a 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,6 +1,6 @@ ## Summary -(Summarize your issue in one sentence - what goes wrong, what did you expect to happen) +(Summarize the bug encountered concisely) ## Steps to reproduce @@ -10,6 +10,10 @@ (What you should see instead) +## Actual behaviour + +(What actually happens) + ## Relevant logs and/or screenshots (Paste any relevant logs - please use code blocks (```) to format console output, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d8093a61b4c..9f4b3d5d244 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,7 +129,7 @@ request that potentially fixes it. ### Feature proposals -To create a feature proposal for CE and CI, open an issue on the +To create a feature proposal for CE, open an issue on the [issue tracker of CE][ce-tracker]. For feature proposals for EE, open an issue on the @@ -144,16 +144,7 @@ code snippet right after your description in a new line: `~"feature proposal"`. Please keep feature proposals as small and simple as possible, complex ones might be edited to make them small and simple. -You are encouraged to use the template below for feature proposals. - -``` -## Description -Include problem, use cases, benefits, and/or goals - -## Proposal - -## Links / references -``` +Please submit Feature Proposals using the 'Feature Proposal' issue template provided on the issue tracker. For changes in the interface, it can be helpful to create a mockup first. If you want to create something yourself, consider opening an issue first to @@ -166,55 +157,11 @@ submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an award emoji and/or join the discussion. -Please submit bugs using the following template in the issue description area. +Please submit bugs using the 'Bug' issue template provided on the issue tracker. The text in the parenthesis is there to help you with what to include. Omit it when submitting the actual issue. You can copy-paste it and then edit as you see fit. -``` -## Summary - -(Summarize your issue in one sentence - what goes wrong, what did you expect to happen) - -## Steps to reproduce - -(How one can reproduce the issue - this is very important) - -## Expected behavior - -(What you should see instead) - -## Relevant logs and/or screenshots - -(Paste any relevant logs - please use code blocks (```) to format console output, -logs, and code as it's very hard to read otherwise.) - -## Output of checks - -### Results of GitLab Application Check - -(For installations with omnibus-gitlab package run and paste the output of: -sudo gitlab-rake gitlab:check SANITIZE=true) - -(For installations from source run and paste the output of: -sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true) - -(we will only investigate if the tests are passing) - -### Results of GitLab Environment Info - -(For installations with omnibus-gitlab package run and paste the output of: -sudo gitlab-rake gitlab:env:info) - -(For installations from source run and paste the output of: -sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production) - -## Possible fixes - -(If you can, link to the line of code that might be responsible for the problem) - -``` - ### Issue weight Issue weight allows us to get an idea of the amount of work required to solve -- cgit v1.2.1 From 831fc817a62d66f4ac4eac8e8cc0b76d593e6448 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 24 Aug 2016 15:51:33 +0100 Subject: Moved `.option-hidden` to exist only in `SELECTABLE_CLASSES` as `NON_SELECTABLE_CLASSES` is used to re-show previously hidden elements --- app/assets/javascripts/gl_dropdown.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 0179b320a3b..2daf1b77e13 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -117,7 +117,7 @@ } }); } else { - return elements.show(); + return elements.show().removeClass('option-hidden'); } } }; @@ -190,9 +190,9 @@ currentIndex = -1; - NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link, .option-hidden'; + NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-link'; - SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ")"; + SELECTABLE_CLASSES = ".dropdown-content li:not(" + NON_SELECTABLE_CLASSES + ", .option-hidden)"; CURSOR_SELECT_SCROLL_PADDING = 5 -- cgit v1.2.1 From 1bc53bc8702ea0cda02a368ba9ca0516ad1e3947 Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 24 Aug 2016 19:29:25 -0500 Subject: Ensure we update the label after input is added --- app/assets/javascripts/gl_dropdown.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 0179b320a3b..521ffb405be 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -565,10 +565,6 @@ } else { field.remove(); } - if (this.options.toggleLabel) { - this.updateLabel(selectedObject, el, this); - } - return selectedObject; } else if (el.hasClass(INDETERMINATE_CLASS)) { el.addClass(ACTIVE_CLASS); el.removeClass(INDETERMINATE_CLASS); @@ -578,7 +574,6 @@ if (!field.length && fieldName) { this.addInput(fieldName, value, selectedObject); } - return selectedObject; } else { if (!this.options.multiSelect || el.hasClass('dropdown-clear-active')) { this.dropdown.find("." + ACTIVE_CLASS).removeClass(ACTIVE_CLASS); @@ -590,9 +585,6 @@ field.remove(); } el.addClass(ACTIVE_CLASS); - if (this.options.toggleLabel) { - this.updateLabel(selectedObject, el, this); - } if (value != null) { if (!field.length && fieldName) { this.addInput(fieldName, value, selectedObject); @@ -600,8 +592,14 @@ field.val(value).trigger('change'); } } - return selectedObject; } + + // Update label right after input has been added + if (this.options.toggleLabel) { + this.updateLabel(selectedObject, el, this); + } + + return selectedObject; }; GitLabDropdown.prototype.addInput = function(fieldName, value, selectedObject) { -- cgit v1.2.1 From dd8afbf05d0727a061e8d7bc1bc3c1db5a666116 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 25 Aug 2016 15:04:15 +0800 Subject: Just use instance variable instead, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14400736 --- app/controllers/projects/artifacts_controller.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 17c6d56c8b9..4c63bec90e5 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -37,8 +37,7 @@ class Projects::ArtifactsController < Projects::ApplicationController end def latest_succeeded - path = ref_name_and_path.last - target_path = artifacts_action_path(path, project, build) + target_path = artifacts_action_path(@path, project, build) if target_path redirect_to(target_path) @@ -63,17 +62,13 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref if params[:ref_name_and_path] - ref_name = ref_name_and_path.first + ref_name, @path = extract_ref(params[:ref_name_and_path]) builds = project.latest_successful_builds_for(ref_name) builds.find_by(name: params[:job]) end end - def ref_name_and_path - @ref_name_and_path ||= extract_ref(params[:ref_name_and_path]) - end - def artifacts_file @artifacts_file ||= build.artifacts_file end -- cgit v1.2.1 From 6a2d2bd18d0a07c33200d74a09b7cf9adcb7a84d Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 25 Aug 2016 15:41:37 +0800 Subject: Add a download icon for artifacts, too. Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14400694 --- app/views/projects/buttons/_download.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 73dcb9c079e..5f5e071eb40 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -38,4 +38,5 @@ - artifacts.each do |job| %li = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do + %i.fa.fa-download %span Download '#{job.name}' -- cgit v1.2.1 From 01a1e3925acf01b925ad3b8ca370db30384111d8 Mon Sep 17 00:00:00 2001 From: Miroslav Meca Date: Thu, 25 Aug 2016 12:01:52 +0000 Subject: Update projects.md The wrong example for "Branches". Added option parameters in protect branch section. Here is reason: https://gitlab.com/gitlab-org/gitlab-ee/commit/3ab07b8aae8dae43cfa3aae1306c59ea264a8594 Maybe this section could/should be deleted. Because in file repositories.md it had been deleted: https://gitlab.com/gitlab-org/gitlab-ee/commit/8f3701eff005aeedcebff8ce02074f5056a369b3 --- doc/api/projects.md | 54 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 0e4806e31c5..eacdb1e1ee6 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -998,6 +998,8 @@ is available before it is returned in the JSON response or an empty response is ## Branches +For more information please consult the [Branches](branches.md) documentation. + ### List branches Lists all branches of a project. @@ -1016,56 +1018,46 @@ Parameters: "name": "async", "commit": { "id": "a2b702edecdf41f07b42653eb1abe30ce98b9fca", - "parents": [ - { - "id": "3f94fc7c85061973edc9906ae170cc269b07ca55" - } + "parent_ids": [ + "3f94fc7c85061973edc9906ae170cc269b07ca55" ], - "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee", "message": "give Caolan credit where it's due (up top)", - "author": { - "name": "Jeremy Ashkenas", - "email": "jashkenas@example.com" - }, - "committer": { - "name": "Jeremy Ashkenas", - "email": "jashkenas@example.com" - }, + "author_name": "Jeremy Ashkenas", + "author_email": "jashkenas@example.com", "authored_date": "2010-12-08T21:28:50+00:00", + "committer_name": "Jeremy Ashkenas", + "committer_email": "jashkenas@example.com", "committed_date": "2010-12-08T21:28:50+00:00" }, - "protected": false + "protected": false, + "developers_can_push": false, + "developers_can_merge": false }, { "name": "gh-pages", "commit": { "id": "101c10a60019fe870d21868835f65c25d64968fc", - "parents": [ - { - "id": "9c15d2e26945a665131af5d7b6d30a06ba338aaa" - } + "parent_ids": [ + "9c15d2e26945a665131af5d7b6d30a06ba338aaa" ], - "tree": "fb5cc9d45da3014b17a876ad539976a0fb9b352a", "message": "Underscore.js 1.5.2", - "author": { - "name": "Jeremy Ashkenas", - "email": "jashkenas@example.com" - }, - "committer": { - "name": "Jeremy Ashkenas", - "email": "jashkenas@example.com" - }, + "author_name": "Jeremy Ashkenas", + "author_email": "jashkenas@example.com", "authored_date": "2013-09-07T12:58:21+00:00", + "committer_name": "Jeremy Ashkenas", + "committer_email": "jashkenas@example.com", "committed_date": "2013-09-07T12:58:21+00:00" }, - "protected": false + "protected": false, + "developers_can_push": false, + "developers_can_merge": false } ] ``` -### List single branch +### Single branch -Lists a specific branch of a project. +A specific branch of a project. ``` GET /projects/:id/repository/branches/:branch @@ -1075,6 +1067,8 @@ Parameters: - `id` (required) - The ID or NAMESPACE/PROJECT_NAME of a project - `branch` (required) - The name of the branch. +- `developers_can_push` - Flag if developers can push to the branch. +- `developers_can_merge` - Flag if developers can merge to the branch. ### Protect single branch -- cgit v1.2.1 From 79fd77953005062bfd89161770d1c0166774bb3d Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Thu, 25 Aug 2016 13:46:59 +0100 Subject: Allow setting branch refs for MR --- app/models/merge_request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 5330a07ee35..918be8b7e66 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -238,12 +238,12 @@ class MergeRequest < ActiveRecord::Base def source_branch_head source_branch_ref = @source_branch_sha || source_branch - source_project.repository.commit(source_branch) if source_branch_ref + source_project.repository.commit(source_branch_ref) if source_branch_ref end def target_branch_head target_branch_ref = @target_branch_sha || target_branch - target_project.repository.commit(target_branch) if target_branch_ref + target_project.repository.commit(target_branch_ref) if target_branch_ref end def target_branch_sha -- cgit v1.2.1 From 7a83df749e3ff3c88cff9f348ed5ed222a15508a Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Thu, 25 Aug 2016 15:41:51 +0100 Subject: Link to the issue templates in Contributing guide Change to use a better title style Add backticks to code segments Spelling and Grammar --- .gitlab/issue_templates/Bug.md | 26 +++++++++++++------------- .gitlab/issue_templates/Feature Proposal.md | 9 +++++---- CONTRIBUTING.md | 4 ++-- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index f6f5879c21a..b676916fdf4 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,44 +1,44 @@ -## Summary +### Summary (Summarize the bug encountered concisely) -## Steps to reproduce +### Steps to reproduce (How one can reproduce the issue - this is very important) -## Expected behavior +### Expected behavior (What you should see instead) -## Actual behaviour +### Actual behaviour (What actually happens) -## Relevant logs and/or screenshots +### Relevant logs and/or screenshots (Paste any relevant logs - please use code blocks (```) to format console output, logs, and code as it's very hard to read otherwise.) -## Output of checks +### Output of checks -### Results of GitLab Application Check +#### Results of GitLab application Check (For installations with omnibus-gitlab package run and paste the output of: -sudo gitlab-rake gitlab:check SANITIZE=true) +`sudo gitlab-rake gitlab:check SANITIZE=true`) (For installations from source run and paste the output of: -sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true) +`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`) (we will only investigate if the tests are passing) -### Results of GitLab Environment Info +#### Results of GitLab environment info (For installations with omnibus-gitlab package run and paste the output of: -sudo gitlab-rake gitlab:env:info) +`sudo gitlab-rake gitlab:env:info`) (For installations from source run and paste the output of: -sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production) +`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) -## Possible fixes +### Possible fixes (If you can, link to the line of code that might be responsible for the problem) diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature Proposal.md index ea009c32296..ea895ee6275 100644 --- a/.gitlab/issue_templates/Feature Proposal.md +++ b/.gitlab/issue_templates/Feature Proposal.md @@ -1,6 +1,7 @@ -## Description -Include problem, use cases, benefits, and/or goals +### Description -## Proposal +(Include problem, use cases, benefits, and/or goals) -## Links / references +### Proposal + +### Links / references diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f4b3d5d244..c457af2ae6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -144,7 +144,7 @@ code snippet right after your description in a new line: `~"feature proposal"`. Please keep feature proposals as small and simple as possible, complex ones might be edited to make them small and simple. -Please submit Feature Proposals using the 'Feature Proposal' issue template provided on the issue tracker. +Please submit Feature Proposals using the ['Feature Proposal' issue template](.gitlab/issue_templates/Feature Proposal.md) provided on the issue tracker. For changes in the interface, it can be helpful to create a mockup first. If you want to create something yourself, consider opening an issue first to @@ -157,7 +157,7 @@ submitting your own, there's a good chance somebody else had the same issue or feature proposal. Show your support with an award emoji and/or join the discussion. -Please submit bugs using the 'Bug' issue template provided on the issue tracker. +Please submit bugs using the ['Bug' issue template](.gitlab/issue_templates/Bug.md) provided on the issue tracker. The text in the parenthesis is there to help you with what to include. Omit it when submitting the actual issue. You can copy-paste it and then edit as you see fit. -- cgit v1.2.1 From 7ed0acd422a95c4cbb1406f67929a5da669f2681 Mon Sep 17 00:00:00 2001 From: Regis Date: Thu, 4 Aug 2016 12:18:00 -0600 Subject: turned ES5 users.js to ES6 users.js.es6 for babel added semicolons remove users.js rename users to user in filename removed uneeded semi-colons and returning null in constructor class is wrapped - a lot of builds will fail formatting replaced 'new User' with 'new gl.User' in app/users/show.html.haml window.gl || window.gl = {} - seeing if rspec9/spinach6/spinach9 will pass putting window logic before IIFE Fixed typo in users show view - extracted jquery calls in constructor to prototype methods fixed window declaration in IIFE argument adding new line --- app/assets/javascripts/user.js | 29 ----------------------------- app/assets/javascripts/user.js.es6 | 35 +++++++++++++++++++++++++++++++++++ app/views/users/show.html.haml | 2 +- 3 files changed, 36 insertions(+), 30 deletions(-) delete mode 100644 app/assets/javascripts/user.js create mode 100644 app/assets/javascripts/user.js.es6 diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js deleted file mode 100644 index 6c4d88cf407..00000000000 --- a/app/assets/javascripts/user.js +++ /dev/null @@ -1,29 +0,0 @@ -(function() { - this.User = (function() { - function User(opts) { - this.opts = opts; - $('.profile-groups-avatars').tooltip({ - "placement": "top" - }); - this.initTabs(); - $('.hide-project-limit-message').on('click', function(e) { - $.cookie('hide_project_limit_message', 'false', { - path: gon.relative_url_root || '/' - }); - $(this).parents('.project-limit-message').remove(); - return e.preventDefault(); - }); - } - - User.prototype.initTabs = function() { - return new UserTabs({ - parentEl: '.user-profile', - action: this.opts.action - }); - }; - - return User; - - })(); - -}).call(this); diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 new file mode 100644 index 00000000000..c934afa458a --- /dev/null +++ b/app/assets/javascripts/user.js.es6 @@ -0,0 +1,35 @@ +(global => { + global.User = class { + constructor(opts) { + this.opts = opts; + this.placeTop(); + this.initTabs(); + this.hideProjectLimitMessage(); + } + + placeTop() { + $('.profile-groups-avatars').tooltip({ + "placement": "top" + }); + } + + initTabs() { + return new UserTabs({ + parentEl: '.user-profile', + action: this.opts.action + }); + } + + hideProjectLimitMessage() { + $('.hide-project-limit-message').on('click', e => { + const path = '/'; + $.cookie('hide_project_limit_message', 'false', { + path: path + }); + $(this).parents('.project-limit-message').remove(); + e.preventDefault(); + return; + }); + } + } +})(window.gl || (window.gl = {})); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index c7f39868e71..9a052abe40a 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -123,6 +123,6 @@ :javascript var userProfile; - userProfile = new User({ + userProfile = new gl.User({ action: "#{controller.action_name}" }); -- cgit v1.2.1 From f0866b28d384f569345eccb6e64ad93d29a3e570 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 25 Aug 2016 20:43:24 -0500 Subject: Fix repo title alignment --- CHANGELOG | 1 + app/assets/stylesheets/framework/header.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index be1063a2330..36c757021d2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.12.0 (unreleased) - Added tests for diff notes - Add delimiter to project stars and forks count (ClemMakesApps) - Fix badge count alignment (ClemMakesApps) + - Fix repo title alignment (ClemMakesApps) - Fix branch title trailing space on hover (ClemMakesApps) - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison) - Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 0c607071840..41ffaa38f6c 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -94,7 +94,7 @@ header { .side-nav-toggle { position: absolute; left: -10px; - margin: 6px 0; + margin: 7px 0; font-size: 18px; padding: 6px 10px; border: none; -- cgit v1.2.1 From 0fe4cf2b0fdbc33572f11bba1a4426ee05ed7599 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 24 Aug 2016 20:06:16 -0700 Subject: Fix Sentry not reporting right program for Sidekiq workers Moves program tag into the global configuration since this doesn't change and since Sidekiq workers get a unique context for each event. Closes #21410 --- app/helpers/sentry_helper.rb | 22 ++-------------------- config/initializers/sentry.rb | 1 + lib/gitlab/sentry.rb | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 lib/gitlab/sentry.rb diff --git a/app/helpers/sentry_helper.rb b/app/helpers/sentry_helper.rb index f8cccade15b..3d255df66a0 100644 --- a/app/helpers/sentry_helper.rb +++ b/app/helpers/sentry_helper.rb @@ -1,27 +1,9 @@ module SentryHelper def sentry_enabled? - Rails.env.production? && current_application_settings.sentry_enabled? + Gitlab::Sentry.enabled? end def sentry_context - return unless sentry_enabled? - - if current_user - Raven.user_context( - id: current_user.id, - email: current_user.email, - username: current_user.username, - ) - end - - Raven.tags_context(program: sentry_program_context) - end - - def sentry_program_context - if Sidekiq.server? - 'sidekiq' - else - 'rails' - end + Gitlab::Sentry.context(current_user) end end diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 74fef7cadfe..5892c1de024 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -18,6 +18,7 @@ if Rails.env.production? # Sanitize fields based on those sanitized from Rails. config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s) + config.tags = { program: Gitlab::Sentry.program_context } end end end diff --git a/lib/gitlab/sentry.rb b/lib/gitlab/sentry.rb new file mode 100644 index 00000000000..117fc508135 --- /dev/null +++ b/lib/gitlab/sentry.rb @@ -0,0 +1,27 @@ +module Gitlab + module Sentry + def self.enabled? + Rails.env.production? && current_application_settings.sentry_enabled? + end + + def self.context(current_user = nil) + return unless self.enabled? + + if current_user + Raven.user_context( + id: current_user.id, + email: current_user.email, + username: current_user.username, + ) + end + end + + def self.program_context + if Sidekiq.server? + 'sidekiq' + else + 'rails' + end + end + end +end -- cgit v1.2.1 From 71d552029126b42de1191a68dd270b5491d849ea Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 11 Aug 2016 10:08:06 -0500 Subject: Fix branches page dropdown sort alignment --- CHANGELOG | 1 + app/assets/stylesheets/framework/nav.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index df8dec7bdde..1f14ed51eba 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.12.0 (unreleased) - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel) - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling) - Add hover color to emoji icon (ClemMakesApps) + - Fix branches page dropdown sort alignment (ClemMakesApps) - Optimistic locking for Issues and Merge Requests (title and description overriding prevention) - Add `wiki_page_events` to project hook APIs (Ben Boeckel) - Remove Gitorious import diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 9e924f99e9c..ceafdc20ebc 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -161,6 +161,7 @@ > .dropdown { margin-right: $gl-padding-top; display: inline-block; + vertical-align: top; &:last-child { margin-right: 0; -- cgit v1.2.1 From b17df0507b704a347d6a8af035526b642ec85284 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 26 Aug 2016 13:10:03 +0800 Subject: Extract ref_name and path in before_action, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5142#note_14469768 --- app/controllers/projects/artifacts_controller.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 4c63bec90e5..59222637961 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -4,6 +4,7 @@ class Projects::ArtifactsController < Projects::ApplicationController layout 'project' before_action :authorize_read_build! before_action :authorize_update_build!, only: [:keep] + before_action :extract_ref_name_and_path before_action :validate_artifacts! def download @@ -48,6 +49,12 @@ class Projects::ArtifactsController < Projects::ApplicationController private + def extract_ref_name_and_path + return unless params[:ref_name_and_path] + + @ref_name, @path = extract_ref(params[:ref_name_and_path]) + end + def validate_artifacts! render_404 unless build && build.artifacts? end @@ -61,12 +68,10 @@ class Projects::ArtifactsController < Projects::ApplicationController end def build_from_ref - if params[:ref_name_and_path] - ref_name, @path = extract_ref(params[:ref_name_and_path]) - builds = project.latest_successful_builds_for(ref_name) + return unless @ref_name - builds.find_by(name: params[:job]) - end + builds = project.latest_successful_builds_for(@ref_name) + builds.find_by(name: params[:job]) end def artifacts_file -- cgit v1.2.1 From 1fc0d4f4b50a9aeae73a0b0a9a4641e57786e6b3 Mon Sep 17 00:00:00 2001 From: Jake Romer Date: Fri, 26 Aug 2016 06:12:30 +0000 Subject: Clarify blank line rule in newlines_styleguide.md To clarify what's meant by "from a logical perspective" here, I consulted Python's PEP8 style guide, which provides some helpfully precise language: > Extra blank lines may be used (sparingly) to separate groups of > related functions. Blank lines may be omitted between a bunch of > related one-liners (e.g. a set of dummy implementations). https://www.python.org/dev/peps/pep-0008/#blank-lines I adapted this passage to the existing language for the newline rule. --- doc/development/newlines_styleguide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md index e03adcaadea..32aac2529a4 100644 --- a/doc/development/newlines_styleguide.md +++ b/doc/development/newlines_styleguide.md @@ -2,7 +2,7 @@ This style guide recommends best practices for newlines in Ruby code. -## Rule: separate code with newlines only when it makes sense from logic perspectice +## Rule: separate code with newlines only to group together related logic ```ruby # bad -- cgit v1.2.1 From c40fd0b1c2aa5d3354fd50e37a4fe22483df2042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20=C3=98ivind=20Bj=C3=B8rnsen?= Date: Fri, 26 Aug 2016 07:24:13 +0000 Subject: docs: make sure to update 8.10-to-8.11 workhorse version too (see !5983) --- doc/update/8.10-to-8.11.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.10-to-8.11.md b/doc/update/8.10-to-8.11.md index 98721763566..b058f8e2a03 100644 --- a/doc/update/8.10-to-8.11.md +++ b/doc/update/8.10-to-8.11.md @@ -82,7 +82,7 @@ GitLab 8.1. ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch --all -sudo -u git -H git checkout v0.7.8 +sudo -u git -H git checkout v0.7.11 sudo -u git -H make ``` -- cgit v1.2.1 From 3a8fb95c98262986ec09655bf5572c3e3d2145d6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 26 Aug 2016 15:40:12 +0800 Subject: Fix tests --- lib/gitlab/badge/coverage/report.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb index 95d925dc7f3..9a0482306b7 100644 --- a/lib/gitlab/badge/coverage/report.rb +++ b/lib/gitlab/badge/coverage/report.rb @@ -12,9 +12,7 @@ module Gitlab @ref = ref @job = job - @pipeline = @project.pipelines - .latest_successful_for(@ref) - .first + @pipeline = @project.pipelines.latest_successful_for(@ref) end def entity -- cgit v1.2.1 From bfd14f876388f6c9a4006ae270b8a98912415226 Mon Sep 17 00:00:00 2001 From: winniehell Date: Fri, 26 Aug 2016 10:54:17 +0200 Subject: Check for existence of elements under test in application_spec.js (!6051) --- spec/javascripts/application_spec.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index b48026c3b77..56b98856614 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -13,17 +13,21 @@ gl.utils.preventDisabledButtons(); isClicked = false; $button = $('#test-button'); + expect($button).toExist(); $button.click(function() { return isClicked = true; }); $button.trigger('click'); return expect(isClicked).toBe(false); }); - return it('should be on the same page if a disabled link clicked', function() { - var locationBeforeLinkClick; + + it('should be on the same page if a disabled link clicked', function() { + var locationBeforeLinkClick, $link; locationBeforeLinkClick = window.location.href; gl.utils.preventDisabledButtons(); - $('#test-link').click(); + $link = $('#test-link'); + expect($link).toExist(); + $link.click(); return expect(window.location.href).toBe(locationBeforeLinkClick); }); }); -- cgit v1.2.1 From 41a0b7b22f7cdec7d216f32d561442c9fc3587be Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 26 Aug 2016 17:32:00 +0800 Subject: Fix CHANGELOG --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index db3e891bdcd..a262dc54e5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,8 +15,6 @@ v 8.12.0 (unreleased) - Fix groups sort dropdown alignment (ClemMakesApps) - Added tests for diff notes - Add a button to download latest successful artifacts for branches and tags !5142 - -v 8.11.1 (unreleased) - Add delimiter to project stars and forks count (ClemMakesApps) - Fix badge count alignment (ClemMakesApps) - Fix branch title trailing space on hover (ClemMakesApps) -- cgit v1.2.1 From 0e0d0ede2c265de4b2fdf76a1376cfced3efaacd Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 25 Aug 2016 12:42:22 -0500 Subject: Fix inconsistent checkbox alignment --- CHANGELOG | 1 + app/assets/stylesheets/pages/issues.scss | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index df8dec7bdde..31f8f11288a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ v 8.12.0 (unreleased) - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck) - Reduce number of database queries on builds tab - Capitalize mentioned issue timeline notes (ClemMakesApps) + - Fix inconsistent checkbox alignment (ClemMakesApps) - Use the default branch for displaying the project icon instead of master !5792 (Hannes Rosenögger) - Adds response mime type to transaction metric action when it's not HTML diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index dfe1e3075da..3a3bb10feac 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -12,6 +12,10 @@ padding-right: 8px; margin-bottom: 10px; min-width: 15px; + + .selected_issue { + vertical-align: text-top; + } } .issue-labels { -- cgit v1.2.1 From 6686084c6502f10fd7e6b8963ab52526cb6831bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Fri, 26 Aug 2016 17:20:00 -0300 Subject: Fix "Wiki" link not appearing in navigation for projects with external wiki --- CHANGELOG | 1 + app/models/ability.rb | 2 +- app/models/project.rb | 4 ++++ spec/models/ability_spec.rb | 13 +++++++++++++ spec/models/project_spec.rb | 12 ++++++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 7817470d95e..b5ae1adf1d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,7 @@ v 8.11.3 (unreleased) - Allow system info page to handle case where info is unavailable - Label list shows all issues (opened or closed) with that label - Don't show resolve conflicts link before MR status is updated + - Fix "Wiki" link not appearing in navigation for projects with external wiki - Fix IE11 fork button bug !598 - Don't prevent viewing the MR when git refs for conflicts can't be found on disk - Fix external issue tracker "Issues" link leading to 404s diff --git a/app/models/ability.rb b/app/models/ability.rb index a49dd703926..c1df4a865f6 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -355,7 +355,7 @@ class Ability rules += named_abilities('project_snippet') end - unless project.wiki_enabled + unless project.has_wiki? rules += named_abilities('wiki') end diff --git a/app/models/project.rb b/app/models/project.rb index 0e4fb94f8eb..0fa41ebbec3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -680,6 +680,10 @@ class Project < ActiveRecord::Base update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) end + def has_wiki? + wiki_enabled? || has_external_wiki? + end + def external_wiki if has_external_wiki.nil? cache_has_external_wiki # Populate diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index aa3b2bbf471..c50ca38bdd9 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -282,4 +282,17 @@ describe Ability, lib: true do end end end + + describe '.project_disabled_features_rules' do + let(:project) { build(:project) } + + subject { described_class.project_disabled_features_rules(project) } + + context 'wiki named abilities' do + it 'disables wiki abilities if the project has no wiki' do + expect(project).to receive(:has_wiki?).and_return(false) + expect(subject).to include(:read_wiki, :create_wiki, :update_wiki, :admin_wiki) + end + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b2baeeb31bb..3b637b0defc 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -506,6 +506,18 @@ describe Project, models: true do end end + describe '#has_wiki?' do + let(:no_wiki_project) { build(:project, wiki_enabled: false, has_external_wiki: false) } + let(:wiki_enabled_project) { build(:project, wiki_enabled: true) } + let(:external_wiki_project) { build(:project, has_external_wiki: true) } + + it 'returns true if project is wiki enabled or has external wiki' do + expect(wiki_enabled_project).to have_wiki + expect(external_wiki_project).to have_wiki + expect(no_wiki_project).not_to have_wiki + end + end + describe '#external_wiki' do let(:project) { create(:project) } -- cgit v1.2.1 From 9c2d061ad468e6a47d21617fb2c6b874e22c13bc Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 24 Aug 2016 11:43:44 +0100 Subject: Drop unused CI tables and files These tables, web hooks and services, are unused but where not dropped with the commits d5c91bb9a601a1a344d94763654f0b0996857497 and 2988e1fbf50b3c9e803a9358933e3e969e64dcc3. The file was left too, but never called. --- app/services/ci/web_hook_service.rb | 35 ---------------------- db/migrate/20160824103857_drop_unused_ci_tables.rb | 11 +++++++ db/schema.rb | 19 +----------- 3 files changed, 12 insertions(+), 53 deletions(-) delete mode 100644 app/services/ci/web_hook_service.rb create mode 100644 db/migrate/20160824103857_drop_unused_ci_tables.rb diff --git a/app/services/ci/web_hook_service.rb b/app/services/ci/web_hook_service.rb deleted file mode 100644 index 92e6df442b4..00000000000 --- a/app/services/ci/web_hook_service.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Ci - class WebHookService - def build_end(build) - execute_hooks(build.project, build_data(build)) - end - - def execute_hooks(project, data) - project.web_hooks.each do |web_hook| - async_execute_hook(web_hook, data) - end - end - - def async_execute_hook(hook, data) - Sidekiq::Client.enqueue(Ci::WebHookWorker, hook.id, data) - end - - def build_data(build) - project = build.project - data = {} - data.merge!({ - build_id: build.id, - build_name: build.name, - build_status: build.status, - build_started_at: build.started_at, - build_finished_at: build.finished_at, - project_id: project.id, - project_name: project.name, - gitlab_url: project.gitlab_url, - ref: build.ref, - before_sha: build.before_sha, - sha: build.sha, - }) - end - end -end diff --git a/db/migrate/20160824103857_drop_unused_ci_tables.rb b/db/migrate/20160824103857_drop_unused_ci_tables.rb new file mode 100644 index 00000000000..65cf46308d9 --- /dev/null +++ b/db/migrate/20160824103857_drop_unused_ci_tables.rb @@ -0,0 +1,11 @@ +class DropUnusedCiTables < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def change + drop_table(:ci_services) + drop_table(:ci_web_hooks) + end +end diff --git a/db/schema.rb b/db/schema.rb index 5a105a91ad1..227e10294e4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160823081327) do +ActiveRecord::Schema.define(version: 20160824103857) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -295,16 +295,6 @@ ActiveRecord::Schema.define(version: 20160823081327) do add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree - create_table "ci_services", force: :cascade do |t| - t.string "type" - t.string "title" - t.integer "project_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - t.boolean "active", default: false, null: false - t.text "properties" - end - create_table "ci_sessions", force: :cascade do |t| t.string "session_id", null: false t.text "data" @@ -360,13 +350,6 @@ ActiveRecord::Schema.define(version: 20160823081327) do add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree - create_table "ci_web_hooks", force: :cascade do |t| - t.string "url", null: false - t.integer "project_id", null: false - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "deploy_keys_projects", force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false -- cgit v1.2.1 From 12784559316fc2c3860641bee9c6f8e2bc12c4ba Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Wed, 3 Aug 2016 22:20:08 -0500 Subject: Change logo animation to CSS --- CHANGELOG | 1 + app/assets/javascripts/logo.js | 42 +--------- app/assets/stylesheets/framework.scss | 1 + app/assets/stylesheets/framework/header.scss | 32 +------- app/assets/stylesheets/framework/logo.scss | 118 +++++++++++++++++++++++++++ app/assets/stylesheets/framework/mixins.scss | 5 ++ app/views/shared/_logo.svg | 16 ++-- 7 files changed, 136 insertions(+), 79 deletions(-) create mode 100644 app/assets/stylesheets/framework/logo.scss diff --git a/CHANGELOG b/CHANGELOG index 3548115dff3..713e0e033d0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.12.0 (unreleased) - Add two-factor recovery endpoint to internal API !5510 - Add font color contrast to external label in admin area (ClemMakesApps) + - Change logo animation to CSS (ClemMakesApps) - Change merge_error column from string to text type - Reduce contributions calendar data payload (ClemMakesApps) - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel) diff --git a/app/assets/javascripts/logo.js b/app/assets/javascripts/logo.js index 218f24fe908..e5d4fd44c96 100644 --- a/app/assets/javascripts/logo.js +++ b/app/assets/javascripts/logo.js @@ -1,50 +1,12 @@ (function() { - var clearHighlights, currentTimer, defaultClass, delay, firstPiece, pieceIndex, pieces, start, stop, work; - Turbolinks.enableProgressBar(); - defaultClass = 'tanuki-shape'; - - pieces = ['path#tanuki-right-cheek', 'path#tanuki-right-eye, path#tanuki-right-ear', 'path#tanuki-nose', 'path#tanuki-left-eye, path#tanuki-left-ear', 'path#tanuki-left-cheek']; - - pieceIndex = 0; - - firstPiece = pieces[0]; - - currentTimer = null; - - delay = 150; - - clearHighlights = function() { - return $("." + defaultClass + ".highlight").attr('class', defaultClass); - }; - start = function() { - clearHighlights(); - pieceIndex = 0; - if (pieces[0] !== firstPiece) { - pieces.reverse(); - } - if (currentTimer) { - clearInterval(currentTimer); - } - return currentTimer = setInterval(work, delay); + $('.tanuki-logo').addClass('animate'); }; stop = function() { - clearInterval(currentTimer); - return clearHighlights(); - }; - - work = function() { - clearHighlights(); - $(pieces[pieceIndex]).attr('class', defaultClass + " highlight"); - if (pieceIndex === pieces.length - 1) { - pieceIndex = 0; - return pieces.reverse(); - } else { - return pieceIndex++; - } + $('.tanuki-logo').removeClass('animate'); }; $(document).on('page:fetch', start); diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index a306b8f3f29..d5cca1b10fb 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -24,6 +24,7 @@ @import "framework/issue_box.scss"; @import "framework/jquery.scss"; @import "framework/lists.scss"; +@import "framework/logo.scss"; @import "framework/markdown_area.scss"; @import "framework/mobile.scss"; @import "framework/modal.scss"; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 0c607071840..afe4a276ae5 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -2,16 +2,6 @@ * Application Header * */ -@mixin tanuki-logo-colors($path-color) { - fill: $path-color; - transition: all 0.8s; - - &:hover, - &.highlight { - fill: lighten($path-color, 25%); - transition: all 0.1s; - } -} header { transition: padding $sidebar-transition-duration; @@ -25,7 +15,7 @@ header { margin: 8px 0; text-align: center; - #tanuki-logo, img { + .tanuki-logo, img { height: 36px; } } @@ -205,26 +195,6 @@ header { } } -#tanuki-logo { - - #tanuki-left-ear, - #tanuki-right-ear, - #tanuki-nose { - @include tanuki-logo-colors($tanuki-red); - } - - #tanuki-left-eye, - #tanuki-right-eye { - @include tanuki-logo-colors($tanuki-orange); - } - - #tanuki-left-cheek, - #tanuki-right-cheek { - @include tanuki-logo-colors($tanuki-yellow); - } - -} - @media (max-width: $screen-xs-max) { header .container-fluid { font-size: 18px; diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss new file mode 100644 index 00000000000..3ee3fb4cee5 --- /dev/null +++ b/app/assets/stylesheets/framework/logo.scss @@ -0,0 +1,118 @@ +@mixin unique-keyframes { + $animation-name: unique-id(); + @include webkit-prefix(animation-name, $animation-name); + + @-webkit-keyframes #{$animation-name} { + @content; + } + @keyframes #{$animation-name} { + @content; + } +} + +@mixin tanuki-logo-colors($path-color) { + fill: $path-color; + transition: all 0.8s; + + &:hover { + fill: lighten($path-color, 25%); + transition: all 0.1s; + } +} + +@mixin tanuki-second-highlight-animations($tanuki-color) { + @include unique-keyframes { + 10%, 80% { + fill: #{$tanuki-color} + } + 20%, 90% { + fill: lighten($tanuki-color, 25%); + } + } +} + +@mixin tanuki-forth-highlight-animations($tanuki-color) { + @include unique-keyframes { + 30%, 60% { + fill: #{$tanuki-color}; + } + 40%, 70% { + fill: lighten($tanuki-color, 25%); + } + } +} + +.tanuki-logo { + + .tanuki-left-ear, + .tanuki-right-ear, + .tanuki-nose { + @include tanuki-logo-colors($tanuki-red); + } + + .tanuki-left-eye, + .tanuki-right-eye { + @include tanuki-logo-colors($tanuki-orange); + } + + .tanuki-left-cheek, + .tanuki-right-cheek { + @include tanuki-logo-colors($tanuki-yellow); + } + + &.animate { + .tanuki-shape { + @include webkit-prefix(animation-duration, 1.5s); + @include webkit-prefix(animation-iteration-count, infinite); + } + + .tanuki-left-cheek { + @include unique-keyframes { + 0%, 10%, 100% { + fill: lighten($tanuki-yellow, 25%); + } + 90% { + fill: $tanuki-yellow; + } + } + } + + .tanuki-left-eye { + @include tanuki-second-highlight-animations($tanuki-orange); + } + + .tanuki-left-ear { + @include tanuki-second-highlight-animations($tanuki-red); + } + + .tanuki-nose { + @include unique-keyframes { + 20%, 70% { + fill: $tanuki-red; + } + 30%, 80% { + fill: lighten($tanuki-red, 25%); + } + } + } + + .tanuki-right-eye { + @include tanuki-forth-highlight-animations($tanuki-orange); + } + + .tanuki-right-ear { + @include tanuki-forth-highlight-animations($tanuki-red); + } + + .tanuki-right-cheek { + @include unique-keyframes { + 40% { + fill: $tanuki-yellow; + } + 60% { + fill: lighten($tanuki-yellow, 25%); + } + } + } + } +} \ No newline at end of file diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index d2d60ed7196..856642400a6 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -129,3 +129,8 @@ color: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.1); } + +@mixin webkit-prefix($property, $value) { + #{'-webkit-' + $property}: $value; + #{$property}: $value; +} diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg index b07f1c5603e..9b67422da2c 100644 --- a/app/views/shared/_logo.svg +++ b/app/views/shared/_logo.svg @@ -1,9 +1,9 @@ -
You\'re all done!
'); return _this.updateBadges(data); }; })(this) diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index d320d3bcc1e..6bcc37042ea 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -66,7 +66,7 @@ - if @todos.any? .js-todos-options{ data: {per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages} } - @todos.group_by(&:project).each do |group| - .panel.panel-default.panel-small.js-todos-list + .panel.panel-default.panel-small - project = group[0] .panel-heading = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index 60152d3da55..0607086c166 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -54,6 +54,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps page.within('.todos-pending-count') { expect(page).to have_content '0' } expect(page).to have_content 'To do 0' expect(page).to have_content 'Done 4' + expect(page).to have_content "You're all done!" expect(page).not_to have_link project.name_with_namespace should_not_see_todo "John Doe assigned you merge request #{merge_request.to_reference}" should_not_see_todo "John Doe mentioned you on issue #{issue.to_reference}" diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb index 32544f3f538..fc555a74f30 100644 --- a/spec/features/todos/todos_spec.rb +++ b/spec/features/todos/todos_spec.rb @@ -118,6 +118,20 @@ describe 'Dashboard Todos', feature: true do expect(page).to have_css("#todo_#{Todo.first.id}") end end + + describe 'mark all as done', js: true do + before do + visit dashboard_todos_path + click_link('Mark all as done') + end + + it 'shows "All done" message!' do + within('.todos-pending-count') { expect(page).to have_content '0' } + expect(page).to have_content 'To do 0' + expect(page).to have_content "You're all done!" + expect(page).not_to have_selector('.gl-pagination') + end + end end context 'User has a Todo in a project pending deletion' do -- cgit v1.2.1 From 636dbc85e24600849de9dd09c4854edf4adfe807 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 31 Aug 2016 17:40:54 +0200 Subject: The ID of a project can be also a string [ci skip] --- doc/api/commits.md | 14 +++++++------- doc/api/projects.md | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/api/commits.md b/doc/api/commits.md index 55d0de7afd9..682151d4b1d 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -10,7 +10,7 @@ GET /projects/:id/repository/commits | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch | | `since` | string | no | Only commits after or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | | `until` | string | no | Only commits before or in this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | @@ -58,7 +58,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit hash or name of a repository branch or tag | ```bash @@ -102,7 +102,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit hash or name of a repository branch or tag | ```bash @@ -138,7 +138,7 @@ Parameters: | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit hash or name of a repository branch or tag | ```bash @@ -187,7 +187,7 @@ POST /projects/:id/repository/commits/:sha/comments | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit SHA or name of a repository branch or tag | | `note` | string | yes | The text of the comment | | `path` | string | no | The file path relative to the repository | @@ -232,7 +232,7 @@ GET /projects/:id/repository/commits/:sha/statuses | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit SHA | `ref_name`| string | no | The name of a repository branch or tag or, if not given, the default branch | `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test` @@ -306,7 +306,7 @@ POST /projects/:id/statuses/:sha | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user +| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user | `sha` | string | yes | The commit SHA | `state` | string | yes | The state of the status. Can be one of the following: `pending`, `running`, `success`, `failed`, `canceled` | `ref` | string | no | The `ref` (branch or tag) to which the status refers diff --git a/doc/api/projects.md b/doc/api/projects.md index 0d5aa61aa74..a62aaee14d7 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -535,7 +535,7 @@ POST /projects/:id/star | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star" @@ -602,7 +602,7 @@ DELETE /projects/:id/star | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | ```bash curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/star" @@ -673,7 +673,7 @@ POST /projects/:id/archive | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/archive" @@ -760,7 +760,7 @@ POST /projects/:id/unarchive | Attribute | Type | Required | Description | | --------- | ---- | -------- | ----------- | -| `id` | integer | yes | The ID of the project or NAMESPACE/PROJECT_NAME | +| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME | ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/unarchive" -- cgit v1.2.1 From 52daddc0b7166fc50949db042f4f05488f0b9eae Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 30 Aug 2016 16:20:56 -0300 Subject: Use updated_at as the last updated date when importing from GitHub --- lib/gitlab/github_import/issue_formatter.rb | 6 +--- lib/gitlab/github_import/milestone_formatter.rb | 36 ++++------------------ lib/gitlab/github_import/pull_request_formatter.rb | 11 +------ .../gitlab/github_import/issue_formatter_spec.rb | 5 ++- .../github_import/milestone_formatter_spec.rb | 5 ++- .../github_import/pull_request_formatter_spec.rb | 7 ++--- 6 files changed, 15 insertions(+), 55 deletions(-) diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index 835ec858b35..07edbe37a13 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -12,7 +12,7 @@ module Gitlab author_id: author_id, assignee_id: assignee_id, created_at: raw_data.created_at, - updated_at: updated_at + updated_at: raw_data.updated_at } end @@ -69,10 +69,6 @@ module Gitlab def state raw_data.state == 'closed' ? 'closed' : 'opened' end - - def updated_at - state == 'closed' ? raw_data.closed_at : raw_data.updated_at - end end end end diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb index 53d4b3102d1..b2fa524cf5b 100644 --- a/lib/gitlab/github_import/milestone_formatter.rb +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -3,14 +3,14 @@ module Gitlab class MilestoneFormatter < BaseFormatter def attributes { - iid: number, + iid: raw_data.number, project: project, - title: title, - description: description, - due_date: due_date, + title: raw_data.title, + description: raw_data.description, + due_date: raw_data.due_on, state: state, - created_at: created_at, - updated_at: updated_at + created_at: raw_data.created_at, + updated_at: raw_data.updated_at } end @@ -20,33 +20,9 @@ module Gitlab private - def number - raw_data.number - end - - def title - raw_data.title - end - - def description - raw_data.description - end - - def due_date - raw_data.due_on - end - def state raw_data.state == 'closed' ? 'closed' : 'active' end - - def created_at - raw_data.created_at - end - - def updated_at - state == 'closed' ? raw_data.closed_at : raw_data.updated_at - end end end end diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index 04aa3664f64..d9d436d7490 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -20,7 +20,7 @@ module Gitlab author_id: author_id, assignee_id: assignee_id, created_at: raw_data.created_at, - updated_at: updated_at + updated_at: raw_data.updated_at } end @@ -103,15 +103,6 @@ module Gitlab 'opened' end end - - def updated_at - case state - when 'merged' then raw_data.merged_at - when 'closed' then raw_data.closed_at - else - raw_data.updated_at - end - end end end end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index 0e7ffbe9b8e..d60c4111e99 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -48,8 +48,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when issue is closed' do - let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed')) } it 'returns formatted attributes' do expected = { @@ -62,7 +61,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do author_id: project.creator_id, assignee_id: nil, created_at: created_at, - updated_at: closed_at + updated_at: updated_at } expect(issue.attributes).to eq(expected) diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb index 5a421e50581..09337c99a07 100644 --- a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -40,8 +40,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do end context 'when milestone is closed' do - let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed')) } it 'returns formatted attributes' do expected = { @@ -52,7 +51,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do state: 'closed', due_date: nil, created_at: created_at, - updated_at: closed_at + updated_at: updated_at } expect(formatter.attributes).to eq(expected) diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index b667abf063d..edfc6ad81c6 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -62,8 +62,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when pull request is closed' do - let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed')) } it 'returns formatted attributes' do expected = { @@ -81,7 +80,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do author_id: project.creator_id, assignee_id: nil, created_at: created_at, - updated_at: closed_at + updated_at: updated_at } expect(pull_request.attributes).to eq(expected) @@ -108,7 +107,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do author_id: project.creator_id, assignee_id: nil, created_at: created_at, - updated_at: merged_at + updated_at: updated_at } expect(pull_request.attributes).to eq(expected) -- cgit v1.2.1 From 80c4f1093747797cafaa832130e9781e3774722e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 30 Aug 2016 16:25:28 -0300 Subject: Don't touch Issue/Merge Request when importing GitHub comments --- lib/gitlab/github_import/importer.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 02ffb43d89b..1b2a5eb8f52 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -152,12 +152,14 @@ module Gitlab end def create_comments(issuable, comments) - comments.each do |raw| - begin - comment = CommentFormatter.new(project, raw) - issuable.notes.create!(comment.attributes) - rescue => e - errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + ActiveRecord::Base.no_touching do + comments.each do |raw| + begin + comment = CommentFormatter.new(project, raw) + issuable.notes.create!(comment.attributes) + rescue => e + errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + end end end end -- cgit v1.2.1 From 9ef743d1e4b669f15f51ca440b1172856e1d7fff Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 30 Aug 2016 16:34:20 -0300 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 08562b1a5a0..65cbcd6059a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -57,6 +57,7 @@ v 8.12.0 (unreleased) v 8.11.4 (unreleased) - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) + - Fix sorting issues by "last updated" doesn't work after import from GitHub - Creating an issue through our API now emails label subscribers !5720 - Block concurrent updates for Pipeline - Fix resolving conflicts on forks -- cgit v1.2.1 From eba024366bdaa2d2645857fcb404ad0468b782fa Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 18 Aug 2016 11:18:25 -0500 Subject: Remove redundant js-timeago-pending from user activity log --- CHANGELOG | 1 + app/assets/javascripts/activities.js | 2 +- app/views/events/_event.html.haml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 08562b1a5a0..40a7237333f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -30,6 +30,7 @@ v 8.12.0 (unreleased) - Add last commit time to repo view (ClemMakesApps) - Added project specific enable/disable setting for LFS !5997 - Don't expose a user's token in the `/api/v3/user` API (!6047) + - Remove redundant js-timeago-pending from user activity log (ClemMakesApps) - Added tests for diff notes - Add a button to download latest successful artifacts for branches and tags !5142 - Remove redundant pipeline tooltips (ClemMakesApps) diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 5ea6086ab77..d5e11e22be5 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -12,7 +12,7 @@ } Activities.prototype.updateTooltips = function() { - return gl.utils.localTimeAgo($('.js-timeago', '#activity')); + return gl.utils.localTimeAgo($('.js-timeago', '.content_list')); }; Activities.prototype.reloadActivities = function() { diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 5c318cd3b8b..31fdcc5e21b 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -1,7 +1,7 @@ - if event.visible_to_user?(current_user) .event-item{ class: event_row_class(event) } .event-item-timestamp - #{time_ago_with_tooltip(event.created_at)} + #{time_ago_with_tooltip(event.created_at, skip_js: true)} = cache [event, current_application_settings, "v2.2"] do = author_avatar(event, size: 40) -- cgit v1.2.1 From 7b09a27e9b27c4f62cfffdc0371a01e5f191980f Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 29 Aug 2016 13:21:37 -0500 Subject: Remove prefixes from transition CSS property --- CHANGELOG | 1 + app/assets/stylesheets/framework/mixins.scss | 8 -------- app/assets/stylesheets/framework/selects.scss | 2 +- app/assets/stylesheets/pages/search.scss | 4 ++-- 4 files changed, 4 insertions(+), 11 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fec019e5abb..c213ca18bfb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.12.0 (unreleased) - Add `wiki_page_events` to project hook APIs (Ben Boeckel) - Remove Gitorious import - Fix inconsistent background color for filter input field (ClemMakesApps) + - Remove prefixes from transition CSS property (ClemMakesApps) - Add Sentry logging to API calls - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling) - Remove unused mixins (ClemMakesApps) diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 396a37bab6e..62dc2cb3fdc 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -9,14 +9,6 @@ border-radius: $radius; } -@mixin transition($transition) { - -webkit-transition: $transition; - -moz-transition: $transition; - -ms-transition: $transition; - -o-transition: $transition; - transition: $transition; -} - /** * Prefilled mixins * Mixins with fixed values diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index b2e22b60440..c75dacf95d9 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -151,7 +151,7 @@ background-position: right 0 bottom 6px; border: 1px solid $input-border; @include border-radius($border-radius-default); - @include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; &:focus { border-color: $input-border-focus; diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index c9d436d72ba..436fb00ba2e 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -80,7 +80,7 @@ .search-icon { @extend .fa-search; - @include transition(color .15s); + transition: color 0.15s; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -125,7 +125,7 @@ } .location-badge { - @include transition(all .15s); + transition: all 0.15s; background-color: $location-badge-active-bg; color: $white-light; } -- cgit v1.2.1 From 97b69862ad50936ad0700b23c485a38b637469a7 Mon Sep 17 00:00:00 2001 From: tiagonbotelho Date: Wed, 31 Aug 2016 12:35:23 +0100 Subject: add specs for tags finder --- app/controllers/projects/tags_controller.rb | 5 +- app/finders/tags_finder.rb | 29 +++++++++++ app/views/projects/tags/index.html.haml | 5 +- spec/finders/tags_finder_spec.rb | 79 +++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 app/finders/tags_finder.rb create mode 100644 spec/finders/tags_finder_spec.rb diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 3e5de2e0d3e..6ea8ee62bc5 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -1,5 +1,6 @@ class Projects::TagsController < Projects::ApplicationController include SortingHelper + # Authorize before_action :require_non_empty_project before_action :authorize_download_code! @@ -7,7 +8,9 @@ class Projects::TagsController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:destroy] def index - @sort = params[:sort] || 'name' + params[:sort] = params[:sort].presence || 'name' + + @sort = params[:sort] @tags = TagsFinder.new(@repository, params).execute @tags = Kaminari.paginate_array(@tags).page(params[:page]) diff --git a/app/finders/tags_finder.rb b/app/finders/tags_finder.rb new file mode 100644 index 00000000000..b474f0805dc --- /dev/null +++ b/app/finders/tags_finder.rb @@ -0,0 +1,29 @@ +class TagsFinder + def initialize(repository, params) + @repository = repository + @params = params + end + + def execute + tags = @repository.tags_sorted_by(sort) + filter_by_name(tags) + end + + private + + def sort + @params[:sort].presence + end + + def search + @params[:search].presence + end + + def filter_by_name(tags) + if search + tags.select { |tag| tag.name.include?(search) } + else + tags + end + end +end diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 31a023f24cf..6adbe9351dc 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -12,7 +12,8 @@ = search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } .dropdown.inline %button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} } - %span.light= @sort.humanize + %span.light + = @sort.humanize %b.caret %ul.dropdown-menu.dropdown-menu-align-right %li @@ -22,7 +23,7 @@ = sort_title_recently_updated = link_to filter_tags_path(sort: sort_value_oldest_updated) do = sort_title_oldest_updated - - if can? current_user, :push_code, @project + - if can?(current_user, :push_code, @project) = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do New tag diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb new file mode 100644 index 00000000000..2ac810e478a --- /dev/null +++ b/spec/finders/tags_finder_spec.rb @@ -0,0 +1,79 @@ +require 'spec_helper' + +describe TagsFinder do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:repository) { project.repository } + + describe '#execute' do + context 'sort only' do + it 'sorts by name' do + tags_finder = described_class.new(repository, {}) + + result = tags_finder.execute + + expect(result.first.name).to eq("v1.0.0") + end + + it 'sorts by recently_updated' do + tags_finder = described_class.new(repository, { sort: 'updated_desc' }) + + result = tags_finder.execute + recently_updated_tag = repository.tags.max do |a, b| + repository.commit(a.target).committed_date <=> repository.commit(b.target).committed_date + end + + expect(result.first.name).to eq(recently_updated_tag.name) + end + + it 'sorts by last_updated' do + tags_finder = described_class.new(repository, { sort: 'updated_asc' }) + + result = tags_finder.execute + + expect(result.first.name).to eq('v1.0.0') + end + end + + context 'filter only' do + it 'filters tags by name' do + tags_finder = described_class.new(repository, { search: '1.0.0' }) + + result = tags_finder.execute + + expect(result.first.name).to eq('v1.0.0') + expect(result.count).to eq(1) + end + + it 'does not find any tags with that name' do + tags_finder = described_class.new(repository, { search: 'hey' }) + + result = tags_finder.execute + + expect(result.count).to eq(0) + end + end + + context 'filter and sort' do + it 'filters tags by name and sorts by recently_updated' do + params = { sort: 'updated_desc', search: 'v1' } + tags_finder = described_class.new(repository, params) + + result = tags_finder.execute + + expect(result.first.name).to eq('v1.1.0') + expect(result.count).to eq(2) + end + + it 'filters tags by name and sorts by last_updated' do + params = { sort: 'updated_asc', search: 'v1' } + tags_finder = described_class.new(repository, params) + + result = tags_finder.execute + + expect(result.first.name).to eq('v1.0.0') + expect(result.count).to eq(2) + end + end + end +end -- cgit v1.2.1 From 6a58af3a4a1c3122d57238505898f61c40bf7b54 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 25 Aug 2016 18:34:01 -0500 Subject: Add BroadcastMessage API implementation --- CHANGELOG | 1 + lib/api/api.rb | 1 + lib/api/broadcast_messages.rb | 99 +++++++++++++++ lib/api/entities.rb | 5 + spec/requests/api/broadcast_messages_spec.rb | 180 +++++++++++++++++++++++++++ 5 files changed, 286 insertions(+) create mode 100644 lib/api/broadcast_messages.rb create mode 100644 spec/requests/api/broadcast_messages_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 2b727491760..e3935038517 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.12.0 (unreleased) - Remove Gitorious import - Fix inconsistent background color for filter input field (ClemMakesApps) - Add Sentry logging to API calls + - Add BroadcastMessage API - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling) - Remove unused mixins (ClemMakesApps) - Add search to all issue board lists diff --git a/lib/api/api.rb b/lib/api/api.rb index 4602e627fdb..e14464c1b0d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -31,6 +31,7 @@ module API mount ::API::AccessRequests mount ::API::AwardEmoji mount ::API::Branches + mount ::API::BroadcastMessages mount ::API::Builds mount ::API::CommitStatuses mount ::API::Commits diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb new file mode 100644 index 00000000000..fb2a4148011 --- /dev/null +++ b/lib/api/broadcast_messages.rb @@ -0,0 +1,99 @@ +module API + class BroadcastMessages < Grape::API + before { authenticate! } + before { authenticated_as_admin! } + + resource :broadcast_messages do + helpers do + def find_message + BroadcastMessage.find(params[:id]) + end + end + + desc 'Get all broadcast messages' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage + end + params do + optional :page, type: Integer, desc: 'Current page number' + optional :per_page, type: Integer, desc: 'Number of messages per page' + end + get do + messages = BroadcastMessage.all + + present paginate(messages), with: Entities::BroadcastMessage + end + + desc 'Create a broadcast message' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage + end + params do + requires :message, type: String, desc: 'Message to display' + optional :starts_at, type: DateTime, desc: 'Starting time', default: -> { Time.zone.now } + optional :ends_at, type: DateTime, desc: 'Ending time', default: -> { 1.hour.from_now } + optional :color, type: String, desc: 'Background color' + optional :font, type: String, desc: 'Foreground color' + end + post do + create_params = declared(params, include_missing: false).to_h + message = BroadcastMessage.create(create_params) + + if message.persisted? + present message, with: Entities::BroadcastMessage + else + render_validation_error!(message) + end + end + + desc 'Get a specific broadcast message' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage + end + params do + requires :id, type: Integer, desc: 'Broadcast message ID' + end + get ':id' do + message = find_message + + present message, with: Entities::BroadcastMessage + end + + desc 'Update a broadcast message' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage + end + params do + requires :id, type: Integer, desc: 'Broadcast message ID' + optional :message, type: String, desc: 'Message to display' + optional :starts_at, type: DateTime, desc: 'Starting time' + optional :ends_at, type: DateTime, desc: 'Ending time' + optional :color, type: String, desc: 'Background color' + optional :font, type: String, desc: 'Foreground color' + end + put ':id' do + message = find_message + update_params = declared(params, include_missing: false).to_h + + if message.update(update_params) + present message, with: Entities::BroadcastMessage + else + render_validation_error!(message) + end + end + + desc 'Delete a broadcast message' do + detail 'This feature was introduced in GitLab 8.12.' + success Entities::BroadcastMessage + end + params do + requires :id, type: Integer, desc: 'Broadcast message ID' + end + delete ':id' do + message = find_message + + present message.destroy, with: Entities::BroadcastMessage + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index e3a8ff6de80..fe7468dd681 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -575,5 +575,10 @@ module API class Template < Grape::Entity expose :name, :content end + + class BroadcastMessage < Grape::Entity + expose :id, :message, :starts_at, :ends_at, :color, :font + expose :active?, as: :active + end end end diff --git a/spec/requests/api/broadcast_messages_spec.rb b/spec/requests/api/broadcast_messages_spec.rb new file mode 100644 index 00000000000..7c9078b2864 --- /dev/null +++ b/spec/requests/api/broadcast_messages_spec.rb @@ -0,0 +1,180 @@ +require 'spec_helper' + +describe API::BroadcastMessages, api: true do + include ApiHelpers + + let(:user) { create(:user) } + let(:admin) { create(:admin) } + + describe 'GET /broadcast_messages' do + it 'returns a 401 for anonymous users' do + get api('/broadcast_messages') + + expect(response).to have_http_status(401) + end + + it 'returns a 403 for users' do + get api('/broadcast_messages', user) + + expect(response).to have_http_status(403) + end + + it 'returns an Array of BroadcastMessages for admins' do + create(:broadcast_message) + + get api('/broadcast_messages', admin) + + expect(response).to have_http_status(200) + expect(json_response).to be_kind_of(Array) + expect(json_response.first.keys) + .to match_array(%w(id message starts_at ends_at color font active)) + end + end + + describe 'GET /broadcast_messages/:id' do + let!(:message) { create(:broadcast_message) } + + it 'returns a 401 for anonymous users' do + get api("/broadcast_messages/#{message.id}") + + expect(response).to have_http_status(401) + end + + it 'returns a 403 for users' do + get api("/broadcast_messages/#{message.id}", user) + + expect(response).to have_http_status(403) + end + + it 'returns the specified message for admins' do + get api("/broadcast_messages/#{message.id}", admin) + + expect(response).to have_http_status(200) + expect(json_response['id']).to eq message.id + expect(json_response.keys) + .to match_array(%w(id message starts_at ends_at color font active)) + end + end + + describe 'POST /broadcast_messages' do + it 'returns a 401 for anonymous users' do + post api('/broadcast_messages'), attributes_for(:broadcast_message) + + expect(response).to have_http_status(401) + end + + it 'returns a 403 for users' do + post api('/broadcast_messages', user), attributes_for(:broadcast_message) + + expect(response).to have_http_status(403) + end + + context 'as an admin' do + it 'requires the `message` parameter' do + attrs = attributes_for(:broadcast_message) + attrs.delete(:message) + + post api('/broadcast_messages', admin), attrs + + expect(response).to have_http_status(400) + expect(json_response['error']).to eq 'message is missing' + end + + it 'defines sane default start and end times' do + time = Time.zone.parse('2016-07-02 10:11:12') + travel_to(time) do + post api('/broadcast_messages', admin), message: 'Test message' + + expect(response).to have_http_status(201) + expect(json_response['starts_at']).to eq '2016-07-02T10:11:12.000Z' + expect(json_response['ends_at']).to eq '2016-07-02T11:11:12.000Z' + end + end + + it 'accepts a custom background and foreground color' do + attrs = attributes_for(:broadcast_message, color: '#000000', font: '#cecece') + + post api('/broadcast_messages', admin), attrs + + expect(response).to have_http_status(201) + expect(json_response['color']).to eq attrs[:color] + expect(json_response['font']).to eq attrs[:font] + end + end + end + + describe 'PUT /broadcast_messages/:id' do + let!(:message) { create(:broadcast_message) } + + it 'returns a 401 for anonymous users' do + put api("/broadcast_messages/#{message.id}"), + attributes_for(:broadcast_message) + + expect(response).to have_http_status(401) + end + + it 'returns a 403 for users' do + put api("/broadcast_messages/#{message.id}", user), + attributes_for(:broadcast_message) + + expect(response).to have_http_status(403) + end + + context 'as an admin' do + it 'accepts new background and foreground colors' do + attrs = { color: '#000000', font: '#cecece' } + + put api("/broadcast_messages/#{message.id}", admin), attrs + + expect(response).to have_http_status(200) + expect(json_response['color']).to eq attrs[:color] + expect(json_response['font']).to eq attrs[:font] + end + + it 'accepts new start and end times' do + time = Time.zone.parse('2016-07-02 10:11:12') + travel_to(time) do + attrs = { starts_at: Time.zone.now, ends_at: 3.hours.from_now } + + put api("/broadcast_messages/#{message.id}", admin), attrs + + expect(response).to have_http_status(200) + expect(json_response['starts_at']).to eq '2016-07-02T10:11:12.000Z' + expect(json_response['ends_at']).to eq '2016-07-02T13:11:12.000Z' + end + end + + it 'accepts a new message' do + attrs = { message: 'new message' } + + put api("/broadcast_messages/#{message.id}", admin), attrs + + expect(response).to have_http_status(200) + expect { message.reload }.to change { message.message }.to('new message') + end + end + end + + describe 'DELETE /broadcast_messages/:id' do + let!(:message) { create(:broadcast_message) } + + it 'returns a 401 for anonymous users' do + delete api("/broadcast_messages/#{message.id}"), + attributes_for(:broadcast_message) + + expect(response).to have_http_status(401) + end + + it 'returns a 403 for users' do + delete api("/broadcast_messages/#{message.id}", user), + attributes_for(:broadcast_message) + + expect(response).to have_http_status(403) + end + + it 'deletes the broadcast message for admins' do + expect { delete api("/broadcast_messages/#{message.id}", admin) } + .to change { BroadcastMessage.count }.by(-1) + end + end +end -- cgit v1.2.1 From 9d4af1b14068aa77848b982372ddf9e11567ae5f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 30 Aug 2016 16:47:44 -0300 Subject: Add BroadcastMessage API documentation --- doc/api/broadcast_messages.md | 158 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 doc/api/broadcast_messages.md diff --git a/doc/api/broadcast_messages.md b/doc/api/broadcast_messages.md new file mode 100644 index 00000000000..c3a9207a3ae --- /dev/null +++ b/doc/api/broadcast_messages.md @@ -0,0 +1,158 @@ +# Broadcast Messages + +> **Note:** This feature was introduced in GitLab 8.12. + +The broadcast message API is only accessible to administrators. All requests by +guests will respond with `401 Unauthorized`, and all requests by normal users +will respond with `403 Forbidden`. + +## Get all broadcast messages + +``` +GET /broadcast_messages +``` + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages +``` + +Example response: + +```json +[ + { + "message":"Example broadcast message", + "starts_at":"2016-08-24T23:21:16.078Z", + "ends_at":"2016-08-26T23:21:16.080Z", + "color":"#E75E40", + "font":"#FFFFFF", + "id":1, + "active": false + } +] +``` + +## Get a specific broadcast message + +``` +GET /broadcast_messages/:id +``` + +| Attribute | Type | Required | Description | +| ----------- | -------- | -------- | ------------------------- | +| `id` | integer | yes | Broadcast message ID | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1 +``` + +Example response: + +```json +{ + "message":"Deploy in progress", + "starts_at":"2016-08-24T23:21:16.078Z", + "ends_at":"2016-08-26T23:21:16.080Z", + "color":"#cecece", + "font":"#FFFFFF", + "id":1, + "active":false +} +``` + +## Create a broadcast message + +Responds with `400 Bad request` when the `message` parameter is missing or the +`color` or `font` values are invalid, and `201 Created` when the broadcast +message was successfully created. + +``` +POST /broadcast_messages +``` + +| Attribute | Type | Required | Description | +| ----------- | -------- | -------- | ---------------------------------------------------- | +| `message` | string | yes | Message to display | +| `starts_at` | datetime | no | Starting time (defaults to current time) | +| `ends_at` | datetime | no | Ending time (defaults to one hour from current time) | +| `color` | string | no | Background color hex code | +| `font` | string | no | Foreground color hex code | + +```bash +curl --data "message=Deploy in progress&color=#cecece" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages +``` + +Example response: + +```json +{ + "message":"Deploy in progress", + "starts_at":"2016-08-26T00:41:35.060Z", + "ends_at":"2016-08-26T01:41:35.060Z", + "color":"#cecece", + "font":"#FFFFFF", + "id":1, + "active": true +} +``` + +## Update a broadcast message + +``` +PUT /broadcast_messages/:id +``` + +| Attribute | Type | Required | Description | +| ----------- | -------- | -------- | ------------------------- | +| `id` | integer | yes | Broadcast message ID | +| `message` | string | no | Message to display | +| `starts_at` | datetime | no | Starting time | +| `ends_at` | datetime | no | Ending time | +| `color` | string | no | Background color hex code | +| `font` | string | no | Foreground color hex code | + +```bash +curl --request PUT --data "message=Update message&color=#000" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1 +``` + +Example response: + +```json +{ + "message":"Update message", + "starts_at":"2016-08-26T00:41:35.060Z", + "ends_at":"2016-08-26T01:41:35.060Z", + "color":"#000", + "font":"#FFFFFF", + "id":1, + "active": true +} +``` + +## Delete a broadcast message + +``` +DELETE /broadcast_messages/:id +``` + +| Attribute | Type | Required | Description | +| ----------- | -------- | -------- | ------------------------- | +| `id` | integer | yes | Broadcast message ID | + +```bash +curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/broadcast_messages/1 +``` + +Example response: + +```json +{ + "message":"Update message", + "starts_at":"2016-08-26T00:41:35.060Z", + "ends_at":"2016-08-26T01:41:35.060Z", + "color":"#000", + "font":"#FFFFFF", + "id":1, + "active": true +} +``` -- cgit v1.2.1 From 9b376e772edda214e189752c13d831bae7f1088d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 25 Aug 2016 11:16:28 -0300 Subject: GitHub importer use default project visibility for non-private projects --- lib/gitlab/github_import/project_creator.rb | 2 +- .../gitlab/github_import/project_creator_spec.rb | 54 ++++++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index f4221003db5..8237fe104d6 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -17,7 +17,7 @@ module Gitlab path: repo.name, description: repo.description, namespace_id: namespace.id, - visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, + visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility, import_type: "github", import_source: repo.full_name, import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@"), diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index 0f363b8b0aa..014ee462e5c 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -2,33 +2,59 @@ require 'spec_helper' describe Gitlab::GithubImport::ProjectCreator, lib: true do let(:user) { create(:user) } + let(:namespace) { create(:group, owner: user) } + let(:repo) do OpenStruct.new( login: 'vim', name: 'vim', - private: true, full_name: 'asd/vim', - clone_url: "https://gitlab.com/asd/vim.git", - owner: OpenStruct.new(login: "john") + clone_url: 'https://gitlab.com/asd/vim.git' ) end - let(:namespace) { create(:group, owner: user) } - let(:token) { "asdffg" } - let(:access_params) { { github_access_token: token } } + + subject(:service) { described_class.new(repo, namespace, user, github_access_token: 'asdffg') } before do namespace.add_owner(user) + allow_any_instance_of(Project).to receive(:add_import_job) end - it 'creates project' do - allow_any_instance_of(Project).to receive(:add_import_job) + describe '#execute' do + it 'creates a project' do + expect { service.execute }.to change(Project, :count).by(1) + end + + it 'handle GitHub credentials' do + project = service.execute + + expect(project.import_url).to eq('https://asdffg@gitlab.com/asd/vim.git') + expect(project.safe_import_url).to eq('https://*****@gitlab.com/asd/vim.git') + expect(project.import_data.credentials).to eq(user: 'asdffg', password: nil) + end + + context 'when Github project is private' do + it 'sets project visibility to private' do + repo.private = true + + project = service.execute + + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + end + + context 'when Github project is public' do + before do + allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL) + end + + it 'sets project visibility to the default project visibility' do + repo.private = false - project_creator = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, user, access_params) - project = project_creator.execute + project = service.execute - expect(project.import_url).to eq("https://asdffg@gitlab.com/asd/vim.git") - expect(project.safe_import_url).to eq("https://*****@gitlab.com/asd/vim.git") - expect(project.import_data.credentials).to eq(user: "asdffg", password: nil) - expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + end + end end end -- cgit v1.2.1 From a41b5e5bdc3ec5e8c44db5824665593799c530b6 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 31 Aug 2016 18:29:00 -0300 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 13ec1bb885f..9837b2edb9d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -63,6 +63,7 @@ v 8.12.0 (unreleased) v 8.11.4 (unreleased) - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) - Fix sorting issues by "last updated" doesn't work after import from GitHub + - GitHub importer use default project visibility for non-private projects - Creating an issue through our API now emails label subscribers !5720 - Block concurrent updates for Pipeline - Fix resolving conflicts on forks -- cgit v1.2.1 From 1af4989c6f448eccf554b817dc55f515165a7eee Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Wed, 31 Aug 2016 12:40:02 -0500 Subject: Center build stage columns in pipeline overview --- CHANGELOG | 1 + app/assets/stylesheets/pages/pipelines.scss | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9837b2edb9d..89142211495 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.12.0 (unreleased) - Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel) - Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling) - Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps) + - Center build stage columns in pipeline overview (ClemMakesApps) - Shorten task status phrase (ClemMakesApps) - Add hover color to emoji icon (ClemMakesApps) - Fix branches page dropdown sort alignment (ClemMakesApps) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 2d6653cd867..b56c0727660 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -2,6 +2,7 @@ .stage { max-width: 90px; width: 90px; + text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -146,6 +147,7 @@ } .stage-cell { + text-align: center; svg { height: 18px; @@ -153,10 +155,6 @@ vertical-align: middle; overflow: visible; } - - .light { - width: 3px; - } } .duration, -- cgit v1.2.1 From e71df3cdd20d5c9bcf4ecc64771a1d88babfddf8 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Tue, 30 Aug 2016 00:01:00 +1000 Subject: Order award tooltips by their created_at date --- CHANGELOG | 1 + app/models/concerns/awardable.rb | 2 +- spec/models/concerns/awardable_spec.rb | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d06fc24d40a..eeb37dbc55a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.12.0 (unreleased) - Fix branch title trailing space on hover (ClemMakesApps) - Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison) - Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison) + - Order award emoji tooltips in order they were added (EspadaV8) - Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps) - Update merge_requests.md with a simpler way to check out a merge request. !5944 - Fix button missing type (ClemMakesApps) diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index 800a16ab246..e6feaf7df4f 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,7 +2,7 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy if self < Participable # By default we always load award_emoji user association diff --git a/spec/models/concerns/awardable_spec.rb b/spec/models/concerns/awardable_spec.rb index a371c4a18a9..de791abdf3d 100644 --- a/spec/models/concerns/awardable_spec.rb +++ b/spec/models/concerns/awardable_spec.rb @@ -45,4 +45,14 @@ describe Issue, "Awardable" do expect { issue.toggle_award_emoji("thumbsdown", award_emoji.user) }.to change { AwardEmoji.count }.by(-1) end end + + describe 'querying award_emoji on an Awardable' do + let(:issue) { create(:issue) } + + it 'sorts in ascending fashion' do + create_list(:award_emoji, 3, awardable: issue) + + expect(issue.award_emoji).to eq issue.award_emoji.sort_by(&:id) + end + end end -- cgit v1.2.1 From a926f1f03e8516302d08195a705fbad2e953bcbb Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 31 Aug 2016 19:54:06 -0300 Subject: Fix suggested colors options for new labels in the admin area --- app/assets/javascripts/dispatcher.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index ba64d2bcf0b..38cdc7b9fba 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -199,6 +199,7 @@ break; case 'labels': switch (path[2]) { + case 'new': case 'edit': new Labels(); } -- cgit v1.2.1 From fdbab40d94b690bdc1890f027a89653924fa6710 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 31 Aug 2016 19:54:11 -0300 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 13ec1bb885f..16e25c436d9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -64,6 +64,7 @@ v 8.11.4 (unreleased) - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) - Fix sorting issues by "last updated" doesn't work after import from GitHub - Creating an issue through our API now emails label subscribers !5720 + - Fix suggested colors options for new labels in the admin area - Block concurrent updates for Pipeline - Fix resolving conflicts on forks - Fix diff commenting on merge requests created prior to 8.10 -- cgit v1.2.1 From 0d8352973bfbd3d1857657fce35284fcaf241cf7 Mon Sep 17 00:00:00 2001 From: winniehell Date: Sun, 17 Jul 2016 02:03:56 +0200 Subject: Use JavaScript tooltips for mentions (!5301) --- CHANGELOG | 1 + app/models/commit.rb | 9 --------- app/models/commit_range.rb | 7 ------- lib/banzai/filter/abstract_reference_filter.rb | 10 +--------- lib/banzai/filter/commit_range_reference_filter.rb | 2 +- lib/banzai/filter/commit_reference_filter.rb | 4 ---- lib/banzai/filter/label_reference_filter.rb | 5 +++++ lib/banzai/filter/milestone_reference_filter.rb | 4 ++++ lib/banzai/filter/reference_filter.rb | 2 +- spec/lib/banzai/filter/commit_range_reference_filter_spec.rb | 6 +++--- spec/lib/banzai/filter/commit_reference_filter_spec.rb | 4 ++-- spec/lib/banzai/filter/external_issue_reference_filter_spec.rb | 2 +- spec/lib/banzai/filter/issue_reference_filter_spec.rb | 4 ++-- spec/lib/banzai/filter/label_reference_filter_spec.rb | 2 +- spec/lib/banzai/filter/merge_request_reference_filter_spec.rb | 4 ++-- spec/lib/banzai/filter/milestone_reference_filter_spec.rb | 2 +- spec/lib/banzai/filter/snippet_reference_filter_spec.rb | 4 ++-- spec/lib/banzai/filter/user_reference_filter_spec.rb | 2 +- spec/models/commit_range_spec.rb | 10 ---------- 19 files changed, 28 insertions(+), 56 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 13ec1bb885f..504d16b5de7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ v 8.12.0 (unreleased) - Add search to all issue board lists - Fix groups sort dropdown alignment (ClemMakesApps) - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps) + - Use JavaScript tooltips for mentions !5301 (winniehell) - Fix markdown help references (ClemMakesApps) - Add last commit time to repo view (ClemMakesApps) - Added project specific enable/disable setting for LFS !5997 diff --git a/app/models/commit.rb b/app/models/commit.rb index 817d063e4a2..e64fd1e0c1b 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -108,15 +108,6 @@ class Commit @diff_line_count end - # Returns a string describing the commit for use in a link title - # - # Example - # - # "Commit: Alex Denisov - Project git clone panel" - def link_title - "Commit: #{author_name} - #{title}" - end - # Returns the commits title. # # Usually, the commit title is the first line of the commit message. diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 630ee9601e0..656a242c265 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -4,12 +4,10 @@ # # range = CommitRange.new('f3f85602...e86e1013', project) # range.exclude_start? # => false -# range.reference_title # => "Commits f3f85602 through e86e1013" # range.to_s # => "f3f85602...e86e1013" # # range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae', project) # range.exclude_start? # => true -# range.reference_title # => "Commits f3f85602^ through e86e1013" # range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"} # range.to_s # => "f3f85602..e86e1013" # @@ -109,11 +107,6 @@ class CommitRange reference end - # Returns a String for use in a link's title attribute - def reference_title - "Commits #{sha_start} through #{sha_to}" - end - # Return a Hash of parameters for passing to a URL helper # # See `namespace_project_compare_url` diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index d77a5e3ff09..16cd774c81a 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -18,10 +18,6 @@ module Banzai @object_sym ||= object_name.to_sym end - def self.object_class_title - @object_title ||= object_class.name.titleize - end - # Public: Find references in text (like `!123` for merge requests) # # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches| @@ -49,10 +45,6 @@ module Banzai self.class.object_sym end - def object_class_title - self.class.object_class_title - end - def references_in(*args, &block) self.class.references_in(*args, &block) end @@ -198,7 +190,7 @@ module Banzai end def object_link_title(object) - "#{object_class_title}: #{object.title}" + object.title end def object_link_text(object, matches) diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb index bbb88c979cc..4358bf45549 100644 --- a/lib/banzai/filter/commit_range_reference_filter.rb +++ b/lib/banzai/filter/commit_range_reference_filter.rb @@ -35,7 +35,7 @@ module Banzai end def object_link_title(range) - range.reference_title + nil end end end diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb index 2ce1816672b..a26dd09c25a 100644 --- a/lib/banzai/filter/commit_reference_filter.rb +++ b/lib/banzai/filter/commit_reference_filter.rb @@ -28,10 +28,6 @@ module Banzai only_path: context[:only_path]) end - def object_link_title(commit) - commit.link_title - end - def object_link_text_extras(object, matches) extras = super diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index e258dc8e2bf..8f262ef3d8d 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -70,6 +70,11 @@ module Banzai def unescape_html_entities(text) CGI.unescapeHTML(text.to_s) end + + def object_link_title(object) + # use title of wrapped element instead + nil + end end end end diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb index ca686c87d97..58fff496d00 100644 --- a/lib/banzai/filter/milestone_reference_filter.rb +++ b/lib/banzai/filter/milestone_reference_filter.rb @@ -59,6 +59,10 @@ module Banzai html_safe end end + + def object_link_title(object) + nil + end end end end diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index bf058241cda..2d221290f7e 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -52,7 +52,7 @@ module Banzai end def reference_class(type) - "gfm gfm-#{type}" + "gfm gfm-#{type} has-tooltip" end # Ensure that a :project key exists in context diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb index 593bd6d5cac..e6c90ad87ee 100644 --- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb @@ -65,14 +65,14 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do expect(reference_filter(act).to_html).to eq exp end - it 'includes a title attribute' do + it 'includes no title attribute' do doc = reference_filter("See #{reference}") - expect(doc.css('a').first.attr('title')).to eq range.reference_title + expect(doc.css('a').first.attr('title')).to eq "" end it 'includes default classes' do doc = reference_filter("See #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb index d46d3f1489e..e0f08282551 100644 --- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb @@ -55,7 +55,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do it 'includes a title attribute' do doc = reference_filter("See #{reference}") - expect(doc.css('a').first.attr('title')).to eq commit.link_title + expect(doc.css('a').first.attr('title')).to eq commit.title end it 'escapes the title attribute' do @@ -67,7 +67,7 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("See #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb index 953466679e4..7116c09fb21 100644 --- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb @@ -64,7 +64,7 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do it 'includes default classes' do doc = filter("Issue #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue has-tooltip' end it 'supports an :only_path context' do diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb index a005b4990e7..fce86a9b6ad 100644 --- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb @@ -54,7 +54,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do it 'includes a title attribute' do doc = reference_filter("Issue #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" + expect(doc.css('a').first.attr('title')).to eq issue.title end it 'escapes the title attribute' do @@ -66,7 +66,7 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Issue #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-issue has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 9276a154007..908ccebbf87 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -21,7 +21,7 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Label #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb index 805acf1c8b3..274258a045c 100644 --- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb @@ -46,7 +46,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do it 'includes a title attribute' do doc = reference_filter("Merge #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" + expect(doc.css('a').first.attr('title')).to eq merge.title end it 'escapes the title attribute' do @@ -58,7 +58,7 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Merge #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 9424f2363e1..7419863d848 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -20,7 +20,7 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Milestone #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb index 5068ddd7faa..9b92d1a3926 100644 --- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb @@ -39,7 +39,7 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do it 'includes a title attribute' do doc = reference_filter("Snippet #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" + expect(doc.css('a').first.attr('title')).to eq snippet.title end it 'escapes the title attribute' do @@ -51,7 +51,7 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Snippet #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-snippet has-tooltip' end it 'includes a data-project attribute' do diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb index 108b36a97cc..fdbdb21eac1 100644 --- a/spec/lib/banzai/filter/user_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb @@ -104,7 +104,7 @@ describe Banzai::Filter::UserReferenceFilter, lib: true do it 'includes default classes' do doc = reference_filter("Hey #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member has-tooltip' end it 'supports an :only_path context' do diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 384a38ebc69..c41359b55a3 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -76,16 +76,6 @@ describe CommitRange, models: true do end end - describe '#reference_title' do - it 'returns the correct String for three-dot ranges' do - expect(range.reference_title).to eq "Commits #{full_sha_from} through #{full_sha_to}" - end - - it 'returns the correct String for two-dot ranges' do - expect(range2.reference_title).to eq "Commits #{full_sha_from}^ through #{full_sha_to}" - end - end - describe '#to_param' do it 'includes the correct keys' do expect(range.to_param.keys).to eq %i(from to) -- cgit v1.2.1 From 91c215f9a7c5f1bd33eee4d8587bc80a92863071 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 31 Aug 2016 20:58:58 -0500 Subject: make projects dropdown accessible --- app/assets/stylesheets/framework/header.scss | 4 ++++ app/helpers/projects_helper.rb | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 2b4fc0fb068..e981de241ce 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -160,11 +160,15 @@ header { } .dropdown-toggle-caret { + color: $gl-text-color; + border: transparent; + background: transparent; position: relative; top: -2px; width: 12px; line-height: 12px; margin-left: 5px; + padding: 0; font-size: 10px; text-align: center; cursor: pointer; diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index f07077bd133..7a90a0bb465 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -61,7 +61,9 @@ module ProjectsHelper project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" } if current_user - project_link << icon("chevron-down", class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) + project_link << button_tag(type: 'button', class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) do + icon("chevron-down") + end end full_title = "#{namespace_link} / #{project_link}".html_safe -- cgit v1.2.1 From 523002e40f79ad85ffa6ce74b8ff405c17429cb6 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 31 Aug 2016 21:16:04 -0500 Subject: prevent project dropdown chevron from disappearing in small viewports --- CHANGELOG | 1 + app/assets/stylesheets/framework/header.scss | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9837b2edb9d..78c430a60af 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -31,6 +31,7 @@ v 8.12.0 (unreleased) - Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps) - Fix markdown help references (ClemMakesApps) - Add last commit time to repo view (ClemMakesApps) + - Fix accessibility and visibility of project list dropdown button !6140 - Added project specific enable/disable setting for LFS !5997 - Don't expose a user's token in the `/api/v3/user` API (!6047) - Remove redundant js-timeago-pending from user activity log (ClemMakesApps) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index e981de241ce..d3e787937e6 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -136,6 +136,8 @@ header { } .title { + position: relative; + padding-right: 20px; margin: 0; font-size: 19px; max-width: 400px; @@ -163,11 +165,11 @@ header { color: $gl-text-color; border: transparent; background: transparent; - position: relative; - top: -2px; + position: absolute; + right: 3px; width: 12px; - line-height: 12px; - margin-left: 5px; + line-height: 19px; + margin-top: (($header-height - 19) / 2); padding: 0; font-size: 10px; text-align: center; -- cgit v1.2.1 From cf1174c673ce4244236b64f25e34f1a609552ede Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 31 Aug 2016 22:55:56 -0500 Subject: add extra viewport breakpoint for project name width --- app/assets/stylesheets/framework/header.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index d3e787937e6..1036219172e 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -150,7 +150,11 @@ header { vertical-align: top; white-space: nowrap; - @media (max-width: $screen-sm-max) { + @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { + max-width: 300px; + } + + @media (max-width: $screen-xs-max) { max-width: 190px; } -- cgit v1.2.1 From d326d3428da89b943bb5f1d4d396f21b3e999ff7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 29 Aug 2016 07:46:30 -0700 Subject: Optimize branch lookups and force a repository reload for Repository#find_branch If `git gc` runs and `Repository` has an instance to `Rugged::Repository`, a bug in libgit2 may cause the instance to return a stale value or a missing branch. This change not only optimizes the branch lookup so we don't have to iterate through every branch, but it also works around the `git gc` issue by forcing a repository reload every time `Repository#find_branch` is called. See: https://github.com/libgit2/libgit2/issues/1534 Closes #15392, #21470 --- CHANGELOG | 3 +++ Gemfile | 2 +- Gemfile.lock | 4 ++-- app/models/repository.rb | 17 +++++++++++++++-- spec/models/repository_spec.rb | 18 ++++++++++++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9837b2edb9d..9ab32684de6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -60,6 +60,9 @@ v 8.12.0 (unreleased) - User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496 - Fixed invisible scroll controls on build page on iPhone +v 8.11.5 (unreleased) + - Optimize branch lookups and force a repository reload for Repository#find_branch + v 8.11.4 (unreleased) - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) - Fix sorting issues by "last updated" doesn't work after import from GitHub diff --git a/Gemfile b/Gemfile index 96841013815..620338e5997 100644 --- a/Gemfile +++ b/Gemfile @@ -53,7 +53,7 @@ gem 'browser', '~> 2.2' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem 'gitlab_git', '~> 10.4.7' +gem 'gitlab_git', '~> 10.5' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes diff --git a/Gemfile.lock b/Gemfile.lock index 1d0fcfd3c3a..28ede86b3ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -279,7 +279,7 @@ GEM diff-lcs (~> 1.1) mime-types (>= 1.16, < 3) posix-spawn (~> 0.3) - gitlab_git (10.4.7) + gitlab_git (10.5.0) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) @@ -858,7 +858,7 @@ DEPENDENCIES github-linguist (~> 4.7.0) github-markup (~> 1.4) gitlab-flowdock-git-hook (~> 1.0.1) - gitlab_git (~> 10.4.7) + gitlab_git (~> 10.5) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.2) diff --git a/app/models/repository.rb b/app/models/repository.rb index 91bdafdac99..f891e8374d2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -120,8 +120,21 @@ class Repository commits end - def find_branch(name) - raw_repository.branches.find { |branch| branch.name == name } + def find_branch(name, fresh_repo: true) + # Since the Repository object may have in-memory index changes, invalidating the memoized Repository object may + # cause unintended side effects. Because finding a branch is a read-only operation, we can safely instantiate + # a new repo here to ensure a consistent state to avoid a libgit2 bug where concurrent access (e.g. via git gc) + # may cause the branch to "disappear" erroneously or have the wrong SHA. + # + # See: https://github.com/libgit2/libgit2/issues/1534 and https://gitlab.com/gitlab-org/gitlab-ce/issues/15392 + raw_repo = + if fresh_repo + Gitlab::Git::Repository.new(path_to_repo) + else + raw_repository + end + + raw_repo.find_branch(name) end def find_tag(name) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 1fea50ad42c..812c72c48cb 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -382,6 +382,24 @@ describe Repository, models: true do end end + describe '#find_branch' do + it 'loads a branch with a fresh repo' do + expect(Gitlab::Git::Repository).to receive(:new).twice.and_call_original + + 2.times do + expect(repository.find_branch('feature')).not_to be_nil + end + end + + it 'loads a branch with a cached repo' do + expect(Gitlab::Git::Repository).to receive(:new).once.and_call_original + + 2.times do + expect(repository.find_branch('feature', fresh_repo: false)).not_to be_nil + end + end + end + describe '#rm_branch' do let(:old_rev) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' } # git rev-parse feature let(:blank_sha) { '0000000000000000000000000000000000000000' } -- cgit v1.2.1 From 51c9cea86835427af983c30ddec7753949cb0bff Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 1 Sep 2016 09:42:18 +0200 Subject: Add MR Documentation description template --- .gitlab/merge_request_templates/Documentation.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .gitlab/merge_request_templates/Documentation.md diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md new file mode 100644 index 00000000000..d2a1eb56423 --- /dev/null +++ b/.gitlab/merge_request_templates/Documentation.md @@ -0,0 +1,14 @@ +See the general Documentation guidelines http://docs.gitlab.com/ce/development/doc_styleguide.html. + +## What does this MR do? + +(briefly describe what this MR is about) + +## Moving docs to a new location? + +See the guidelines: http://docs.gitlab.com/ce/development/doc_styleguide.html#changing-document-location + +- [ ] Make sure the old link is not removed and has its contents replaced with a link to the new location. +- [ ] Make sure internal links pointing to the document in question are not broken. +- [ ] Search and replace any links referring to old docs in GitLab Rails app, specifically under the `app/views/` directory. +- [ ] If working on CE, submit an MR to EE with the changes as well. -- cgit v1.2.1 From 25c84fd629e72acc753b920a35eb3cfe1cf93035 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 1 Sep 2016 10:48:34 +0100 Subject: Fixed issue where moving issue & then scrolling stop new page loading --- app/assets/javascripts/boards/models/list.js.es6 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/boards/models/list.js.es6 b/app/assets/javascripts/boards/models/list.js.es6 index 296a033071e..91fd620fdb3 100644 --- a/app/assets/javascripts/boards/models/list.js.es6 +++ b/app/assets/javascripts/boards/models/list.js.es6 @@ -52,7 +52,7 @@ class List { } nextPage () { - if (Math.floor(this.issues.length / 20) === this.page) { + if (this.issuesSize > this.issues.length) { this.page++; return this.getIssues(false); @@ -94,15 +94,20 @@ class List { } addIssue (issue, listFrom) { - this.issues.push(issue); + if (!this.findIssue(issue.id)) { + this.issues.push(issue); - if (this.label) { - issue.addLabel(this.label); - } + if (this.label) { + issue.addLabel(this.label); + } - if (listFrom) { - this.issuesSize++; - gl.boardService.moveIssue(issue.id, listFrom.id, this.id); + if (listFrom) { + this.issuesSize++; + gl.boardService.moveIssue(issue.id, listFrom.id, this.id) + .then(() => { + listFrom.getIssues(false); + }); + } } } -- cgit v1.2.1 From 195d79dce0ea424aeea78e8ad35ece83bd708641 Mon Sep 17 00:00:00 2001 From: Regis Date: Thu, 1 Sep 2016 05:38:58 -0600 Subject: path const and prevent default location --- app/assets/javascripts/user.js.es6 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index c934afa458a..d644a1c0bc6 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -22,12 +22,12 @@ hideProjectLimitMessage() { $('.hide-project-limit-message').on('click', e => { - const path = '/'; + e.preventDefault(); + const path = gon.relative_url_root || '/'; $.cookie('hide_project_limit_message', 'false', { path: path }); $(this).parents('.project-limit-message').remove(); - e.preventDefault(); return; }); } -- cgit v1.2.1 From 0d45972c936bc498b2e9d1f552099b4679afd607 Mon Sep 17 00:00:00 2001 From: Regis Date: Thu, 1 Sep 2016 05:40:38 -0600 Subject: single quotes for 'placement': 'top' k-v --- app/assets/javascripts/user.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index d644a1c0bc6..f9cdf82f529 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -9,7 +9,7 @@ placeTop() { $('.profile-groups-avatars').tooltip({ - "placement": "top" + 'placement': 'top' }); } -- cgit v1.2.1 From ee419bf48b16b91b2a47f877f169fbf531f8ae11 Mon Sep 17 00:00:00 2001 From: Regis Date: Thu, 1 Sep 2016 05:54:10 -0600 Subject: no string wraps on keys --- app/assets/javascripts/user.js.es6 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index f9cdf82f529..833f35df59b 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -9,7 +9,7 @@ placeTop() { $('.profile-groups-avatars').tooltip({ - 'placement': 'top' + placement: 'top' }); } -- cgit v1.2.1 From beff8b9bd077fea4bbcb09881396b57ba07f568e Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sat, 27 Aug 2016 23:45:01 +0100 Subject: Swapped out author dropdown and started on swapping out project dropdown --- app/assets/javascripts/gl_dropdown.js | 1 + app/helpers/issuables_helper.rb | 13 ++++++++++++ app/helpers/todos_helper.rb | 26 ++++++++++-------------- app/views/dashboard/todos/index.html.haml | 33 ++++++++++++++++++++----------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 77b2082cba0..ba59ce3c956 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -202,6 +202,7 @@ var ref, ref1, ref2, ref3, searchFields, selector, self; this.el = el1; this.options = options; + console.log(this.options); this.updateLabel = bind(this.updateLabel, this); this.hidden = bind(this.hidden, this); this.opened = bind(this.opened, this); diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index b9baeb1d6c4..dcf2ef6bb0a 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -49,6 +49,19 @@ module IssuablesHelper end end + def project_dropdown_label(project_id, default_label) + return default_label if project_id.nil? + return "Any project" if project_id == "0" + + project = Project.find_by(id: project_id) + + if project + project.name_with_namespace || project.name + else + default_label + end + end + def milestone_dropdown_label(milestone_title, default_label = "Milestone") if milestone_title == Milestone::Upcoming.name milestone_title = Milestone::Upcoming.title diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index 0465327060e..a9f4c8b07b5 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -78,13 +78,11 @@ module TodosHelper end def todo_actions_options - actions = [ - OpenStruct.new(id: '', title: 'Any Action'), - OpenStruct.new(id: Todo::ASSIGNED, title: 'Assigned'), - OpenStruct.new(id: Todo::MENTIONED, title: 'Mentioned') + [ + { id: '', text: 'Any Action' }, + { id: Todo::ASSIGNED, text: 'Assigned' }, + { id: Todo::MENTIONED, text: 'Mentioned' } ] - - options_from_collection_for_select(actions, 'id', 'title', params[:action_id]) end def todo_projects_options @@ -92,22 +90,18 @@ module TodosHelper projects = projects.includes(:namespace) projects = projects.map do |project| - OpenStruct.new(id: project.id, title: project.name_with_namespace) + { id: project.id, text: project.name_with_namespace } end - projects.unshift(OpenStruct.new(id: '', title: 'Any Project')) - - options_from_collection_for_select(projects, 'id', 'title', params[:project_id]) + projects.unshift({ id: '', text: 'Any Project' }).to_json end def todo_types_options - types = [ - OpenStruct.new(title: 'Any Type', name: ''), - OpenStruct.new(title: 'Issue', name: 'Issue'), - OpenStruct.new(title: 'Merge Request', name: 'MergeRequest') + [ + { text: 'Any Type', id: '' }, + { text: 'Issue', id: 'Issue' }, + { text: 'Merge Request', id: 'MergeRequest' } ] - - options_from_collection_for_select(types, 'name', 'title', params[:type]) end private diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 6bcc37042ea..42dc1fd0118 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -28,20 +28,23 @@ .row-content-block.second-block = form_tag todos_filter_path(without: [:project_id, :author_id, :type, :action_id]), method: :get, class: 'filter-form' do .filter-item.inline - = select_tag('project_id', todo_projects_options, - class: 'select2 trigger-submit', include_blank: true, - data: {placeholder: 'Project'}) + - if params[:project_id].present? + = hidden_field_tag(:project_id, params[:project_id]) + = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit', + placeholder: 'Search projects', data: { data: todo_projects_options, selected: params[:project_id], field_name: 'project_id', default_label: 'Project' } }) .filter-item.inline - = users_select_tag(:author_id, selected: params[:author_id], - placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true) + - if params[:author_id].present? + = hidden_field_tag(:author_id, params[:author_id]) + = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', + placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } }) .filter-item.inline - = select_tag('type', todo_types_options, - class: 'select2 trigger-submit', include_blank: true, - data: {placeholder: 'Type'}) + -# = select_tag('type', todo_types_options, + -# class: 'select2 trigger-submit', include_blank: true, + -# data: {placeholder: 'Type'}) .filter-item.inline.actions-filter - = select_tag('action_id', todo_actions_options, - class: 'select2 trigger-submit', include_blank: true, - data: {placeholder: 'Action'}) + -# = select_tag('action_id', todo_actions_options, + -# class: 'select2 trigger-submit', include_blank: true, + -# data: {placeholder: 'Action'}) .pull-right .dropdown.inline.prepend-left-10 @@ -80,6 +83,14 @@ :javascript new UsersSelect(); + $projectDropdown = $('.js-project-search'); + $projectDropdown.glDropdown({ + filterable: true, + selectable: true, + fieldName: 'project_id', + data: $projectDropdown.data('data') + }); + $('form.filter-form').on('submit', function (event) { event.preventDefault(); Turbolinks.visit(this.action + '&' + $(this).serialize()); -- cgit v1.2.1 From 0e2dd06f259a42e360cb73a9f17de46cc0ab31fd Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 28 Aug 2016 00:09:21 +0100 Subject: Completed project filter dropdown, still need to move it from inline to ProjectSelect.js (or different) --- app/assets/javascripts/gl_dropdown.js | 1 - app/helpers/issuables_helper.rb | 2 +- app/views/dashboard/todos/index.html.haml | 8 +++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index ba59ce3c956..77b2082cba0 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -202,7 +202,6 @@ var ref, ref1, ref2, ref3, searchFields, selector, self; this.el = el1; this.options = options; - console.log(this.options); this.updateLabel = bind(this.updateLabel, this); this.hidden = bind(this.hidden, this); this.opened = bind(this.opened, this); diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index dcf2ef6bb0a..5c04bba323f 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -56,7 +56,7 @@ module IssuablesHelper project = Project.find_by(id: project_id) if project - project.name_with_namespace || project.name + project.name_with_namespace else default_label end diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 42dc1fd0118..ffdb88975a5 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -88,7 +88,13 @@ filterable: true, selectable: true, fieldName: 'project_id', - data: $projectDropdown.data('data') + data: $projectDropdown.data('data'), + clicked: function() { + if ($projectDropdown.hasClass('js-filter-submit')) { + console.log('booM!'); + return $projectDropdown.closest('form').submit(); + } + } }); $('form.filter-form').on('submit', function (event) { -- cgit v1.2.1 From f157a9e5144fde90dc31add4006b9132e1489aa1 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 28 Aug 2016 00:18:48 +0100 Subject: Added type and action dropdowns, need to finalize by removing all inline and polishing off the selected dropdown states --- app/views/dashboard/todos/index.html.haml | 40 ++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index ffdb88975a5..4a8536315a5 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -38,14 +38,15 @@ = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } }) .filter-item.inline - -# = select_tag('type', todo_types_options, - -# class: 'select2 trigger-submit', include_blank: true, - -# data: {placeholder: 'Type'}) + - if params[:type].present? + = hidden_field_tag(:type, params[:type]) + = dropdown_tag(params[:type] || 'Type', options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit', + data: { data: todo_types_options, selected: params[:type], field_name: 'type', default_label: 'Type' } }) .filter-item.inline.actions-filter - -# = select_tag('action_id', todo_actions_options, - -# class: 'select2 trigger-submit', include_blank: true, - -# data: {placeholder: 'Action'}) - + - if params[:action_id].present? + = hidden_field_tag(:action_id, params[:action_id]) + = dropdown_tag(params[:action_id] || 'Action', options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit', + data: { data: todo_actions_options, selected: params[:action_id], field_name: 'action_id', default_label: 'Action' } }) .pull-right .dropdown.inline.prepend-left-10 %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} @@ -91,12 +92,35 @@ data: $projectDropdown.data('data'), clicked: function() { if ($projectDropdown.hasClass('js-filter-submit')) { - console.log('booM!'); return $projectDropdown.closest('form').submit(); } } }); + $typeDropdown = $('.js-type-search'); + $typeDropdown.glDropdown({ + selectable: true, + fieldName: 'type_id', + data: $typeDropdown.data('data'), + clicked: function() { + if ($typeDropdown.hasClass('js-filter-submit')) { + return $typeDropdown.closest('form').submit(); + } + } + }); + + $actionDropdown = $('.js-action-search'); + $actionDropdown.glDropdown({ + selectable: true, + fieldName: 'action_id', + data: $actionDropdown.data('data'), + clicked: function() { + if ($actionDropdown.hasClass('js-filter-submit')) { + return $actionDropdown.closest('form').submit(); + } + } + }); + $('form.filter-form').on('submit', function (event) { event.preventDefault(); Turbolinks.visit(this.action + '&' + $(this).serialize()); -- cgit v1.2.1 From 922b38a0bea8c5b8610120dfb5ea168db713f3e3 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 28 Aug 2016 16:24:48 +0100 Subject: Removed inline JS and improved dropdown labels --- app/assets/javascripts/todos.js | 56 +++++++++++++++++++++++++++++++ app/helpers/todos_helper.rb | 16 +++++++-- app/views/dashboard/todos/index.html.haml | 53 +++-------------------------- 3 files changed, 73 insertions(+), 52 deletions(-) diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index 06605320a35..ef1eadfb9c0 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -13,6 +13,7 @@ this.perPage = this.el.data('perPage'); this.clearListeners(); this.initBtnListeners(); + this.initFilters(); } Todos.prototype.clearListeners = function() { @@ -27,6 +28,61 @@ return $('.todo').on('click', this.goToTodoUrl); }; + Todos.prototype.initFilters = function() { + new UsersSelect(); + this.initProjectFilterDropdown(); + this.initTypeFilterDropdown(); + this.initActionFilterDropdown(); + + $('form.filter-form').on('submit', function (event) { + event.preventDefault(); + Turbolinks.visit(this.action + '&' + $(this).serialize()); + }); + }; + + Todos.prototype.initProjectFilterDropdown = function() { + $projectDropdown = $('.js-project-search'); + $projectDropdown.glDropdown({ + filterable: true, + selectable: true, + fieldName: 'project_id', + data: $projectDropdown.data('data'), + clicked: function() { + if ($projectDropdown.hasClass('js-filter-submit')) { + return $projectDropdown.closest('form.filter-form').submit(); + } + } + }); + }; + + Todos.prototype.initTypeFilterDropdown = function() { + $typeDropdown = $('.js-type-search'); + $typeDropdown.glDropdown({ + selectable: true, + fieldName: 'type', + data: $typeDropdown.data('data'), + clicked: function() { + if ($typeDropdown.hasClass('js-filter-submit')) { + return $typeDropdown.closest('form.filter-form').submit(); + } + } + }); + }; + + Todos.prototype.initActionFilterDropdown = function() { + $actionDropdown = $('.js-action-search'); + $actionDropdown.glDropdown({ + selectable: true, + fieldName: 'action_id', + data: $actionDropdown.data('data'), + clicked: function() { + if ($actionDropdown.hasClass('js-filter-submit')) { + return $actionDropdown.closest('form.filter-form').submit(); + } + } + }); + }; + Todos.prototype.doneClicked = function(e) { var $this; e.preventDefault(); diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb index a9f4c8b07b5..1e86f648203 100644 --- a/app/helpers/todos_helper.rb +++ b/app/helpers/todos_helper.rb @@ -98,12 +98,22 @@ module TodosHelper def todo_types_options [ - { text: 'Any Type', id: '' }, - { text: 'Issue', id: 'Issue' }, - { text: 'Merge Request', id: 'MergeRequest' } + { id: '', text: 'Any Type' }, + { id: 'Issue', text: 'Issue' }, + { id: 'MergeRequest', text: 'Merge Request' } ] end + def todo_actions_dropdown_label(selected_action_id, default_action) + selected_action = todo_actions_options.find { |action| action[:id] == selected_action_id.to_i} + selected_action ? selected_action[:text] : default_action + end + + def todo_types_dropdown_label(selected_type, default_type) + selected_type = todo_types_options.find { |type| type[:id] == selected_type && type[:id] != '' } + selected_type ? selected_type[:text] : default_type + end + private def show_todo_state?(todo) diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 4a8536315a5..70dd4db6aaf 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -40,13 +40,13 @@ .filter-item.inline - if params[:type].present? = hidden_field_tag(:type, params[:type]) - = dropdown_tag(params[:type] || 'Type', options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit', - data: { data: todo_types_options, selected: params[:type], field_name: 'type', default_label: 'Type' } }) + = dropdown_tag(todo_types_dropdown_label(params[:type], 'Type'), options: { toggle_class: 'js-type-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-type js-filter-submit', + data: { data: todo_types_options } }) .filter-item.inline.actions-filter - if params[:action_id].present? = hidden_field_tag(:action_id, params[:action_id]) - = dropdown_tag(params[:action_id] || 'Action', options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit', - data: { data: todo_actions_options, selected: params[:action_id], field_name: 'action_id', default_label: 'Action' } }) + = dropdown_tag(todo_actions_dropdown_label(params[:action_id], 'Action'), options: { toggle_class: 'js-action-search js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-action js-filter-submit', + data: { data: todo_actions_options }}) .pull-right .dropdown.inline.prepend-left-10 %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'} @@ -80,48 +80,3 @@ = paginate @todos, theme: "gitlab" - else .nothing-here-block You're all done! - -:javascript - new UsersSelect(); - - $projectDropdown = $('.js-project-search'); - $projectDropdown.glDropdown({ - filterable: true, - selectable: true, - fieldName: 'project_id', - data: $projectDropdown.data('data'), - clicked: function() { - if ($projectDropdown.hasClass('js-filter-submit')) { - return $projectDropdown.closest('form').submit(); - } - } - }); - - $typeDropdown = $('.js-type-search'); - $typeDropdown.glDropdown({ - selectable: true, - fieldName: 'type_id', - data: $typeDropdown.data('data'), - clicked: function() { - if ($typeDropdown.hasClass('js-filter-submit')) { - return $typeDropdown.closest('form').submit(); - } - } - }); - - $actionDropdown = $('.js-action-search'); - $actionDropdown.glDropdown({ - selectable: true, - fieldName: 'action_id', - data: $actionDropdown.data('data'), - clicked: function() { - if ($actionDropdown.hasClass('js-filter-submit')) { - return $actionDropdown.closest('form').submit(); - } - } - }); - - $('form.filter-form').on('submit', function (event) { - event.preventDefault(); - Turbolinks.visit(this.action + '&' + $(this).serialize()); - }); -- cgit v1.2.1 From bd595eb82cd239c123818be9144258fdbaaae79c Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 28 Aug 2016 17:46:01 +0100 Subject: Removed select2 from todos feature spec --- features/steps/dashboard/todos.rb | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/features/steps/dashboard/todos.rb b/features/steps/dashboard/todos.rb index 0607086c166..344b6fda9a6 100644 --- a/features/steps/dashboard/todos.rb +++ b/features/steps/dashboard/todos.rb @@ -3,7 +3,6 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps include SharedPaths include SharedProject include SharedUser - include Select2Helper step '"John Doe" is a developer of project "Shop"' do project.team << [john_doe, :developer] @@ -55,7 +54,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps expect(page).to have_content 'To do 0' expect(page).to have_content 'Done 4' expect(page).to have_content "You're all done!" - expect(page).not_to have_link project.name_with_namespace + expect('.prepend-top-default').not_to have_link project.name_with_namespace should_not_see_todo "John Doe assigned you merge request #{merge_request.to_reference}" should_not_see_todo "John Doe mentioned you on issue #{issue.to_reference}" should_not_see_todo "John Doe assigned you issue #{issue.to_reference}" @@ -80,19 +79,31 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps end step 'I filter by "Enterprise"' do - select2(enterprise.id, from: "#project_id") + click_button 'Project' + page.within '.dropdown-menu-project' do + click_link enterprise.name_with_namespace + end end step 'I filter by "John Doe"' do - select2(john_doe.id, from: "#author_id") + click_button 'Author' + page.within '.dropdown-menu-author' do + click_link john_doe.username + end end step 'I filter by "Issue"' do - select2('Issue', from: "#type") + click_button 'Type' + page.within '.dropdown-menu-type' do + click_link 'Issue' + end end step 'I filter by "Mentioned"' do - select2("#{Todo::MENTIONED}", from: '#action_id') + click_button 'Action' + page.within '.dropdown-menu-action' do + click_link 'Mentioned' + end end step 'I should not see todos' do -- cgit v1.2.1 From 0ccdb41ced96c304658cf88b0869c5b78a4f15b7 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Tue, 30 Aug 2016 00:28:44 +0100 Subject: Review changes, simplified dropdown init --- app/assets/javascripts/todos.js | 51 ++++++------------------------- app/views/dashboard/todos/index.html.haml | 6 ++-- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index ef1eadfb9c0..a908f730954 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -30,9 +30,9 @@ Todos.prototype.initFilters = function() { new UsersSelect(); - this.initProjectFilterDropdown(); - this.initTypeFilterDropdown(); - this.initActionFilterDropdown(); + this.initFilterDropdown($('.js-project-search'), 'project_id', true); + this.initFilterDropdown($('.js-type-search'), 'type'); + this.initFilterDropdown($('.js-action-search'), 'action_id'); $('form.filter-form').on('submit', function (event) { event.preventDefault(); @@ -40,47 +40,16 @@ }); }; - Todos.prototype.initProjectFilterDropdown = function() { - $projectDropdown = $('.js-project-search'); - $projectDropdown.glDropdown({ - filterable: true, + Todos.prototype.initFilterDropdown = function($dropdown, fieldName, isFilterable) { + $dropdown.glDropdown({ selectable: true, - fieldName: 'project_id', - data: $projectDropdown.data('data'), + filterable: isFilterable, + fieldName: fieldName, + data: $dropdown.data('data'), clicked: function() { - if ($projectDropdown.hasClass('js-filter-submit')) { - return $projectDropdown.closest('form.filter-form').submit(); - } + return $dropdown.closest('form.filter-form').submit(); } - }); - }; - - Todos.prototype.initTypeFilterDropdown = function() { - $typeDropdown = $('.js-type-search'); - $typeDropdown.glDropdown({ - selectable: true, - fieldName: 'type', - data: $typeDropdown.data('data'), - clicked: function() { - if ($typeDropdown.hasClass('js-filter-submit')) { - return $typeDropdown.closest('form.filter-form').submit(); - } - } - }); - }; - - Todos.prototype.initActionFilterDropdown = function() { - $actionDropdown = $('.js-action-search'); - $actionDropdown.glDropdown({ - selectable: true, - fieldName: 'action_id', - data: $actionDropdown.data('data'), - clicked: function() { - if ($actionDropdown.hasClass('js-filter-submit')) { - return $actionDropdown.closest('form.filter-form').submit(); - } - } - }); + }) }; Todos.prototype.doneClicked = function(e) { diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 70dd4db6aaf..9d31f31c639 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -30,12 +30,12 @@ .filter-item.inline - if params[:project_id].present? = hidden_field_tag(:project_id, params[:project_id]) - = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit', - placeholder: 'Search projects', data: { data: todo_projects_options, selected: params[:project_id], field_name: 'project_id', default_label: 'Project' } }) + = dropdown_tag(project_dropdown_label(params[:project_id], 'Project'), options: { toggle_class: 'js-project-search js-filter-submit', title: 'Filter by project', filter: true, filterInput: 'input#project-search', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit', + placeholder: 'Search projects', data: { data: todo_projects_options } }) .filter-item.inline - if params[:author_id].present? = hidden_field_tag(:author_id, params[:author_id]) - = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', + = dropdown_tag(user_dropdown_label(params[:author_id], 'Author'), options: { toggle_class: 'js-user-search js-filter-submit js-author-search', title: 'Filter by author', filter: true, filterInput: 'input#author-search', dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author js-filter-submit', placeholder: 'Search authors', data: { any_user: 'Any Author', first_user: (current_user.username if current_user), current_user: true, project_id: (@project.id if @project), selected: params[:author_id], field_name: 'author_id', default_label: 'Author' } }) .filter-item.inline - if params[:type].present? -- cgit v1.2.1 From e67a483752310e32bc4577c03dd9042565b71c0a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Wed, 31 Aug 2016 18:33:12 +0100 Subject: Fixed project filtering --- app/assets/javascripts/todos.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/todos.js b/app/assets/javascripts/todos.js index a908f730954..23eda7d44ca 100644 --- a/app/assets/javascripts/todos.js +++ b/app/assets/javascripts/todos.js @@ -30,7 +30,7 @@ Todos.prototype.initFilters = function() { new UsersSelect(); - this.initFilterDropdown($('.js-project-search'), 'project_id', true); + this.initFilterDropdown($('.js-project-search'), 'project_id', ['text']); this.initFilterDropdown($('.js-type-search'), 'type'); this.initFilterDropdown($('.js-action-search'), 'action_id'); @@ -40,11 +40,12 @@ }); }; - Todos.prototype.initFilterDropdown = function($dropdown, fieldName, isFilterable) { + Todos.prototype.initFilterDropdown = function($dropdown, fieldName, searchFields) { $dropdown.glDropdown({ selectable: true, - filterable: isFilterable, + filterable: searchFields ? true : false, fieldName: fieldName, + search: { fields: searchFields }, data: $dropdown.data('data'), clicked: function() { return $dropdown.closest('form.filter-form').submit(); -- cgit v1.2.1 From 28db37de70fa94b2e7d287c9cfa31946f037125a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Thu, 1 Sep 2016 14:05:58 +0100 Subject: Added todo filter tests --- features/dashboard/todos.feature | 20 --------- spec/features/todos/todos_filtering_spec.rb | 63 +++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 spec/features/todos/todos_filtering_spec.rb diff --git a/features/dashboard/todos.feature b/features/dashboard/todos.feature index 42f5d6d2af7..0b23bbb7951 100644 --- a/features/dashboard/todos.feature +++ b/features/dashboard/todos.feature @@ -22,26 +22,6 @@ Feature: Dashboard Todos And I mark all todos as done Then I should see all todos marked as done - @javascript - Scenario: I filter by project - Given I filter by "Enterprise" - Then I should not see todos - - @javascript - Scenario: I filter by author - Given I filter by "John Doe" - Then I should not see todos related to "Mary Jane" in the list - - @javascript - Scenario: I filter by type - Given I filter by "Issue" - Then I should not see todos related to "Merge Requests" in the list - - @javascript - Scenario: I filter by action - Given I filter by "Mentioned" - Then I should not see todos related to "Assignments" in the list - @javascript Scenario: I click on a todo row Given I click on the todo diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb new file mode 100644 index 00000000000..83cf306437d --- /dev/null +++ b/spec/features/todos/todos_filtering_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe 'Dashboard > User filters todos', feature: true, js: true do + include WaitForAjax + + let(:user_1) { create(:user, username: 'user_1', name: 'user_1') } + let(:user_2) { create(:user, username: 'user_2', name: 'user_2') } + + let(:project_1) { create(:empty_project, name: 'project_1') } + let(:project_2) { create(:empty_project, name: 'project_2') } + + let(:issue) { create(:issue, title: 'issue', project: project_1) } + + let!(:merge_request) { create(:merge_request, source_project: project_2, title: 'merge_request') } + + before do + create(:todo, user: user_1, author: user_2, project: project_1, target: issue, action: 1) + create(:todo, user: user_1, author: user_1, project: project_2, target: merge_request, action: 2) + + project_1.team << [user_1, :developer] + project_2.team << [user_1, :developer] + login_as(user_1) + visit dashboard_todos_path + end + + it 'filters by project' do + click_button 'Project' + within '.dropdown-menu-project' do + fill_in 'Search projects', with: project_1.name_with_namespace + click_link project_1.name_with_namespace + end + wait_for_ajax + expect('.prepend-top-default').not_to have_content project_2.name_with_namespace + end + + it 'filters by author' do + click_button 'Author' + within '.dropdown-menu-author' do + fill_in 'Search authors', with: user_1.name + click_link user_1.name + end + wait_for_ajax + expect('.prepend-top-default').not_to have_content user_2.name + end + + it 'filters by type' do + click_button 'Type' + within '.dropdown-menu-type' do + click_link 'Issue' + end + wait_for_ajax + expect('.prepend-top-default').not_to have_content ' merge request !' + end + + it 'filters by action' do + click_button 'Action' + within '.dropdown-menu-action' do + click_link 'Assigned' + end + wait_for_ajax + expect('.prepend-top-default').not_to have_content ' mentioned ' + end +end -- cgit v1.2.1 From 892dea67717c0efbd6a28f7639f34535ec0a8747 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Mon, 1 Aug 2016 19:31:21 -0300 Subject: Project tools visibility level --- CHANGELOG | 1 + app/controllers/jwt_controller.rb | 2 +- app/controllers/projects/application_controller.rb | 2 +- app/controllers/projects/discussions_controller.rb | 2 +- app/controllers/projects/issues_controller.rb | 2 +- app/controllers/projects/labels_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/controllers/projects/milestones_controller.rb | 2 +- app/controllers/projects/snippets_controller.rb | 2 +- app/controllers/projects_controller.rb | 18 ++- app/helpers/application_helper.rb | 2 +- app/helpers/compare_helper.rb | 2 +- app/helpers/projects_helper.rb | 19 ++++ .../concerns/project_features_compatibility.rb | 37 +++++++ app/models/project.rb | 27 +++-- app/models/project_feature.rb | 63 +++++++++++ app/models/user.rb | 2 +- app/policies/project_policy.rb | 12 +- app/services/ci/register_build_service.rb | 8 +- app/services/merge_requests/get_urls_service.rb | 2 +- app/services/projects/create_service.rb | 4 +- app/services/projects/fork_service.rb | 4 +- app/views/layouts/nav/_project_settings.html.haml | 2 +- app/views/projects/edit.html.haml | 98 +++++++++-------- app/views/projects/graphs/_head.html.haml | 2 +- .../20160831214002_create_project_features.rb | 16 +++ .../20160831214543_migrate_project_features.rb | 44 ++++++++ ...223750_remove_features_enabled_from_projects.rb | 29 +++++ db/schema.rb | 20 +++- doc/user/permissions.md | 9 ++ features/steps/project/project.rb | 2 +- features/steps/shared/project.rb | 6 +- lib/api/entities.rb | 12 +- lib/api/groups.rb | 2 +- lib/api/projects.rb | 2 +- lib/gitlab/github_import/importer.rb | 2 +- lib/gitlab/github_import/project_creator.rb | 12 +- lib/gitlab/import_export/import_export.yml | 7 +- .../projects/snippets_controller_spec.rb | 2 +- spec/factories/projects.rb | 21 +++- spec/features/projects/features_visibility_spec.rb | 122 +++++++++++++++++++++ .../import_export/test_project_export.tar.gz | Bin 687442 -> 676870 bytes spec/lib/gitlab/auth_spec.rb | 3 +- spec/lib/gitlab/github_import/importer_spec.rb | 2 +- spec/lib/gitlab/import_export/project.json | 6 +- .../import_export/project_tree_restorer_spec.rb | 13 ++- .../import_export/project_tree_saver_spec.rb | 12 ++ spec/lib/gitlab/import_export/reader_spec.rb | 6 + spec/models/ability_spec.rb | 4 +- .../project_features_compatibility_spec.rb | 25 +++++ spec/models/project_feature_spec.rb | 91 +++++++++++++++ spec/models/project_spec.rb | 4 +- spec/models/user_spec.rb | 3 +- spec/requests/api/projects_spec.rb | 9 +- spec/requests/git_http_spec.rb | 3 +- spec/requests/jwt_controller_spec.rb | 13 ++- spec/services/ci/register_build_service_spec.rb | 19 ++++ .../merge_requests/get_urls_service_spec.rb | 2 +- spec/services/projects/create_service_spec.rb | 6 +- .../single_repository_worker_spec.rb | 8 +- 60 files changed, 713 insertions(+), 143 deletions(-) create mode 100644 app/models/concerns/project_features_compatibility.rb create mode 100644 app/models/project_feature.rb create mode 100644 db/migrate/20160831214002_create_project_features.rb create mode 100644 db/migrate/20160831214543_migrate_project_features.rb create mode 100644 db/migrate/20160831223750_remove_features_enabled_from_projects.rb create mode 100644 spec/features/projects/features_visibility_spec.rb create mode 100644 spec/models/concerns/project_features_compatibility_spec.rb create mode 100644 spec/models/project_feature_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 13ec1bb885f..a2b6766f98b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,6 +34,7 @@ v 8.12.0 (unreleased) - Added project specific enable/disable setting for LFS !5997 - Don't expose a user's token in the `/api/v3/user` API (!6047) - Remove redundant js-timeago-pending from user activity log (ClemMakesApps) + - Ability to manage project issues, snippets, wiki, merge requests and builds access level - Added tests for diff notes - Add a button to download latest successful artifacts for branches and tags !5142 - Remove redundant pipeline tooltips (ClemMakesApps) diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 014b9b43ff2..66ebdcc37a7 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -37,7 +37,7 @@ class JwtController < ApplicationController def authenticate_project(login, password) if login == 'gitlab-ci-token' - Project.find_by(builds_enabled: true, runners_token: password) + Project.with_builds_enabled.find_by(runners_token: password) end end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 91315a07deb..b2ff36f6538 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -88,6 +88,6 @@ class Projects::ApplicationController < ApplicationController end def builds_enabled - return render_404 unless @project.builds_enabled? + return render_404 unless @project.feature_available?(:builds, current_user) end end diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb index b2e8733ccb7..d174e1145a7 100644 --- a/app/controllers/projects/discussions_controller.rb +++ b/app/controllers/projects/discussions_controller.rb @@ -38,6 +38,6 @@ class Projects::DiscussionsController < Projects::ApplicationController end def module_enabled - render_404 unless @project.merge_requests_enabled + render_404 unless @project.feature_available?(:merge_requests, current_user) end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 7c03dcd2e64..72d2d361878 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -201,7 +201,7 @@ class Projects::IssuesController < Projects::ApplicationController end def module_enabled - return render_404 unless @project.issues_enabled && @project.default_issues_tracker? + return render_404 unless @project.feature_available?(:issues, current_user) && @project.default_issues_tracker? end def redirect_to_external_issue_tracker diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 0ca675623e5..28fa4a5b141 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -99,7 +99,7 @@ class Projects::LabelsController < Projects::ApplicationController protected def module_enabled - unless @project.issues_enabled || @project.merge_requests_enabled + unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user) return render_404 end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 4f5f3b6aa09..4f9ca0097a1 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -413,7 +413,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def module_enabled - return render_404 unless @project.merge_requests_enabled + return render_404 unless @project.feature_available?(:merge_requests, current_user) end def validates_merge_request diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index da2892bfb3f..ff63f22cb5b 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -106,7 +106,7 @@ class Projects::MilestonesController < Projects::ApplicationController end def module_enabled - unless @project.issues_enabled || @project.merge_requests_enabled + unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user) return render_404 end end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 6d0a7ee1031..17ceefec3b8 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -94,7 +94,7 @@ class Projects::SnippetsController < Projects::ApplicationController end def module_enabled - return render_404 unless @project.snippets_enabled + return render_404 unless @project.feature_available?(:snippets, current_user) end def snippet_params diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 84d6b106cd7..eaa38fa6c98 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -303,13 +303,23 @@ class ProjectsController < Projects::ApplicationController end def project_params + project_feature_attributes = + { + project_feature_attributes: + [ + :issues_access_level, :builds_access_level, + :wiki_access_level, :merge_requests_access_level, :snippets_access_level + ] + } + params.require(:project).permit( :name, :path, :description, :issues_tracker, :tag_list, :runners_token, - :issues_enabled, :merge_requests_enabled, :snippets_enabled, :container_registry_enabled, + :container_registry_enabled, :issues_tracker_id, :default_branch, - :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, - :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled, :lfs_enabled + :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, + :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, + :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled, + :lfs_enabled, project_feature_attributes ) end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f3733b01721..5f3765cad0d 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -110,7 +110,7 @@ module ApplicationHelper project = event.project # Skip if project repo is empty or MR disabled - return false unless project && !project.empty_repo? && project.merge_requests_enabled + return false unless project && !project.empty_repo? && project.feature_available?(:merge_requests, current_user) # Skip if user already created appropriate MR return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb index f1dc906cab4..aa54ee07bdc 100644 --- a/app/helpers/compare_helper.rb +++ b/app/helpers/compare_helper.rb @@ -3,7 +3,7 @@ module CompareHelper from.present? && to.present? && from != to && - project.merge_requests_enabled && + project.feature_available?(:merge_requests, current_user) && project.repository.branch_names.include?(from) && project.repository.branch_names.include?(to) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index f07077bd133..79a1eba9714 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -412,4 +412,23 @@ module ProjectsHelper message.strip.gsub(project.repository_storage_path.chomp('/'), "[REPOS PATH]") end + + def project_feature_options + { + 'Disabled' => ProjectFeature::DISABLED, + 'Only team members' => ProjectFeature::PRIVATE, + 'Everyone with access' => ProjectFeature::ENABLED + } + end + + def project_feature_access_select(field) + # Don't show option "everyone with access" if project is private + options = project_feature_options + level = @project.project_feature.public_send(field) + + options.delete('Everyone with access') if @project.private? && level != ProjectFeature::ENABLED + + options = options_for_select(options, selected: @project.project_feature.public_send(field) || ProjectFeature::ENABLED) + content_tag(:select, options, name: "project[project_feature_attributes][#{field.to_s}]", id: "project_project_feature_attributes_#{field.to_s}", class: "pull-right form-control").html_safe + end end diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb new file mode 100644 index 00000000000..9216122923e --- /dev/null +++ b/app/models/concerns/project_features_compatibility.rb @@ -0,0 +1,37 @@ +# Makes api V3 compatible with old project features permissions methods +# +# After migrating issues_enabled merge_requests_enabled builds_enabled snippets_enabled and wiki_enabled +# fields to a new table "project_features", support for the old fields is still needed in the API. + +module ProjectFeaturesCompatibility + extend ActiveSupport::Concern + + def wiki_enabled=(value) + write_feature_attribute(:wiki_access_level, value) + end + + def builds_enabled=(value) + write_feature_attribute(:builds_access_level, value) + end + + def merge_requests_enabled=(value) + write_feature_attribute(:merge_requests_access_level, value) + end + + def issues_enabled=(value) + write_feature_attribute(:issues_access_level, value) + end + + def snippets_enabled=(value) + write_feature_attribute(:snippets_access_level, value) + end + + private + + def write_feature_attribute(field, value) + build_project_feature unless project_feature + + access_level = value == "true" ? ProjectFeature::ENABLED : ProjectFeature::DISABLED + project_feature.update_attribute(field, access_level) + end +end diff --git a/app/models/project.rb b/app/models/project.rb index e5027af4a0e..a6de2c48071 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -11,24 +11,23 @@ class Project < ActiveRecord::Base include AfterCommitQueue include CaseSensitivity include TokenAuthenticatable + include ProjectFeaturesCompatibility extend Gitlab::ConfigHelper UNKNOWN_IMPORT_URL = 'http://unknown.git' + delegate :feature_available?, :builds_enabled?, :wiki_enabled?, :merge_requests_enabled?, to: :project_feature, allow_nil: true + default_value_for :archived, false default_value_for :visibility_level, gitlab_config_features.visibility_level - default_value_for :issues_enabled, gitlab_config_features.issues - default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests - default_value_for :builds_enabled, gitlab_config_features.builds - default_value_for :wiki_enabled, gitlab_config_features.wiki - default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :container_registry_enabled, gitlab_config_features.container_registry default_value_for(:repository_storage) { current_application_settings.repository_storage } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } after_create :ensure_dir_exist after_save :ensure_dir_exist, if: :namespace_id_changed? + after_initialize :setup_project_feature # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -62,10 +61,10 @@ class Project < ActiveRecord::Base belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id' belongs_to :namespace - has_one :board, dependent: :destroy - has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id' + has_one :board, dependent: :destroy + # Project services has_many :services has_one :campfire_service, dependent: :destroy @@ -130,6 +129,7 @@ class Project < ActiveRecord::Base has_many :notification_settings, dependent: :destroy, as: :source has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" + has_one :project_feature, dependent: :destroy has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', foreign_key: :gl_project_id @@ -142,6 +142,7 @@ class Project < ActiveRecord::Base has_many :deployments, dependent: :destroy accepts_nested_attributes_for :variables, allow_destroy: true + accepts_nested_attributes_for :project_feature delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true @@ -159,8 +160,6 @@ class Project < ActiveRecord::Base length: { within: 0..255 }, format: { with: Gitlab::Regex.project_path_regex, message: Gitlab::Regex.project_path_regex_message } - validates :issues_enabled, :merge_requests_enabled, - :wiki_enabled, inclusion: { in: [true, false] } validates :namespace, presence: true validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id @@ -196,6 +195,9 @@ class Project < ActiveRecord::Base scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) } + scope :with_builds_enabled, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id').where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0') } + scope :with_issues_enabled, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id').where('project_features.issues_access_level IS NULL or project_features.issues_access_level > 0') } + scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') } scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) } @@ -1121,7 +1123,7 @@ class Project < ActiveRecord::Base end def enable_ci - self.builds_enabled = true + project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED) end def any_runners?(&block) @@ -1288,6 +1290,11 @@ class Project < ActiveRecord::Base private + # Prevents the creation of project_feature record for every project + def setup_project_feature + build_project_feature unless project_feature + end + def default_branch_protected? current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL || current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb new file mode 100644 index 00000000000..9c602c582bd --- /dev/null +++ b/app/models/project_feature.rb @@ -0,0 +1,63 @@ +class ProjectFeature < ActiveRecord::Base + # == Project features permissions + # + # Grants access level to project tools + # + # Tools can be enabled only for users, everyone or disabled + # Access control is made only for non private projects + # + # levels: + # + # Disabled: not enabled for anyone + # Private: enabled only for team members + # Enabled: enabled for everyone able to access the project + # + + # Permision levels + DISABLED = 0 + PRIVATE = 10 + ENABLED = 20 + + FEATURES = %i(issues merge_requests wiki snippets builds) + + belongs_to :project + + def feature_available?(feature, user) + raise ArgumentError, 'invalid project feature' unless FEATURES.include?(feature) + + get_permission(user, public_send("#{feature}_access_level")) + end + + def builds_enabled? + return true unless builds_access_level + + builds_access_level > DISABLED + end + + def wiki_enabled? + return true unless wiki_access_level + + wiki_access_level > DISABLED + end + + def merge_requests_enabled? + return true unless merge_requests_access_level + + merge_requests_access_level > DISABLED + end + + private + + def get_permission(user, level) + case level + when DISABLED + false + when PRIVATE + user && (project.team.member?(user) || user.admin?) + when ENABLED + true + else + true + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 8f5958333d7..6996740eebd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -433,7 +433,7 @@ class User < ActiveRecord::Base # # This logic is duplicated from `Ability#project_abilities` into a SQL form. def projects_where_can_admin_issues - authorized_projects(Gitlab::Access::REPORTER).non_archived.where.not(issues_enabled: false) + authorized_projects(Gitlab::Access::REPORTER).non_archived.with_issues_enabled end def is_admin? diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 15a9f2f0dca..acf36d422d1 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -145,28 +145,28 @@ class ProjectPolicy < BasePolicy end def disabled_features! - unless project.issues_enabled + unless project.feature_available?(:issues, user) cannot!(*named_abilities(:issue)) end - unless project.merge_requests_enabled + unless project.feature_available?(:merge_requests, user) cannot!(*named_abilities(:merge_request)) end - unless project.issues_enabled || project.merge_requests_enabled + unless project.feature_available?(:issues, user) || project.feature_available?(:merge_requests, user) cannot!(*named_abilities(:label)) cannot!(*named_abilities(:milestone)) end - unless project.snippets_enabled + unless project.feature_available?(:snippets, user) cannot!(*named_abilities(:project_snippet)) end - unless project.has_wiki? + unless project.feature_available?(:wiki, user) || project.has_external_wiki? cannot!(*named_abilities(:wiki)) end - unless project.builds_enabled + unless project.feature_available?(:builds, user) cannot!(*named_abilities(:build)) cannot!(*named_abilities(:pipeline)) cannot!(*named_abilities(:environment)) diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 9a187f5d694..6973191b203 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,16 +8,18 @@ module Ci builds = if current_runner.shared? builds. - # don't run projects which have not enabled shared runners - joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }). + # don't run projects which have not enabled shared runners and builds + joins(:project).where(projects: { shared_runners_enabled: true }). + joins('LEFT JOIN project_features ON ci_builds.gl_project_id = project_features.project_id'). # this returns builds that are ordered by number of running builds # we prefer projects that don't use shared runners at all joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id"). + where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0'). order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC') else # do run projects which are only assigned to this runner (FIFO) - builds.where(project: current_runner.projects.where(builds_enabled: true)).order('created_at ASC') + builds.where(project: current_runner.projects.with_builds_enabled).order('created_at ASC') end build = builds.find do |build| diff --git a/app/services/merge_requests/get_urls_service.rb b/app/services/merge_requests/get_urls_service.rb index 08c1f72d65a..1262ecbc29a 100644 --- a/app/services/merge_requests/get_urls_service.rb +++ b/app/services/merge_requests/get_urls_service.rb @@ -31,7 +31,7 @@ module MergeRequests def get_branches(changes) return [] if project.empty_repo? - return [] unless project.merge_requests_enabled + return [] unless project.merge_requests_enabled? changes_list = Gitlab::ChangesList.new(changes) changes_list.map do |change| diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 55956be2844..be749ba4a1c 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -7,7 +7,6 @@ module Projects def execute forked_from_project_id = params.delete(:forked_from_project_id) import_data = params.delete(:import_data) - @project = Project.new(params) # Make sure that the user is allowed to use the specified visibility level @@ -81,8 +80,7 @@ module Projects log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") unless @project.gitlab_project_import? - @project.create_wiki if @project.wiki_enabled? - + @project.create_wiki if @project.feature_available?(:wiki, current_user) @project.build_missing_services @project.create_labels diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index de6dc38cc8e..a2de4dccece 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -8,7 +8,6 @@ module Projects name: @project.name, path: @project.path, shared_runners_enabled: @project.shared_runners_enabled, - builds_enabled: @project.builds_enabled, namespace_id: @params[:namespace].try(:id) || current_user.namespace.id } @@ -17,6 +16,9 @@ module Projects end new_project = CreateService.new(current_user, new_params).execute + builds_access_level = @project.project_feature.builds_access_level + new_project.project_feature.update_attributes(builds_access_level: builds_access_level) + new_project end diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 52a5bdc1a1b..613b8b7d301 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -26,7 +26,7 @@ %span Protected Branches - - if @project.builds_enabled? + - if @project.feature_available?(:builds, current_user) = nav_link(controller: :runners) do = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do %span diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 836c6d7b83f..f6d751a343e 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -44,52 +44,56 @@ %hr %fieldset.features.append-bottom-0 %h5.prepend-top-0 - Features - .form-group - .checkbox - = f.label :issues_enabled do - = f.check_box :issues_enabled - %strong Issues - %br - %span.descr Lightweight issue tracking system for this project - .form-group - .checkbox - = f.label :merge_requests_enabled do - = f.check_box :merge_requests_enabled - %strong Merge Requests - %br - %span.descr Submit changes to be merged upstream - .form-group - .checkbox - = f.label :builds_enabled do - = f.check_box :builds_enabled - %strong Builds - %br - %span.descr Test and deploy your changes before merge - .form-group - .checkbox - = f.label :wiki_enabled do - = f.check_box :wiki_enabled - %strong Wiki - %br - %span.descr Pages for project documentation - .form-group - .checkbox - = f.label :snippets_enabled do - = f.check_box :snippets_enabled - %strong Snippets - %br - %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') + Feature Visibility + + = f.fields_for :project_feature do |feature_fields| + .form_group.prepend-top-20 + .row + .col-md-9 + = feature_fields.label :issues_access_level, "Issues", class: 'label-light' + %span.help-block Lightweight issue tracking system for this project + .col-md-3 + = project_feature_access_select(:issues_access_level) + + .row + .col-md-9 + = feature_fields.label :merge_requests_access_level, "Merge requests", class: 'label-light' + %span.help-block Submit changes to be merged upstream + .col-md-3 + = project_feature_access_select(:merge_requests_access_level) + + .row + .col-md-9 + = feature_fields.label :builds_access_level, "Builds", class: 'label-light' + %span.help-block Submit Test and deploy your changes before merge + .col-md-3 + = project_feature_access_select(:builds_access_level) + + .row + .col-md-9 + = feature_fields.label :wiki_access_level, "Wiki", class: 'label-light' + %span.help-block Pages for project documentation + .col-md-3 + = project_feature_access_select(:wiki_access_level) + + .row + .col-md-9 + = feature_fields.label :snippets_access_level, "Snippets", class: 'label-light' + %span.help-block Share code pastes with others out of Git repository + .col-md-3 + = project_feature_access_select(:snippets_access_level) + + - 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 .form-group .checkbox @@ -98,7 +102,7 @@ %strong Container Registry %br %span.descr Enable Container Registry for this repository - %hr + = render 'merge_request_settings', f: f %hr %fieldset.features.append-bottom-default diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index a231d684559..082e2cb4d8c 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -12,7 +12,7 @@ = link_to 'Commits', commits_namespace_project_graph_path = nav_link(action: :languages) do = link_to 'Languages', languages_namespace_project_graph_path - - if @project.builds_enabled? + - if @project.feature_available?(:builds, current_user) = nav_link(action: :ci) do = link_to ci_namespace_project_graph_path do Continuous Integration diff --git a/db/migrate/20160831214002_create_project_features.rb b/db/migrate/20160831214002_create_project_features.rb new file mode 100644 index 00000000000..2d76a015a08 --- /dev/null +++ b/db/migrate/20160831214002_create_project_features.rb @@ -0,0 +1,16 @@ +class CreateProjectFeatures < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :project_features do |t| + t.belongs_to :project, index: true + t.integer :merge_requests_access_level + t.integer :issues_access_level + t.integer :wiki_access_level + t.integer :snippets_access_level + t.integer :builds_access_level + + t.timestamps + end + end +end diff --git a/db/migrate/20160831214543_migrate_project_features.rb b/db/migrate/20160831214543_migrate_project_features.rb new file mode 100644 index 00000000000..93f9821bc76 --- /dev/null +++ b/db/migrate/20160831214543_migrate_project_features.rb @@ -0,0 +1,44 @@ +class MigrateProjectFeatures < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = true + DOWNTIME_REASON = + <<-EOT + Migrating issues_enabled, merge_requests_enabled, wiki_enabled, builds_enabled, snippets_enabled fields from projects to + a new table called project_features. + EOT + + def up + sql = + %Q{ + INSERT INTO project_features(project_id, issues_access_level, merge_requests_access_level, wiki_access_level, + builds_access_level, snippets_access_level, created_at, updated_at) + SELECT + id AS project_id, + CASE WHEN issues_enabled IS true THEN 20 ELSE 0 END AS issues_access_level, + CASE WHEN merge_requests_enabled IS true THEN 20 ELSE 0 END AS merge_requests_access_level, + CASE WHEN wiki_enabled IS true THEN 20 ELSE 0 END AS wiki_access_level, + CASE WHEN builds_enabled IS true THEN 20 ELSE 0 END AS builds_access_level, + CASE WHEN snippets_enabled IS true THEN 20 ELSE 0 END AS snippets_access_level, + created_at, + updated_at + FROM projects + } + + execute(sql) + end + + def down + sql = %Q{ + UPDATE projects + SET + issues_enabled = COALESCE((SELECT CASE WHEN issues_access_level = 20 THEN true ELSE false END AS issues_enabled FROM project_features WHERE project_features.project_id = projects.id), true), + merge_requests_enabled = COALESCE((SELECT CASE WHEN merge_requests_access_level = 20 THEN true ELSE false END AS merge_requests_enabled FROM project_features WHERE project_features.project_id = projects.id),true), + wiki_enabled = COALESCE((SELECT CASE WHEN wiki_access_level = 20 THEN true ELSE false END AS wiki_enabled FROM project_features WHERE project_features.project_id = projects.id), true), + builds_enabled = COALESCE((SELECT CASE WHEN builds_access_level = 20 THEN true ELSE false END AS builds_enabled FROM project_features WHERE project_features.project_id = projects.id), true), + snippets_enabled = COALESCE((SELECT CASE WHEN snippets_access_level = 20 THEN true ELSE false END AS snippets_enabled FROM project_features WHERE project_features.project_id = projects.id),true) + } + + execute(sql) + end +end diff --git a/db/migrate/20160831223750_remove_features_enabled_from_projects.rb b/db/migrate/20160831223750_remove_features_enabled_from_projects.rb new file mode 100644 index 00000000000..a2c207b49ea --- /dev/null +++ b/db/migrate/20160831223750_remove_features_enabled_from_projects.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveFeaturesEnabledFromProjects < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + # Set this constant to true if this migration requires downtime. + DOWNTIME = true + DOWNTIME_REASON = "Removing fields from database requires downtine." + + def up + remove_column :projects, :issues_enabled + remove_column :projects, :merge_requests_enabled + remove_column :projects, :builds_enabled + remove_column :projects, :wiki_enabled + remove_column :projects, :snippets_enabled + end + + # Ugly SQL but the only way i found to make it work on both Postgres and Mysql + # It will be slow but it is ok since it is a revert method + def down + add_column_with_default(:projects, :issues_enabled, :boolean, default: true, allow_null: false) + add_column_with_default(:projects, :merge_requests_enabled, :boolean, default: true, allow_null: false) + add_column_with_default(:projects, :builds_enabled, :boolean, default: true, allow_null: false) + add_column_with_default(:projects, :wiki_enabled, :boolean, default: true, allow_null: false) + add_column_with_default(:projects, :snippets_enabled, :boolean, default: true, allow_null: false) + end +end diff --git a/db/schema.rb b/db/schema.rb index 963d528d170..af6e74a4e25 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160830232601) do +ActiveRecord::Schema.define(version: 20160831223750) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -766,6 +766,19 @@ ActiveRecord::Schema.define(version: 20160830232601) do add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree add_index "personal_access_tokens", ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree + create_table "project_features", force: :cascade do |t| + t.integer "project_id" + t.integer "merge_requests_access_level" + t.integer "issues_access_level" + t.integer "wiki_access_level" + t.integer "snippets_access_level" + t.integer "builds_access_level" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "project_features", ["project_id"], name: "index_project_features_on_project_id", using: :btree + create_table "project_group_links", force: :cascade do |t| t.integer "project_id", null: false t.integer "group_id", null: false @@ -790,11 +803,7 @@ ActiveRecord::Schema.define(version: 20160830232601) do t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" t.string "import_url" t.integer "visibility_level", default: 0, null: false @@ -808,7 +817,6 @@ ActiveRecord::Schema.define(version: 20160830232601) do t.integer "commit_count", default: 0 t.text "import_error" t.integer "ci_id" - t.boolean "builds_enabled", default: true, null: false t.boolean "shared_runners_enabled", default: true, null: false t.string "runners_token" t.string "build_coverage_regex" diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 66542861781..1498cb361c8 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -104,6 +104,15 @@ will find the option to flag the user as external. By default new users are not set as external users. This behavior can be changed by an administrator under **Admin > Application Settings**. +## Project features + +Project features like wiki and issues can be hidden from users depending on +which visibility level you select on project settings. + +- Disabled: disabled for everyone +- Only team members: only team members will see even if your project is public or internal +- Everyone with access: everyone can see depending on your project visibility level + ## GitLab CI GitLab CI permissions rely on the role the user has in GitLab. There are four diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 76fefee9254..975c879149e 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -5,7 +5,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps step 'change project settings' do fill_in 'project_name_edit', with: 'NewName' - uncheck 'project_issues_enabled' + select 'Disabled', from: 'project_project_feature_attributes_issues_access_level' end step 'I save project' do diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 0b4920883b8..afbd8ef1233 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -15,7 +15,7 @@ module SharedProject # Create a specific project called "Shop" step 'I own project "Shop"' do @project = Project.find_by(name: "Shop") - @project ||= create(:project, name: "Shop", namespace: @user.namespace, snippets_enabled: true) + @project ||= create(:project, name: "Shop", namespace: @user.namespace) @project.team << [@user, :master] end @@ -41,6 +41,8 @@ module SharedProject step 'I own project "Forum"' do @project = Project.find_by(name: "Forum") @project ||= create(:project, name: "Forum", namespace: @user.namespace, path: 'forum_project') + @project.build_project_feature + @project.project_feature.save @project.team << [@user, :master] end @@ -95,7 +97,7 @@ module SharedProject step 'I should see project settings' do expect(current_path).to eq edit_namespace_project_path(@project.namespace, @project) expect(page).to have_content("Project name") - expect(page).to have_content("Features") + expect(page).to have_content("Feature Visibility") end def current_project diff --git a/lib/api/entities.rb b/lib/api/entities.rb index fe7468dd681..e7fe437ee0d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -76,7 +76,15 @@ module API expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :name, :name_with_namespace expose :path, :path_with_namespace - expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :container_registry_enabled + expose :container_registry_enabled + + # Expose old field names with the new permissions methods to keep API compatible + expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:user]) } + expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:user]) } + expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:user]) } + expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:user]) } + expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:user]) } + expose :created_at, :last_activity_at expose :shared_runners_enabled, :lfs_enabled expose :creator_id @@ -84,7 +92,7 @@ module API expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? } expose :avatar_url expose :star_count, :forks_count - expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? } + expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:user]) && project.default_issues_tracker? } expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :public_builds expose :shared_with_groups do |project, options| diff --git a/lib/api/groups.rb b/lib/api/groups.rb index f981ec0dbfe..d2df77238d5 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -97,7 +97,7 @@ module API group = find_group(params[:id]) projects = GroupProjectsFinder.new(group).execute(current_user) projects = paginate projects - present projects, with: Entities::Project + present projects, with: Entities::Project, user: current_user end # Transfer a project to the Group namespace diff --git a/lib/api/projects.rb b/lib/api/projects.rb index f8979a1cc29..a1fd598414a 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -51,7 +51,7 @@ module API @projects = current_user.viewable_starred_projects @projects = filter_projects(@projects) @projects = paginate @projects - present @projects, with: Entities::Project + present @projects, with: Entities::Project, user: current_user end # Get all projects for admin user diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 1b2a5eb8f52..4fdc2f46be0 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -168,7 +168,7 @@ module Gitlab unless project.wiki_enabled? wiki = WikiFormatter.new(project) gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) - project.update_attribute(:wiki_enabled, true) + project.project.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) end rescue Gitlab::Shell::Error => e # GitHub error message when the wiki repo has not been created, diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb index f4221003db5..0ad30c1e5b2 100644 --- a/lib/gitlab/github_import/project_creator.rb +++ b/lib/gitlab/github_import/project_creator.rb @@ -11,7 +11,7 @@ module Gitlab end def execute - ::Projects::CreateService.new( + project = ::Projects::CreateService.new( current_user, name: repo.name, path: repo.name, @@ -20,9 +20,15 @@ module Gitlab visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC, import_type: "github", import_source: repo.full_name, - import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@"), - wiki_enabled: !repo.has_wiki? # If repo has wiki we'll import it later + import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@") ).execute + + # If repo has wiki we'll import it later + if repo.has_wiki? && project + project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) + end + + project end end end diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index 1da51043611..c2e8a1ca5dd 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -39,15 +39,12 @@ project_tree: - :labels - milestones: - :events + - :project_feature # Only include the following attributes for the models specified. included_attributes: project: - :description - - :issues_enabled - - :merge_requests_enabled - - :wiki_enabled - - :snippets_enabled - :visibility_level - :archived user: @@ -72,4 +69,4 @@ methods: statuses: - :type merge_request_diff: - - :utf8_st_diffs \ No newline at end of file + - :utf8_st_diffs diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb index b8a28f43707..72a3ebf2ebd 100644 --- a/spec/controllers/projects/snippets_controller_spec.rb +++ b/spec/controllers/projects/snippets_controller_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Projects::SnippetsController do - let(:project) { create(:project_empty_repo, :public, snippets_enabled: true) } + let(:project) { create(:project_empty_repo, :public) } let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index f82d68a1816..fb84ba07d25 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -8,7 +8,6 @@ FactoryGirl.define do path { name.downcase.gsub(/\s/, '_') } namespace creator - snippets_enabled true trait :public do visibility_level Gitlab::VisibilityLevel::PUBLIC @@ -27,6 +26,26 @@ FactoryGirl.define do project.create_repository end end + + # Nest Project Feature attributes + transient do + wiki_access_level ProjectFeature::ENABLED + builds_access_level ProjectFeature::ENABLED + snippets_access_level ProjectFeature::ENABLED + issues_access_level ProjectFeature::ENABLED + merge_requests_access_level ProjectFeature::ENABLED + end + + after(:create) do |project, evaluator| + project.project_feature. + update_attributes( + wiki_access_level: evaluator.wiki_access_level, + builds_access_level: evaluator.builds_access_level, + snippets_access_level: evaluator.snippets_access_level, + issues_access_level: evaluator.issues_access_level, + merge_requests_access_level: evaluator.merge_requests_access_level, + ) + end end # Project with empty repository diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb new file mode 100644 index 00000000000..9b487e350f2 --- /dev/null +++ b/spec/features/projects/features_visibility_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' +include WaitForAjax + +describe 'Edit Project Settings', feature: true do + let(:member) { create(:user) } + let!(:project) { create(:project, :public, path: 'gitlab', name: 'sample') } + let(:non_member) { create(:user) } + + describe 'project features visibility selectors', js: true do + before do + project.team << [member, :master] + login_as(member) + end + + tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" } + + tools.each do |tool_name, shortcut_name| + describe "feature #{tool_name}" do + it 'toggles visibility' do + visit edit_namespace_project_path(project.namespace, project) + + select 'Disabled', from: "project_project_feature_attributes_#{tool_name}_access_level" + click_button 'Save changes' + wait_for_ajax + expect(page).not_to have_selector(".shortcuts-#{shortcut_name}") + + select 'Everyone with access', from: "project_project_feature_attributes_#{tool_name}_access_level" + click_button 'Save changes' + wait_for_ajax + expect(page).to have_selector(".shortcuts-#{shortcut_name}") + + select 'Only team members', from: "project_project_feature_attributes_#{tool_name}_access_level" + click_button 'Save changes' + wait_for_ajax + expect(page).to have_selector(".shortcuts-#{shortcut_name}") + + sleep 0.1 + end + end + end + end + + describe 'project features visibility pages' do + before do + @tools = + { + builds: namespace_project_pipelines_path(project.namespace, project), + issues: namespace_project_issues_path(project.namespace, project), + wiki: namespace_project_wiki_path(project.namespace, project, :home), + snippets: namespace_project_snippets_path(project.namespace, project), + merge_requests: namespace_project_merge_requests_path(project.namespace, project), + } + end + + context 'normal user' do + it 'renders 200 if tool is enabled' do + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::ENABLED) + visit url + expect(page.status_code).to eq(200) + end + end + + it 'renders 404 if feature is disabled' do + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED) + visit url + expect(page.status_code).to eq(404) + end + end + + it 'renders 404 if feature is enabled only for team members' do + project.team.truncate + + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE) + visit url + expect(page.status_code).to eq(404) + end + end + + it 'renders 200 if users is member of group' do + group = create(:group) + project.group = group + project.save + + group.add_owner(member) + + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE) + visit url + expect(page.status_code).to eq(200) + end + end + end + + context 'admin user' do + before do + non_member.update_attribute(:admin, true) + login_as(non_member) + end + + it 'renders 404 if feature is disabled' do + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED) + visit url + expect(page.status_code).to eq(404) + end + end + + it 'renders 200 if feature is enabled only for team members' do + project.team.truncate + + @tools.each do |method_name, url| + project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE) + visit url + expect(page.status_code).to eq(200) + end + end + end + end +end diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz index 7bb0d26b21c..e14b2705704 100644 Binary files a/spec/features/projects/import_export/test_project_export.tar.gz and b/spec/features/projects/import_export/test_project_export.tar.gz differ diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index b0772cad312..7c23e02d05a 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -7,7 +7,8 @@ describe Gitlab::Auth, lib: true do it 'recognizes CI' do token = '123' project = create(:empty_project) - project.update_attributes(runners_token: token, builds_enabled: true) + project.update_attributes(runners_token: token) + ip = 'ip' expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: 'gitlab-ci-token') diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index b7c3bc4e1a7..3fb8de81545 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Gitlab::GithubImport::Importer, lib: true do describe '#execute' do context 'when an error occurs' do - let(:project) { create(:project, import_url: 'https://github.com/octocat/Hello-World.git', wiki_enabled: false) } + let(:project) { create(:project, import_url: 'https://github.com/octocat/Hello-World.git', wiki_access_level: ProjectFeature::DISABLED) } let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json index cbbf98dca94..5114f9c55e1 100644 --- a/spec/lib/gitlab/import_export/project.json +++ b/spec/lib/gitlab/import_export/project.json @@ -1,9 +1,5 @@ { "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", - "issues_enabled": true, - "merge_requests_enabled": true, - "wiki_enabled": true, - "snippets_enabled": false, "visibility_level": 10, "archived": false, "issues": [ @@ -7307,4 +7303,4 @@ "protected_branches": [ ] -} \ No newline at end of file +} diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 4d857945fde..a07ef279e68 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -5,7 +5,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do let(:user) { create(:user) } let(:namespace) { create(:namespace, owner: user) } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } - let(:project) { create(:empty_project, name: 'project', path: 'project') } + let!(:project) { create(:empty_project, name: 'project', path: 'project', builds_access_level: ProjectFeature::DISABLED, issues_access_level: ProjectFeature::DISABLED) } let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) } let(:restored_project_json) { project_tree_restorer.restore } @@ -18,6 +18,17 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do expect(restored_project_json).to be true end + it 'restore correct project features' do + restored_project_json + project = Project.find_by_path('project') + + expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED) + expect(project.project_feature.builds_access_level).to eq(ProjectFeature::DISABLED) + expect(project.project_feature.snippets_access_level).to eq(ProjectFeature::ENABLED) + expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::ENABLED) + expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED) + end + it 'creates a valid pipeline note' do restored_project_json diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb index 3a86a4ce07c..d891c2d0cc6 100644 --- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb @@ -111,6 +111,14 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do expect(saved_project_json['issues'].first['label_links'].first['label']).not_to be_empty end + it 'has project feature' do + project_feature = saved_project_json['project_feature'] + expect(project_feature).not_to be_empty + expect(project_feature["issues_access_level"]).to eq(ProjectFeature::DISABLED) + expect(project_feature["wiki_access_level"]).to eq(ProjectFeature::ENABLED) + expect(project_feature["builds_access_level"]).to eq(ProjectFeature::PRIVATE) + end + it 'does not complain about non UTF-8 characters in MR diffs' do ActiveRecord::Base.connection.execute("UPDATE merge_request_diffs SET st_diffs = '---\n- :diff: !binary |-\n LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n YXR'") @@ -154,6 +162,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do create(:event, target: milestone, project: project, action: Event::CREATED, author: user) + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) + project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::PRIVATE) + project end diff --git a/spec/lib/gitlab/import_export/reader_spec.rb b/spec/lib/gitlab/import_export/reader_spec.rb index b6dec41d218..3ceb1e7e803 100644 --- a/spec/lib/gitlab/import_export/reader_spec.rb +++ b/spec/lib/gitlab/import_export/reader_spec.rb @@ -32,6 +32,12 @@ describe Gitlab::ImportExport::Reader, lib: true do expect(described_class.new(shared: shared).project_tree).to match(include: [:issues]) end + it 'generates the correct hash for a single project feature relation' do + setup_yaml(project_tree: [:project_feature]) + + expect(described_class.new(shared: shared).project_tree).to match(include: [:project_feature]) + end + it 'generates the correct hash for a multiple project relation' do setup_yaml(project_tree: [:issues, :snippets]) diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb index b05510342bc..1bdf005c823 100644 --- a/spec/models/ability_spec.rb +++ b/spec/models/ability_spec.rb @@ -220,13 +220,13 @@ describe Ability, lib: true do end describe '.project_disabled_features_rules' do - let(:project) { build(:project) } + let(:project) { create(:project, wiki_access_level: ProjectFeature::DISABLED) } subject { described_class.allowed(project.owner, project) } context 'wiki named abilities' do it 'disables wiki abilities if the project has no wiki' do - expect(project).to receive(:has_wiki?).and_return(false) + expect(project).to receive(:has_external_wiki?).and_return(false) expect(subject).not_to include(:read_wiki, :create_wiki, :update_wiki, :admin_wiki) end end diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb new file mode 100644 index 00000000000..5363aea4d22 --- /dev/null +++ b/spec/models/concerns/project_features_compatibility_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe ProjectFeaturesCompatibility do + let(:project) { create(:project) } + let(:features) { %w(issues wiki builds merge_requests snippets) } + + # We had issues_enabled, snippets_enabled, builds_enabled, merge_requests_enabled and issues_enabled fields on projects table + # All those fields got moved to a new table called project_feature and are now integers instead of booleans + # This spec tests if the described concern makes sure parameters received by the API are correctly parsed to the new table + # So we can keep it compatible + + it "converts fields from 'true' to ProjectFeature::ENABLED" do + features.each do |feature| + project.update_attribute("#{feature}_enabled".to_sym, "true") + expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::ENABLED) + end + end + + it "converts fields from 'false' to ProjectFeature::DISABLED" do + features.each do |feature| + project.update_attribute("#{feature}_enabled".to_sym, "false") + expect(project.project_feature.public_send("#{feature}_access_level")).to eq(ProjectFeature::DISABLED) + end + end +end diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb new file mode 100644 index 00000000000..8d554a01be5 --- /dev/null +++ b/spec/models/project_feature_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' + +describe ProjectFeature do + let(:project) { create(:project) } + let(:user) { create(:user) } + + describe '#feature_available?' do + let(:features) { %w(issues wiki builds merge_requests snippets) } + + context 'when features are disabled' do + it "returns false" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::DISABLED) + expect(project.feature_available?(:issues, user)).to eq(false) + end + end + end + + context 'when features are enabled only for team members' do + it "returns false when user is not a team member" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE) + expect(project.feature_available?(:issues, user)).to eq(false) + end + end + + it "returns true when user is a team member" do + project.team << [user, :developer] + + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE) + expect(project.feature_available?(:issues, user)).to eq(true) + end + end + + it "returns true when user is a member of project group" do + group = create(:group) + project = create(:project, namespace: group) + group.add_developer(user) + + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE) + expect(project.feature_available?(:issues, user)).to eq(true) + end + end + + it "returns true if user is an admin" do + user.update_attribute(:admin, true) + + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE) + expect(project.feature_available?(:issues, user)).to eq(true) + end + end + end + + context 'when feature is enabled for everyone' do + it "returns true" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::ENABLED) + expect(project.feature_available?(:issues, user)).to eq(true) + end + end + end + end + + describe '#*_enabled?' do + let(:features) { %w(wiki builds merge_requests) } + + it "returns false when feature is disabled" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::DISABLED) + expect(project.public_send("#{feature}_enabled?")).to eq(false) + end + end + + it "returns true when feature is enabled only for team members" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::PRIVATE) + expect(project.public_send("#{feature}_enabled?")).to eq(true) + end + end + + it "returns true when feature is enabled for everyone" do + features.each do |feature| + project.project_feature.update_attribute("#{feature}_access_level".to_sym, ProjectFeature::ENABLED) + expect(project.public_send("#{feature}_enabled?")).to eq(true) + end + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index dd309ea1b68..4a41fafb84d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -508,7 +508,7 @@ describe Project, models: true do describe '#has_wiki?' do let(:no_wiki_project) { build(:project, wiki_enabled: false, has_external_wiki: false) } - let(:wiki_enabled_project) { build(:project, wiki_enabled: true) } + let(:wiki_enabled_project) { build(:project) } let(:external_wiki_project) { build(:project, has_external_wiki: true) } it 'returns true if project is wiki enabled or has external wiki' do @@ -734,8 +734,6 @@ describe Project, models: true do describe '#builds_enabled' do let(:project) { create :project } - before { project.builds_enabled = true } - subject { project.builds_enabled } it { expect(project.builds_enabled?).to be_truthy } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8eb0c5033c9..a1770d96f83 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1006,8 +1006,7 @@ describe User, models: true do end it 'does not include projects for which issues are disabled' do - project = create(:project) - project.update_attributes(issues_enabled: false) + project = create(:project, issues_access_level: ProjectFeature::DISABLED) expect(user.projects_where_can_admin_issues.to_a).to be_empty expect(user.can?(:admin_issue, project)).to eq(false) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 63f2467be63..28aa56e8644 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -73,7 +73,7 @@ describe API::API, api: true do end it 'does not include open_issues_count' do - project.update_attributes( { issues_enabled: false } ) + project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) get api('/projects', user) expect(response.status).to eq 200 @@ -231,8 +231,15 @@ describe API::API, api: true do post api('/projects', user), project project.each_pair do |k, v| + next if %i{ issues_enabled merge_requests_enabled wiki_enabled }.include?(k) expect(json_response[k.to_s]).to eq(v) end + + # Check feature permissions attributes + project = Project.find_by_path(project[:path]) + expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED) + expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED) + expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED) end it 'sets a project as public' do diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index afaf4b7cefb..9ca3b021aa2 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -289,7 +289,8 @@ describe 'Git HTTP requests', lib: true do let(:project) { FactoryGirl.create :empty_project } before do - project.update_attributes(runners_token: token, builds_enabled: true) + project.update_attributes(runners_token: token) + project.project_feature.update_attributes(builds_access_level: ProjectFeature::ENABLED) end it "downloads get status 200" do diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index c6172b9cc7d..fc42b534dca 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -22,19 +22,20 @@ describe JwtController do context 'when using authorized request' do context 'using CI token' do - let(:project) { create(:empty_project, runners_token: 'token', builds_enabled: builds_enabled) } + let(:project) { create(:empty_project, runners_token: 'token') } let(:headers) { { authorization: credentials('gitlab-ci-token', project.runners_token) } } - subject! { get '/jwt/auth', parameters, headers } - context 'project with enabled CI' do - let(:builds_enabled) { true } - + subject! { get '/jwt/auth', parameters, headers } it { expect(service_class).to have_received(:new).with(project, nil, parameters) } end context 'project with disabled CI' do - let(:builds_enabled) { false } + before do + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) + end + + subject! { get '/jwt/auth', parameters, headers } it { expect(response).to have_http_status(403) } end diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 026d0ca6534..1e21a32a062 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -151,6 +151,25 @@ module Ci it { expect(build.runner).to eq(specific_runner) } end end + + context 'disallow when builds are disabled' do + before do + project.update(shared_runners_enabled: true) + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) + end + + context 'and uses shared runner' do + let(:build) { service.execute(shared_runner) } + + it { expect(build).to be_nil } + end + + context 'and uses specific runner' do + let(:build) { service.execute(specific_runner) } + + it { expect(build).to be_nil } + end + end end end end diff --git a/spec/services/merge_requests/get_urls_service_spec.rb b/spec/services/merge_requests/get_urls_service_spec.rb index 8a4b76367e3..3a71776e81f 100644 --- a/spec/services/merge_requests/get_urls_service_spec.rb +++ b/spec/services/merge_requests/get_urls_service_spec.rb @@ -50,7 +50,7 @@ describe MergeRequests::GetUrlsService do let(:changes) { new_branch_changes } before do - project.merge_requests_enabled = false + project.project_feature.update_attribute(:merge_requests_access_level, ProjectFeature::DISABLED) end it_behaves_like 'no_merge_request_url' diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index bbced59ff02..3ea1273abc3 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -69,7 +69,7 @@ describe Projects::CreateService, services: true do context 'wiki_enabled false does not create wiki repository directory' do before do - @opts.merge!(wiki_enabled: false) + @opts.merge!( { project_feature_attributes: { wiki_access_level: ProjectFeature::DISABLED } }) @project = create_project(@user, @opts) @path = ProjectWiki.new(@project, @user).send(:path_to_repo) end @@ -85,7 +85,7 @@ describe Projects::CreateService, services: true do context 'global builds_enabled false does not enable CI by default' do before do - @opts.merge!(builds_enabled: false) + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED) end it { is_expected.to be_falsey } @@ -93,7 +93,7 @@ describe Projects::CreateService, services: true do context 'global builds_enabled true does enable CI by default' do before do - @opts.merge!(builds_enabled: true) + project.project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED) end it { is_expected.to be_truthy } diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb index 05e07789dac..59cfb2c8e3a 100644 --- a/spec/workers/repository_check/single_repository_worker_spec.rb +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -5,7 +5,7 @@ describe RepositoryCheck::SingleRepositoryWorker do subject { described_class.new } it 'passes when the project has no push events' do - project = create(:project_empty_repo, wiki_enabled: false) + project = create(:project_empty_repo, wiki_access_level: ProjectFeature::DISABLED) project.events.destroy_all break_repo(project) @@ -25,7 +25,7 @@ describe RepositoryCheck::SingleRepositoryWorker do end it 'fails if the wiki repository is broken' do - project = create(:project_empty_repo, wiki_enabled: true) + project = create(:project_empty_repo, wiki_access_level: ProjectFeature::ENABLED) project.create_wiki # Test sanity: everything should be fine before the wiki repo is broken @@ -39,7 +39,7 @@ describe RepositoryCheck::SingleRepositoryWorker do end it 'skips wikis when disabled' do - project = create(:project_empty_repo, wiki_enabled: false) + project = create(:project_empty_repo, wiki_access_level: ProjectFeature::DISABLED) # Make sure the test would fail if the wiki repo was checked break_wiki(project) @@ -49,7 +49,7 @@ describe RepositoryCheck::SingleRepositoryWorker do end it 'creates missing wikis' do - project = create(:project_empty_repo, wiki_enabled: true) + project = create(:project_empty_repo, wiki_access_level: ProjectFeature::ENABLED) FileUtils.rm_rf(wiki_path(project)) subject.perform(project.id) -- cgit v1.2.1 From 85e494890a931b6031aed2f9f2e50d30d6f37a32 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 25 Aug 2016 16:15:44 -0500 Subject: Replace play icon font with svg --- CHANGELOG | 1 + app/assets/stylesheets/framework/dropdowns.scss | 7 +++++++ app/assets/stylesheets/pages/environments.scss | 5 +++-- app/assets/stylesheets/pages/pipelines.scss | 7 +++++++ app/views/projects/ci/builds/_build.html.haml | 2 +- app/views/projects/ci/pipelines/_pipeline.html.haml | 4 ++-- app/views/projects/deployments/_actions.haml | 4 ++-- app/views/shared/icons/_icon_play.svg | 4 +++- 8 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9837b2edb9d..08745aea3f1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,7 @@ v 8.12.0 (unreleased) - Load branches asynchronously in Cherry Pick and Revert dialogs. - Add merge request versions !5467 - Change using size to use count and caching it for number of group members. !5935 + - Replace play icon font with svg (ClemMakesApps) - Added 'only_allow_merge_if_build_succeeds' project setting in the API. !5930 (Duck) - Reduce number of database queries on builds tab - Capitalize mentioned issue timeline notes (ClemMakesApps) diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index edb2ff01f88..b0ba112476b 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -183,6 +183,13 @@ &.dropdown-menu-user-link { line-height: 16px; } + + .icon-play { + fill: $table-text-gray; + margin-right: 6px; + height: 12px; + width: 11px; + } } .dropdown-header { diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 55f9d4a0011..d01c60ee6ab 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -4,8 +4,9 @@ margin: 0; } - .fa-play { - font-size: 14px; + .icon-play { + height: 13px; + width: 12px; } .dropdown-new { diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 2d6653cd867..7aabafe11a5 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -215,6 +215,13 @@ border-color: $border-white-normal; } } + + .btn { + .icon-play { + height: 13px; + width: 12px; + } + } } } diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 1fdf32466f2..73de8abe55b 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -89,4 +89,4 @@ = icon('repeat') - elsif build.playable? = link_to play_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do - = icon('play') + = custom_icon('icon_play') diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index b119f6edf14..bb9493f5158 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -66,13 +66,13 @@ - if actions.any? .btn-group %a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'} - = icon("play") + = custom_icon('icon_play') %b.caret %ul.dropdown-menu.dropdown-menu-align-right - actions.each do |build| %li = link_to play_namespace_project_build_path(pipeline.project.namespace, pipeline.project, build), method: :post, rel: 'nofollow' do - = icon("play") + = custom_icon('icon_play') %span= build.name.humanize - if artifacts.present? .btn-group diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml index f7bf3b834ef..16d134eb6b6 100644 --- a/app/views/projects/deployments/_actions.haml +++ b/app/views/projects/deployments/_actions.haml @@ -5,13 +5,13 @@ .inline .dropdown %a.dropdown-new.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'} - = icon("play") + = custom_icon('icon_play') %b.caret %ul.dropdown-menu.dropdown-menu-align-right - actions.each do |action| %li = link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow' do - = icon("play") + = custom_icon('icon_play') %span= action.name.humanize - if local_assigns.fetch(:allow_rollback, false) diff --git a/app/views/shared/icons/_icon_play.svg b/app/views/shared/icons/_icon_play.svg index 80a6d41dbf6..e965afa9a56 100644 --- a/app/views/shared/icons/_icon_play.svg +++ b/app/views/shared/icons/_icon_play.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file -- cgit v1.2.1 From 2560fc5adbc533d87e8ca56b471ed56bc84d95f9 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Wed, 24 Aug 2016 09:38:31 -0500 Subject: Remove inconsistent font weight for sidebar's labels --- CHANGELOG | 1 + app/views/shared/issuable/_sidebar.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 77d44296880..a3d9508dd8f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,7 @@ v 8.12.0 (unreleased) - Don't expose a user's token in the `/api/v3/user` API (!6047) - Remove redundant js-timeago-pending from user activity log (ClemMakesApps) - Ability to manage project issues, snippets, wiki, merge requests and builds access level + - Remove inconsistent font weight for sidebar's labels (ClemMakesApps) - Added tests for diff notes - Add a button to download latest successful artifacts for branches and tags !5142 - Remove redundant pipeline tooltips (ClemMakesApps) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index c1b50e65af5..b13daaf43c9 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -118,7 +118,7 @@ = icon('spinner spin', class: 'block-loading') - if can_edit_issuable = link_to 'Edit', '#', class: 'edit-link pull-right' - .value.bold.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels_array.any?) } + .value.issuable-show-labels.hide-collapsed{ class: ("has-labels" if issuable.labels_array.any?) } - if issuable.labels_array.any? - issuable.labels_array.each do |label| = link_to_label(label, type: issuable.to_ability_name) -- cgit v1.2.1 From dd0431c5f7c93a2f9cee6766c9c8f3b0536022e9 Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Thu, 1 Sep 2016 12:33:37 -0500 Subject: Some minor updates for upgrade guides for 8.12. --- doc/install/installation.md | 4 ++-- doc/update/8.11-to-8.12.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 2d0932d4f04..9522c3e7170 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -268,9 +268,9 @@ sudo usermod -aG redis git ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-11-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-12-stable gitlab -**Note:** You can change `8-11-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-12-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index 953e9d7e74c..c8ca42f97bc 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -82,7 +82,7 @@ GitLab 8.1. ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch --all -sudo -u git -H git checkout v0.7.8 +sudo -u git -H git checkout v0.7.11 sudo -u git -H make ``` -- cgit v1.2.1 From 9a10c0a8d542df2db2e75c230bf116bed421f8d5 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Thu, 1 Sep 2016 19:50:45 +0200 Subject: Rubocop syntax 2.3 --- .rubocop.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 282f4539f03..5bd31ccf329 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,8 +5,8 @@ require: inherit_from: .rubocop_todo.yml AllCops: - TargetRubyVersion: 2.1 - # Cop names are not displayed in offense messages by default. Change behavior + TargetRubyVersion: 2.3 + # Cop names are not d§splayed in offense messages by default. Change behavior # by overriding DisplayCopNames, or by giving the -D/--display-cop-names # option. DisplayCopNames: true @@ -192,6 +192,9 @@ Style/FlipFlop: Style/For: Enabled: true +# Checks if there is a magic comment to enforce string literals +Style/FrozenStringLiteralComment: + Enabled: false # Do not introduce global variables. Style/GlobalVars: Enabled: true -- cgit v1.2.1 From 191ed1faebfa486ea6b1f72ce764ece730d6e64a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 1 Sep 2016 13:34:50 -0500 Subject: Add curve to generic commit status pipeline --- .../generic_commit_statuses/_generic_commit_status_pipeline.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml index 31d40f6ad03..576d0bec51b 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status_pipeline.html.haml @@ -1,4 +1,5 @@ %li.build + .curve .build-content - if subject.target_url - link_to subject.target_url do -- cgit v1.2.1 From bf1337be949baef050ad99fc5e4c60e7ac4e3aae Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 1 Sep 2016 14:28:53 -0500 Subject: Change widths of content in MR pipeline tab --- app/assets/stylesheets/pages/pipelines.scss | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 2911576b66f..ee5d9de66d8 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -474,12 +474,16 @@ .pipelines.tab-pane { .content-list.pipelines { - overflow: scroll; + overflow: auto; } .stage { - max-width: 60px; - width: 60px; + max-width: 100px; + width: 100px; + } + + .pipeline-actions { + min-width: initial; } } -- cgit v1.2.1 From f43a0470bd52132c2853582c51637a736dcce5e8 Mon Sep 17 00:00:00 2001 From: Regis Date: Thu, 1 Sep 2016 14:51:53 -0600 Subject: removed null return - renamed 'placeTop' to 'placeProfileAvatarsToTop' --- app/assets/javascripts/user.js.es6 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/user.js.es6 b/app/assets/javascripts/user.js.es6 index 833f35df59b..6889d3a7491 100644 --- a/app/assets/javascripts/user.js.es6 +++ b/app/assets/javascripts/user.js.es6 @@ -2,12 +2,12 @@ global.User = class { constructor(opts) { this.opts = opts; - this.placeTop(); + this.placeProfileAvatarsToTop(); this.initTabs(); this.hideProjectLimitMessage(); } - placeTop() { + placeProfileAvatarsToTop() { $('.profile-groups-avatars').tooltip({ placement: 'top' }); @@ -28,7 +28,6 @@ path: path }); $(this).parents('.project-limit-message').remove(); - return; }); } } -- cgit v1.2.1 From 818c3f79f74f205f9e8762c4eab3ace28f311797 Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Thu, 1 Sep 2016 17:22:03 -0500 Subject: Update CHANGELOG with 8.11.4 entries. --- CHANGELOG | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index bdc5685ed27..76123f62b27 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -71,27 +71,33 @@ v 8.12.0 (unreleased) v 8.11.5 (unreleased) - Optimize branch lookups and force a repository reload for Repository#find_branch + - Fix suggested colors options for new labels in the admin area. !6138 -v 8.11.4 (unreleased) - - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) +v 8.11.4 + - Fix resolving conflicts on forks. !6082 + - Fix diff commenting on merge requests created prior to 8.10. !6029 + - Fix pipelines tab layout regression. !5952 + - Fix "Wiki" link not appearing in navigation for projects with external wiki. !6057 + - Do not enforce using hash with hidden key in CI configuration. !6079 + - Fix hover leading space bug in pipeline graph !5980 - Fix sorting issues by "last updated" doesn't work after import from GitHub - GitHub importer use default project visibility for non-private projects - Creating an issue through our API now emails label subscribers !5720 - - Fix suggested colors options for new labels in the admin area - Block concurrent updates for Pipeline - - Fix resolving conflicts on forks - - Fix diff commenting on merge requests created prior to 8.10 + - Don't create groups for unallowed users when importing projects - Fix issue boards leak private label names and descriptions + - Fix broken gitlab:backup:restore because of bad permissions on repo storage !6098 (Dirk Hörner) + - Remove gitorious. !5866 v 8.11.3 - - Do not enforce using hash with hidden key in CI configuration. !6079 - Allow system info page to handle case where info is unavailable - Label list shows all issues (opened or closed) with that label - Don't show resolve conflicts link before MR status is updated - - Fix "Wiki" link not appearing in navigation for projects with external wiki - - Fix IE11 fork button bug !598 + - Fix IE11 fork button bug !5982 - Don't prevent viewing the MR when git refs for conflicts can't be found on disk - Fix external issue tracker "Issues" link leading to 404s + - Don't try to show merge conflict resolution info if a merge conflict contains non-UTF-8 characters + - Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling) v 8.11.2 - Show "Create Merge Request" widget for push events to fork projects on the source project. !5978 @@ -103,8 +109,6 @@ v 8.11.2 v 8.11.1 - Pulled due to packaging error. -v 8.11.0 (unreleased) - - Fix pipelines tab layout regression (brycepj) v 8.11.0 - Use test coverage value from the latest successful pipeline in badge. !5862 - Add test coverage report badge. !5708 -- cgit v1.2.1 From 3ef2c38b63031984c2a284a983bce805c5a0181a Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 2 Sep 2016 08:55:54 +0200 Subject: Change the inline code to codeblocks for the new features doc guideline [ci skip] --- doc/development/doc_styleguide.md | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md index 37bb59e112c..39b801f761d 100644 --- a/doc/development/doc_styleguide.md +++ b/doc/development/doc_styleguide.md @@ -155,15 +155,30 @@ Inside the document: - Every piece of documentation that comes with a new feature should declare the GitLab version that feature got introduced. Right below the heading add a - note: `> Introduced in GitLab 8.3.`. + note: + + ``` + > Introduced in GitLab 8.3. + ``` + - If possible every feature should have a link to the MR that introduced it. The above note would be then transformed to: - `> [Introduced][ce-1242] in GitLab 8.3.`, where - the [link identifier](#links) is named after the repository (CE) and the MR - number. -- If the feature is only in GitLab EE, don't forget to mention it, like: - `> Introduced in GitLab EE 8.3.`. Otherwise, leave - this mention out. + + ``` + > [Introduced][ce-1242] in GitLab 8.3. + ``` + + , where the [link identifier](#links) is named after the repository (CE) and + the MR number. + +- If the feature is only in GitLab Enterprise Edition, don't forget to mention + it, like: + + ``` + > Introduced in GitLab Enterprise Edition 8.3. + ``` + + Otherwise, leave this mention out. ## References -- cgit v1.2.1 From 619b350279c6e3a987cefce935fdaf7ab13632ff Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Tue, 23 Aug 2016 15:18:42 +0000 Subject: Update memory requirements --- doc/install/requirements.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 571f1a38358..b499d3422d1 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -63,24 +63,24 @@ If you have enough RAM memory and a recent CPU the speed of GitLab is mainly lim ### Memory -You need at least 2GB of addressable memory (RAM + swap) to install and use GitLab! +You need at least 4GB of addressable memory (RAM + swap) to install and use GitLab! The operating system and any other running applications will also be using memory -so keep in mind that you need at least 2GB available before running GitLab. With +so keep in mind that you need at least 4GB available before running GitLab. With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage. -- 512MB RAM + 1.5GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. -- 1GB RAM + 1GB swap supports up to 100 users but it will be very slow -- **2GB RAM** is the **recommended** memory size for all installations and supports up to 100 users -- 4GB RAM supports up to 1,000 users -- 8GB RAM supports up to 2,000 users -- 16GB RAM supports up to 4,000 users -- 32GB RAM supports up to 8,000 users -- 64GB RAM supports up to 16,000 users -- 128GB RAM supports up to 32,000 users +- 1GB RAM + 3GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. +- 2GB RAM + 2GB swap supports up to 100 users but it will be very slow +- **4GB RAM** is the **recommended** memory size for all installations and supports up to 100 users +- 8GB RAM supports up to 1,000 users +- 16GB RAM supports up to 2,000 users +- 32GB RAM supports up to 4,000 users +- 64GB RAM supports up to 8,000 users +- 128GB RAM supports up to 16,000 users +- 256GB RAM supports up to 32,000 users - More users? Run it on [multiple application servers](https://about.gitlab.com/high-availability/) -We recommend having at least 1GB of swap on your server, even if you currently have +We recommend having at least 2GB of swap on your server, even if you currently have enough available RAM. Having swap will help reduce the chance of errors occurring if your available memory changes. @@ -113,10 +113,10 @@ It's possible to increase the amount of unicorn workers and this will usually he For most instances we recommend using: CPU cores + 1 = unicorn workers. So for a machine with 2 cores, 3 unicorn workers is ideal. -For all machines that have 1GB and up we recommend a minimum of three unicorn workers. -If you have a 512MB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. +For all machines that have 2GB and up we recommend a minimum of three unicorn workers. +If you have a 1GB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). -If you have a 512MB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. +If you have a 1GB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). -- cgit v1.2.1 From a3828abb63306967c8e2d1c3602b8b7d953b74a0 Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Thu, 1 Sep 2016 09:08:25 +0200 Subject: Change minimum Unicorns required to two --- doc/install/requirements.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index b499d3422d1..04907249f5c 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -114,9 +114,7 @@ For most instances we recommend using: CPU cores + 1 = unicorn workers. So for a machine with 2 cores, 3 unicorn workers is ideal. For all machines that have 2GB and up we recommend a minimum of three unicorn workers. -If you have a 1GB machine with a magnetic (non-SSD) swap drive we recommend to configure only one Unicorn worker to prevent excessive swapping. -With one Unicorn worker only git over ssh access will work because the git over HTTP access requires two running workers (one worker to receive the user request and one worker for the authorization check). -If you have a 1GB machine with a SSD drive you can use two Unicorn workers, this will allow HTTP access although it will be slow due to swapping. +If you have a 1GB machine we recommend to configure only two Unicorn worker to prevent excessive swapping. To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). -- cgit v1.2.1 From 04a5a88f61c61fa916d151da9505be7292b7c7d9 Mon Sep 17 00:00:00 2001 From: Ahmad Sherif Date: Thu, 1 Sep 2016 09:24:47 +0200 Subject: Fix a typo --- doc/install/requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 04907249f5c..9799e0a3730 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -114,7 +114,7 @@ For most instances we recommend using: CPU cores + 1 = unicorn workers. So for a machine with 2 cores, 3 unicorn workers is ideal. For all machines that have 2GB and up we recommend a minimum of three unicorn workers. -If you have a 1GB machine we recommend to configure only two Unicorn worker to prevent excessive swapping. +If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping. To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings). -- cgit v1.2.1