From e2108f16ec2c8bcc51a7afe69bfe20f3bb0cb37f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 29 Sep 2015 09:37:46 -0700 Subject: [PATCH 001/119] Update upgrade guide for Redis >= 2.4.0 requirement [ci skip] Closes #2837 --- README.md | 2 +- doc/update/7.14-to-8.0.md | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 99d5bc0b6ca..e66a5546db2 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ GitLab is a Ruby on Rails application that runs on the following software: - Ubuntu/Debian/CentOS/RHEL - Ruby (MRI) 2.1 - Git 1.7.10+ -- Redis 2.0+ +- Redis 2.4+ - MySQL or PostgreSQL For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html). diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 552216be932..b5d7382e49a 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -175,12 +175,21 @@ Also note that because Apache does not support upstreams behind Unix sockets you Now, GitLab CE and EE has CI integrated. However, migrations don't happen automatically and you need to do it manually. Please follow the following guide [to migrate](../migrate_ci_to_ce/README.md) your GitLab CI instance to GitLab CE/EE. -### 10. Start application +### 10. Use Redis v2.4.0+ + +Previous versions of GitLab allowed Redis versions >= 2.0 to be used, but +Sidekiq jobs could fail due to lack of support for the SREM command. GitLab +8.0 now checks that Redis >= 2.4.0 is used. You can check your Redis version +with the following command: + + redis-cli info | grep redis_version + +### 11. Start application sudo service gitlab start sudo service nginx restart -### 11. Check application status +### 12. Check application status Check if GitLab and its environment are configured correctly: From 42be5ee1cd8eb752c52ddb82bdf7c452190d4193 Mon Sep 17 00:00:00 2001 From: Ricardo Band Date: Sat, 12 Sep 2015 20:53:18 +0000 Subject: [PATCH 002/119] hooks: Add full project namespace to payload Payload of "project_member, :create" and "project_member, :destroy" now also have a field project_path_with_namespace. --- CHANGELOG | 1 + app/services/system_hooks_service.rb | 1 + doc/system_hooks/system_hooks.md | 42 +++++++++++----------- spec/services/system_hooks_service_spec.rb | 4 +-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f01da3fe7dd..01772e1d339 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.1.0 (unreleased) - Ensure code blocks are properly highlighted after a note is updated - Fix wrong access level badge on MR comments - Hide password in the service settings form + - Add full project namespace to payload of system webhooks (Ricardo Band) v 8.0.3 - Fix URL shown in Slack notifications diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 60235b6be2a..9a5fe4af9dd 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -54,6 +54,7 @@ class SystemHooksService data.merge!({ project_name: model.project.name, project_path: model.project.path, + project_path_with_namespace: model.project.path_with_namespace, project_id: model.project.id, user_name: model.user.name, user_email: model.user.email, diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index b0e4613cdef..5cb05b13b3e 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -48,16 +48,17 @@ X-Gitlab-Event: System Hook ```json { - "created_at": "2012-07-21T07:30:56Z", - "event_name": "user_add_to_team", - "project_access": "Master", - "project_id": 74, - "project_name": "StoreCloud", - "project_path": "storecloud", - "user_email": "johnsmith@gmail.com", - "user_name": "John Smith", - "user_id": 41, - "project_visibility": "private", + "created_at": "2012-07-21T07:30:56Z", + "event_name": "user_add_to_team", + "project_access": "Master", + "project_id": 74, + "project_name": "StoreCloud", + "project_path": "storecloud", + "project_path_with_namespace": "jsmith/storecloud", + "user_email": "johnsmith@gmail.com", + "user_name": "John Smith", + "user_id": 41, + "project_visibility": "private", } ``` @@ -65,16 +66,17 @@ X-Gitlab-Event: System Hook ```json { - "created_at": "2012-07-21T07:30:56Z", - "event_name": "user_remove_from_team", - "project_access": "Master", - "project_id": 74, - "project_name": "StoreCloud", - "project_path": "storecloud", - "user_email": "johnsmith@gmail.com", - "user_name": "John Smith", - "user_id": 41, - "project_visibility": "private", + "created_at": "2012-07-21T07:30:56Z", + "event_name": "user_remove_from_team", + "project_access": "Master", + "project_id": 74, + "project_name": "StoreCloud", + "project_path": "storecloud", + "project_path_with_namespace": "jsmith/storecloud", + "user_email": "johnsmith@gmail.com", + "user_name": "John Smith", + "user_id": 41, + "project_visibility": "private", } ``` diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 48c49e2f717..a31fc1e4b07 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -13,8 +13,8 @@ describe SystemHooksService do it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } From abf18abb52dbdb409d481854bde24ed880c76f56 Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Sun, 4 Oct 2015 23:39:24 -0700 Subject: [PATCH 003/119] allow crawling of commit page but not patch/diffs Commit page has valuable information that search engines should be allowed to crawl however the .patch and .diff pages have no new information that is not on commit page --- public/robots.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/robots.txt b/public/robots.txt index 528f421083e..4f616c7f4c1 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -38,7 +38,8 @@ Disallow: /*/*/edit Disallow: /*/*/raw Disallow: /*/*/blame Disallow: /*/*/commits/*/* -Disallow: /*/*/commit +Disallow: /*/*/commit/*.patch +Disallow: /*/*/commit/*.diff Disallow: /*/*/compare Disallow: /*/*/branches/new Disallow: /*/*/tags/new From a5f6af007a307b91b8fa45dfe821d5da31e32c47 Mon Sep 17 00:00:00 2001 From: Jan Bruckner Date: Mon, 5 Oct 2015 15:27:01 +0200 Subject: [PATCH 004/119] don't set LATEST_TAG automatically in patch update guide --- doc/update/patch_versions.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index a66a863f6c4..da719229ab6 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -23,9 +23,11 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production cd /home/git/gitlab sudo -u git -H git fetch --all sudo -u git -H git checkout -- Gemfile.lock db/schema.rb -LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`) -sudo -u git -H git checkout $LATEST_TAG -b $LATEST_TAG +sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG ``` +Replace `LATEST_TAG` with the latest GitLab tag you want to update to, for example `v8.0.3`. +Use `git tag -l 'v*.[0-9]' --sort='v:refname'` to see a list of all tags. +Make sure to update patch versions only (check your current version with `cat VERSION`) ### 3. Update gitlab-shell to the corresponding version From 01fe901e47895df923235b66d7e8ce0f2341ee2b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 5 Oct 2015 09:59:02 -0700 Subject: [PATCH 005/119] Fixes GDK issue where repos would not be imported properly Seed-Fu runs this entire fixture in a transaction, so the `after_commit` hook won't run until after the fixture is loaded. That is too late since the Sidekiq::Testing block has already exited. Force clearing the `after_commit` queue to ensure the job is run now. See: gitlab-org/gitlab-development-kit#58 --- db/fixtures/development/04_project.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index 8f71198e47f..973b3f278f9 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -73,6 +73,11 @@ Sidekiq::Testing.inline! do } project = Projects::CreateService.new(User.first, params).execute + # Seed-Fu runs this entire fixture in a transaction, so the `after_commit` + # hook won't run until after the fixture is loaded. That is too late + # since the Sidekiq::Testing block has already exited. Force clearing + # the `after_commit` queue to ensure the job is run now. + project.send(:_run_after_commit_queue) if project.valid? print '.' From 1f14e689e579163222fa0a410d3ab4ca672d465e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Oct 2015 13:52:11 +0200 Subject: [PATCH 006/119] Added specs for TrendingProjectsFinder --- spec/finders/trending_projects_finder_spec.rb | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 spec/finders/trending_projects_finder_spec.rb diff --git a/spec/finders/trending_projects_finder_spec.rb b/spec/finders/trending_projects_finder_spec.rb new file mode 100644 index 00000000000..1ad2e23f1f1 --- /dev/null +++ b/spec/finders/trending_projects_finder_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe TrendingProjectsFinder do + let(:user) { create(:user) } + let(:group) { create(:group) } + + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :public, group: group) } + + before do + 2.times do + create(:note_on_commit, project: project1) + end + + create(:note_on_commit, project: project2) + end + + describe '#execute' do + describe 'without an explicit start date' do + subject { described_class.new.execute(user).to_a } + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project1, project2]) + end + end + + describe 'with an explicit start date' do + let(:date) { 2.months.ago } + + subject { described_class.new.execute(user, date).to_a } + + before do + 2.times do + create(:note_on_commit, project: project2, created_at: date) + end + end + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project2, project1]) + end + end + end +end From d15eec64604d68093d8f01711c1847ee240eb0d2 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Oct 2015 13:52:19 +0200 Subject: [PATCH 007/119] Use >= instead of > in TrendingProjectsFinder By using >= we can ensure we actually get all comments of the past month, instead of the comments of the past month minus the first day in the range. --- app/finders/trending_projects_finder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb index 9ea342cb26d..9b710f0751f 100644 --- a/app/finders/trending_projects_finder.rb +++ b/app/finders/trending_projects_finder.rb @@ -6,7 +6,7 @@ class TrendingProjectsFinder # Determine trending projects based on comments count # for period of time - ex. month - projects.joins(:notes).where('notes.created_at > ?', start_date). + projects.joins(:notes).where('notes.created_at >= ?', start_date). group("projects.id").reorder("count(notes.id) DESC") end From b7abba0ca0a4629a854eee0488f94f160452e2f6 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Oct 2015 16:35:51 +0200 Subject: [PATCH 008/119] Revamp trending projects query This changes the query to use a COUNT nested in an INNER JOIN, instead of a COUNT plus a GROUP BY. There are two reasons for this: 1. Using a COUNT in an INNER JOIN can be quite a bit faster. 2. The use of a GROUP BY means that method calls such as "any?" (and everything else that calls "count") operate on a Hash that counts the amount of notes on a per project basis, instead of just counting the total amount of projects. The query has been moved into Project.trending as its logic is simple enough. As a result of this testing the TrendingProjectsFinder class simply involves testing if the right methods are called, removing the need for setting up database records. --- app/finders/trending_projects_finder.rb | 11 +---- app/models/project.rb | 14 ++++++ spec/finders/trending_projects_finder_spec.rb | 44 +++++++++---------- spec/models/project_spec.rb | 38 ++++++++++++++++ 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb index 9b710f0751f..81a12403801 100644 --- a/app/finders/trending_projects_finder.rb +++ b/app/finders/trending_projects_finder.rb @@ -1,13 +1,6 @@ class TrendingProjectsFinder - def execute(current_user, start_date = nil) - start_date ||= Date.today - 1.month - - projects = projects_for(current_user) - - # Determine trending projects based on comments count - # for period of time - ex. month - projects.joins(:notes).where('notes.created_at >= ?', start_date). - group("projects.id").reorder("count(notes.id) DESC") + def execute(current_user, start_date = 1.month.ago) + projects_for(current_user).trending(start_date) end private diff --git a/app/models/project.rb b/app/models/project.rb index bb47b9abb03..4661522b8a0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -260,6 +260,20 @@ class Project < ActiveRecord::Base name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR %r{(?#{name_pattern}/#{name_pattern})} end + + def trending(since = 1.month.ago) + # By counting in the JOIN we don't expose the GROUP BY to the outer query. + # This means that calls such as "any?" and "count" just return a number of + # the total count, instead of the counts grouped per project as a Hash. + join_body = "INNER JOIN ( + SELECT project_id, COUNT(*) AS amount + FROM notes + WHERE created_at >= #{sanitize(since)} + GROUP BY project_id + ) join_note_counts ON projects.id = join_note_counts.project_id" + + joins(join_body).reorder('join_note_counts.amount DESC') + end end def team diff --git a/spec/finders/trending_projects_finder_spec.rb b/spec/finders/trending_projects_finder_spec.rb index 1ad2e23f1f1..a49cbfd5160 100644 --- a/spec/finders/trending_projects_finder_spec.rb +++ b/spec/finders/trending_projects_finder_spec.rb @@ -1,42 +1,38 @@ require 'spec_helper' describe TrendingProjectsFinder do - let(:user) { create(:user) } - let(:group) { create(:group) } - - let(:project1) { create(:empty_project, :public, group: group) } - let(:project2) { create(:empty_project, :public, group: group) } - - before do - 2.times do - create(:note_on_commit, project: project1) - end - - create(:note_on_commit, project: project2) - end + let(:user) { build(:user) } describe '#execute' do describe 'without an explicit start date' do - subject { described_class.new.execute(user).to_a } + subject { described_class.new } - it 'sorts Projects by the amount of notes in descending order' do - expect(subject).to eq([project1, project2]) + it 'returns the trending projects' do + relation = double(:ar_relation) + + allow(subject).to receive(:projects_for) + .with(user) + .and_return(relation) + + allow(relation).to receive(:trending) + .with(an_instance_of(ActiveSupport::TimeWithZone)) end end describe 'with an explicit start date' do let(:date) { 2.months.ago } - subject { described_class.new.execute(user, date).to_a } + subject { described_class.new } - before do - 2.times do - create(:note_on_commit, project: project2, created_at: date) - end - end + it 'returns the trending projects' do + relation = double(:ar_relation) - it 'sorts Projects by the amount of notes in descending order' do - expect(subject).to eq([project2, project1]) + allow(subject).to receive(:projects_for) + .with(user) + .and_return(relation) + + allow(relation).to receive(:trending) + .with(date) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8b5d2c3a1c1..f93935ebe3b 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -423,4 +423,42 @@ describe Project do it { expect(project.gitlab_ci?).to be_truthy } it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } end + + describe '.trending' do + let(:group) { create(:group) } + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :public, group: group) } + + before do + 2.times do + create(:note_on_commit, project: project1) + end + + create(:note_on_commit, project: project2) + end + + describe 'without an explicit start date' do + subject { described_class.trending.to_a } + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project1, project2]) + end + end + + describe 'with an explicit start date' do + let(:date) { 2.months.ago } + + subject { described_class.trending(date).to_a } + + before do + 2.times do + create(:note_on_commit, project: project2, created_at: date) + end + end + + it 'sorts Projects by the amount of notes in descending order' do + expect(subject).to eq([project2, project1]) + end + end + end end From 7adf9a52ba94433f7de30f190a2d94bc3d7949fd Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Oct 2015 16:39:03 +0200 Subject: [PATCH 009/119] Added benchmarks for finding trending projects --- .../finders/trending_projects_finder_spec.rb | 14 ++++++++ spec/benchmarks/models/project_spec.rb | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 spec/benchmarks/finders/trending_projects_finder_spec.rb create mode 100644 spec/benchmarks/models/project_spec.rb diff --git a/spec/benchmarks/finders/trending_projects_finder_spec.rb b/spec/benchmarks/finders/trending_projects_finder_spec.rb new file mode 100644 index 00000000000..551ce21840d --- /dev/null +++ b/spec/benchmarks/finders/trending_projects_finder_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe TrendingProjectsFinder, benchmark: true do + describe '#execute' do + let(:finder) { described_class.new } + let(:user) { create(:user) } + + # to_a is used to force actually running the query (instead of just building + # it). + benchmark_subject { finder.execute(user).non_archived.to_a } + + it { is_expected.to iterate_per_second(500) } + end +end diff --git a/spec/benchmarks/models/project_spec.rb b/spec/benchmarks/models/project_spec.rb new file mode 100644 index 00000000000..f1dd10440a9 --- /dev/null +++ b/spec/benchmarks/models/project_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Project, benchmark: true do + describe '.trending' do + let(:group) { create(:group) } + let(:project1) { create(:empty_project, :public, group: group) } + let(:project2) { create(:empty_project, :public, group: group) } + + let(:iterations) { 500 } + + before do + 2.times do + create(:note_on_commit, project: project1) + end + + create(:note_on_commit, project: project2) + end + + describe 'without an explicit start date' do + benchmark_subject { described_class.trending.to_a } + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'with an explicit start date' do + let(:date) { 1.month.ago } + + benchmark_subject { described_class.trending(date).to_a } + + it { is_expected.to iterate_per_second(iterations) } + end + end +end From 4841e883020146fecd386caff7f31f2d9cf307b6 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 6 Oct 2015 17:32:22 +0200 Subject: [PATCH 010/119] Added changelog entry for trending projects tweaks --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 0400dbfabec..ea0f6909f5a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) + - Improved performance of the trending projects page - Fix bug where transferring a project would result in stale commit links (Stan Hu) - Include full path of source and target branch names in New Merge Request page (Stan Hu) - Add user preference to view activities as default dashboard (Stan Hu) From 9ccd8e2617e9d6e8b5f2ef02e46b5ac8457807aa Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 6 Oct 2015 11:21:45 -0700 Subject: [PATCH 011/119] Verify repository is valid --- db/fixtures/development/04_project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index 973b3f278f9..b4639999967 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -79,7 +79,7 @@ Sidekiq::Testing.inline! do # the `after_commit` queue to ensure the job is run now. project.send(:_run_after_commit_queue) - if project.valid? + if project.valid? && project.valid_repo? print '.' else puts project.errors.full_messages From 0dd49560b95439a74eb25282127a3d8066b68e2a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 6 Oct 2015 21:42:32 -0400 Subject: [PATCH 012/119] Update omniauth-twitter to ~> 1.2.0 --- Gemfile | 24 ++++++++++++------------ Gemfile.lock | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Gemfile b/Gemfile index 4938cbf8b80..573fbedee6c 100644 --- a/Gemfile +++ b/Gemfile @@ -22,20 +22,20 @@ gem "mysql2", '~> 0.3.16', group: :mysql gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries -gem "devise", '~> 3.5.2' -gem "devise-async", '~> 0.9.0' -gem 'omniauth', "~> 1.2.2" +gem 'devise', '~> 3.5.2' +gem 'devise-async', '~> 0.9.0' +gem 'omniauth', '~> 1.2.2' +gem 'omniauth-bitbucket', '~> 0.0.2' +gem 'omniauth-github', '~> 1.1.1' +gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.5' -gem 'omniauth-twitter', '~> 1.0.1' -gem 'omniauth-github', '~> 1.1.1' -gem 'omniauth-shibboleth', '~> 1.1.1' -gem 'omniauth-kerberos', '~> 0.2.0', group: :kerberos -gem 'omniauth-gitlab', '~> 1.0.0' -gem 'omniauth-bitbucket', '~> 0.0.2' -gem 'omniauth-saml', '~> 1.4.0' -gem 'doorkeeper', '~> 2.1.3' +gem 'omniauth-kerberos', '~> 0.2.0', group: :kerberos +gem 'omniauth-saml', '~> 1.4.0' +gem 'omniauth-shibboleth', '~> 1.1.1' +gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' -gem "rack-oauth2", "~> 1.0.5" +gem 'doorkeeper', '~> 2.1.3' +gem 'rack-oauth2', '~> 1.0.5' # Two-factor authentication gem 'devise-two-factor', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1dd56cd9c8c..8a0474a062b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -456,9 +456,9 @@ GEM ruby-saml (~> 1.0.0) omniauth-shibboleth (1.1.2) omniauth (>= 1.0.0) - omniauth-twitter (1.0.1) - multi_json (~> 1.3) - omniauth-oauth (~> 1.0) + omniauth-twitter (1.2.1) + json (~> 1.3) + omniauth-oauth (~> 1.1) omniauth_crowd (2.2.3) activesupport nokogiri (>= 1.4.4) @@ -874,7 +874,7 @@ DEPENDENCIES omniauth-kerberos (~> 0.2.0) omniauth-saml (~> 1.4.0) omniauth-shibboleth (~> 1.1.1) - omniauth-twitter (~> 1.0.1) + omniauth-twitter (~> 1.2.0) omniauth_crowd org-ruby (~> 0.9.12) paranoia (~> 2.0) From 25f77305c3cf66a0c0aa5f15e59be6afb7009be3 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 6 Oct 2015 21:48:19 -0400 Subject: [PATCH 013/119] Update omniauth-kerberos to ~> 0.3.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 573fbedee6c..59169b724c4 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.5' -gem 'omniauth-kerberos', '~> 0.2.0', group: :kerberos +gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-shibboleth', '~> 1.1.1' gem 'omniauth-twitter', '~> 1.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8a0474a062b..87c9f5aa08e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -440,7 +440,7 @@ GEM omniauth-google-oauth2 (0.2.6) omniauth (> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-kerberos (0.2.0) + omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) omniauth-multipassword (0.4.2) @@ -871,7 +871,7 @@ DEPENDENCIES omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.0) omniauth-google-oauth2 (~> 0.2.5) - omniauth-kerberos (~> 0.2.0) + omniauth-kerberos (~> 0.3.0) omniauth-saml (~> 1.4.0) omniauth-shibboleth (~> 1.1.1) omniauth-twitter (~> 1.2.0) From 59abf498c67872da702ee35832859f6c07b37a8e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 6 Oct 2015 21:57:10 -0400 Subject: [PATCH 014/119] Loosen omniauth-google-oauth2 to ~> 0.2.0 --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 59169b724c4..98cd3bcd260 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' -gem 'omniauth-google-oauth2', '~> 0.2.5' +gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-shibboleth', '~> 1.1.1' From 33222b575603e6e24446ded8fa33ff36cfa25c0e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 6 Oct 2015 22:03:42 -0400 Subject: [PATCH 015/119] Update omniauth-shibboleth to ~> 1.2.0 --- Gemfile | 4 ++-- Gemfile.lock | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 98cd3bcd260..bfcdeff16da 100644 --- a/Gemfile +++ b/Gemfile @@ -24,6 +24,7 @@ gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries gem 'devise', '~> 3.5.2' gem 'devise-async', '~> 0.9.0' +gem 'doorkeeper', '~> 2.1.3' gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-github', '~> 1.1.1' @@ -31,10 +32,9 @@ gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.0' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-saml', '~> 1.4.0' -gem 'omniauth-shibboleth', '~> 1.1.1' +gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' -gem 'doorkeeper', '~> 2.1.3' gem 'rack-oauth2', '~> 1.0.5' # Two-factor authentication diff --git a/Gemfile.lock b/Gemfile.lock index 87c9f5aa08e..1ae60c62d6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -454,7 +454,7 @@ GEM omniauth-saml (1.4.1) omniauth (~> 1.1) ruby-saml (~> 1.0.0) - omniauth-shibboleth (1.1.2) + omniauth-shibboleth (1.2.1) omniauth (>= 1.0.0) omniauth-twitter (1.2.1) json (~> 1.3) @@ -870,10 +870,10 @@ DEPENDENCIES omniauth-bitbucket (~> 0.0.2) omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.0) - omniauth-google-oauth2 (~> 0.2.5) + omniauth-google-oauth2 (~> 0.2.0) omniauth-kerberos (~> 0.3.0) omniauth-saml (~> 1.4.0) - omniauth-shibboleth (~> 1.1.1) + omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd org-ruby (~> 0.9.12) From f9aab3fb586614dc21fa799a5088dc53acd43215 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 7 Oct 2015 11:43:38 +0200 Subject: [PATCH 016/119] Clarify Reply by email settings when using Postfix mail server --- config/mail_room.yml.example | 2 +- doc/incoming_email/README.md | 67 ++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 7 deletions(-) diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example index 82e1a42058e..ed4a5193a6a 100644 --- a/config/mail_room.yml.example +++ b/config/mail_room.yml.example @@ -9,7 +9,7 @@ # # Whether the IMAP server uses StartTLS # :start_tls: false # # Email account username. Usually the full email address. - # :email: "replies@gitlab.example.com" + # :email: "gitlab-incoming@gmail.com" # # Email account password # :password: "password" # # The name of the mailbox where incoming mail will end up. Usually "inbox". diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md index 316746ab54d..87267423471 100644 --- a/doc/incoming_email/README.md +++ b/doc/incoming_email/README.md @@ -16,24 +16,35 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these ## Set it up -In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. - ### Omnibus package installations 1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` that references the item being replied to and fill in the details for your specific IMAP server and email account: ```ruby + # Postfix mail server, assumes mailbox incoming@gitlab.example.com + gitlab_rails['incoming_email_enabled'] = true + gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com" + gitlab_rails['incoming_email_host'] = "gitlab.example.com" # IMAP server host + gitlab_rails['incoming_email_port'] = 143 # IMAP server port + gitlab_rails['incoming_email_ssl'] = false # Whether the IMAP server uses SSL + gitlab_rails['incoming_email_email'] = "incoming" # Email account username. Usually the full email address. + gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password + gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". + ``` + + ```ruby + # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com gitlab_rails['incoming_email_enabled'] = true gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com" gitlab_rails['incoming_email_host'] = "imap.gmail.com" # IMAP server host gitlab_rails['incoming_email_port'] = 993 # IMAP server port gitlab_rails['incoming_email_ssl'] = true # Whether the IMAP server uses SSL gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com" # Email account username. Usually the full email address. - gitlab_rails['incoming_email_password'] = "password" # Email account password + gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox". ``` - As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`. + As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`. 1. Reconfigure GitLab for the changes to take effect: @@ -64,12 +75,20 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. ``` ```yaml + # Postfix mail server, assumes mailbox incoming@gitlab.example.com + incoming_email: + enabled: true + address: "incoming+%{key}@gitlab.example.com" + ``` + + ```yaml + # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com incoming_email: enabled: true address: "gitlab-incoming+%{key}@gmail.com" ``` - As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`. + As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`. 2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`: @@ -84,6 +103,40 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. ``` ```yaml + # Postfix mail server + :mailboxes: + - + # IMAP server host + :host: "gitlab.example.com" + # IMAP server port + :port: 143 + # Whether the IMAP server uses SSL + :ssl: false + # Whether the IMAP server uses StartTLS + :start_tls: false + # Email account username. Usually the full email address. + :email: "incoming" + # Email account password + :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". + :name: "inbox" + # Always "sidekiq". + :delivery_method: sidekiq + # Always true. + :delete_after_delivery: true + :delivery_options: + # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "resque:gitlab". + :namespace: resque:gitlab + # Always "incoming_email". + :queue: incoming_email + # Always "EmailReceiverWorker" + :worker: EmailReceiverWorker + ``` + + ```yaml + # Gmail / Google Apps :mailboxes: - # IMAP server host @@ -143,6 +196,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. 1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to: ```yaml + # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com incoming_email: enabled: true address: "gitlab-incoming+%{key}@gmail.com" @@ -159,6 +213,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. 3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account: ```yaml + # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com :mailboxes: - # IMAP server host @@ -208,4 +263,4 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`. bundle exec rake gitlab:incoming_email:check RAILS_ENV=development ``` -8. Reply by email should now be working. \ No newline at end of file +8. Reply by email should now be working. From 0ab6ca93aadaf08f65f36e7fbdb5b837bac6e160 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 16 Sep 2015 22:45:22 -0700 Subject: [PATCH 017/119] Add directory feature button Change "+" icon under "Files" section to have three options: * Create file * Upload file * New directory Upload file is no longer accessible from the "Create file" page. Users can now select a target branch in upload file as well. Closes #2799: Fixes a bug where file modes were overwritten after a commit Closes https://github.com/gitlabhq/gitlabhq/issues/8253: Existing files can no longer be overwritten in the "Create file" section. Closes #2557 --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 4 +- .../blob/blob_file_dropzone.js.coffee | 1 + app/controllers/projects/blob_controller.rb | 8 +-- app/controllers/projects/tree_controller.rb | 37 +++++++++++ app/models/repository.rb | 29 ++++++--- app/services/files/create_dir_service.rb | 9 +++ app/services/files/create_service.rb | 2 +- app/services/files/update_service.rb | 2 +- app/services/merge_requests/merge_service.rb | 2 +- app/views/projects/blob/_new_dir.html.haml | 25 ++++++++ app/views/projects/blob/_upload.html.haml | 9 ++- app/views/projects/blob/new.html.haml | 7 +-- app/views/projects/tree/_tree.html.haml | 26 ++++++-- config/routes.rb | 9 +++ features/project/source/browse_files.feature | 33 +++++++--- features/steps/project/source/browse_files.rb | 63 +++++++++++++++---- .../projects/tree_controller_spec.rb | 36 +++++++++++ 19 files changed, 255 insertions(+), 50 deletions(-) create mode 100644 app/services/files/create_dir_service.rb create mode 100644 app/views/projects/blob/_new_dir.html.haml diff --git a/CHANGELOG b/CHANGELOG index 388fa2f8966..9d55622dd51 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) + - Add support for creating directories from Files page (Stan Hu) - Fix bug where transferring a project would result in stale commit links (Stan Hu) - Include full path of source and target branch names in New Merge Request page (Stan Hu) - Add user preference to view activities as default dashboard (Stan Hu) diff --git a/Gemfile b/Gemfile index 4938cbf8b80..5572fa0b6c8 100644 --- a/Gemfile +++ b/Gemfile @@ -47,7 +47,7 @@ gem "browser", '~> 1.0.0' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 7.2.17' +gem "gitlab_git", '~> 7.2.18' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes diff --git a/Gemfile.lock b/Gemfile.lock index 1dd56cd9c8c..5eaf3808425 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -279,7 +279,7 @@ GEM mime-types (~> 1.19) gitlab_emoji (0.1.1) gemojione (~> 2.0) - gitlab_git (7.2.17) + gitlab_git (7.2.18) activesupport (~> 4.0) charlock_holmes (~> 0.6) gitlab-linguist (~> 3.0) @@ -836,7 +836,7 @@ DEPENDENCIES gitlab-flowdock-git-hook (~> 1.0.1) gitlab-linguist (~> 3.0.1) gitlab_emoji (~> 0.1) - gitlab_git (~> 7.2.17) + gitlab_git (~> 7.2.18) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee index 3ab3ba66754..5b604adbbb1 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee +++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee @@ -47,6 +47,7 @@ class @BlobFileDropzone return this.on 'sending', (file, xhr, formData) -> + formData.append('new_branch', form.find('#new_branch').val()) formData.append('commit_message', form.find('#commit_message').val()) return diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 8776721d243..ae9b1384463 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -8,7 +8,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :require_non_empty_project, except: [:new, :create] before_action :authorize_download_code! - before_action :authorize_push_code!, only: [:destroy] + before_action :authorize_push_code!, only: [:destroy, :create] before_action :assign_blob_vars before_action :commit, except: [:new, :create] before_action :blob, except: [:new, :create] @@ -25,7 +25,7 @@ class Projects::BlobController < Projects::ApplicationController result = Files::CreateService.new(@project, current_user, @commit_params).execute if result[:status] == :success - flash[:notice] = "Your changes have been successfully committed" + flash[:notice] = "The changes have been successfully committed" respond_to do |format| format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } @@ -34,7 +34,7 @@ class Projects::BlobController < Projects::ApplicationController flash[:alert] = result[:message] respond_to do |format| format.html { render :new } - format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } } + format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } } end end end @@ -154,7 +154,7 @@ class Projects::BlobController < Projects::ApplicationController def editor_variables @current_branch = @ref - @target_branch = (sanitized_new_branch_name || @ref) + @target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref @file_path = if action_name.to_s == 'create' diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 92e4bc16d9d..7eaff1d61ee 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -1,10 +1,13 @@ # Controller for viewing a repository's file structure class Projects::TreeController < Projects::ApplicationController include ExtractsPath + include ActionView::Helpers::SanitizeHelper before_action :require_non_empty_project, except: [:new, :create] before_action :assign_ref_vars + before_action :assign_dir_vars, only: [:create_dir] before_action :authorize_download_code! + before_action :authorize_push_code!, only: [:create_dir] def show return not_found! unless @repository.commit(@ref) @@ -26,4 +29,38 @@ class Projects::TreeController < Projects::ApplicationController format.js { no_cache_headers } end end + + def create_dir + return not_found! unless @commit_params.values.all? + + begin + result = Files::CreateDirService.new(@project, current_user, @commit_params).execute + message = result[:message] + rescue => e + message = e.to_s + end + + if result && result[:status] == :success + flash[:notice] = "The directory has been successfully created" + respond_to do |format| + format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) } + end + else + flash[:alert] = message + respond_to do |format| + format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, @new_branch) } + end + end + end + + def assign_dir_vars + @new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref + @dir_name = File.join(@path, params[:dir_name]) + @commit_params = { + file_path: @dir_name, + current_branch: @ref, + target_branch: @new_branch, + commit_message: params[:commit_message], + } + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 2c5ab62d22c..8b51602bc23 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -373,11 +373,25 @@ class Repository @root_ref ||= raw_repository.root_ref end - def commit_file(user, path, content, message, branch) + def commit_dir(user, path, message, branch) commit_with_hooks(user, branch) do |ref| - path[0] = '' if path[0] == '/' + committer = user_to_committer(user) + options = {} + options[:committer] = committer + options[:author] = committer - committer = user_to_comitter(user) + options[:commit] = { + message: message, + branch: ref, + } + + raw_repository.mkdir(path, options) + end + end + + def commit_file(user, path, content, message, branch, update) + commit_with_hooks(user, branch) do |ref| + committer = user_to_committer(user) options = {} options[:committer] = committer options[:author] = committer @@ -388,7 +402,8 @@ class Repository options[:file] = { content: content, - path: path + path: path, + update: update } Gitlab::Git::Blob.commit(raw_repository, options) @@ -397,9 +412,7 @@ class Repository def remove_file(user, path, message, branch) commit_with_hooks(user, branch) do |ref| - path[0] = '' if path[0] == '/' - - committer = user_to_comitter(user) + committer = user_to_committer(user) options = {} options[:committer] = committer options[:author] = committer @@ -416,7 +429,7 @@ class Repository end end - def user_to_comitter(user) + def user_to_committer(user) { email: user.email, name: user.name, diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb new file mode 100644 index 00000000000..71272fb5707 --- /dev/null +++ b/app/services/files/create_dir_service.rb @@ -0,0 +1,9 @@ +require_relative "base_service" + +module Files + class CreateDirService < Files::BaseService + def commit + repository.commit_dir(current_user, @file_path, @commit_message, @target_branch) + end + end +end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index ffbb5993279..c8e3a910bba 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -3,7 +3,7 @@ require_relative "base_service" module Files class CreateService < Files::BaseService def commit - repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, false) end def validate diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb index a20903c6f02..1960dc7d949 100644 --- a/app/services/files/update_service.rb +++ b/app/services/files/update_service.rb @@ -3,7 +3,7 @@ require_relative "base_service" module Files class UpdateService < Files::BaseService def commit - repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch) + repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, true) end end end diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index fcc0f2a6a8d..7963af127e1 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -29,7 +29,7 @@ module MergeRequests private def commit - committer = repository.user_to_comitter(current_user) + committer = repository.user_to_committer(current_user) options = { message: commit_message, diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml new file mode 100644 index 00000000000..cb1567a2e68 --- /dev/null +++ b/app/views/projects/blob/_new_dir.html.haml @@ -0,0 +1,25 @@ +#modal-create-new-dir.modal + .modal-dialog + .modal-content + .modal-header + %a.close{href: "#", "data-dismiss" => "modal"} × + %h3.page-title Create New Directory + .modal-body + = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, id: 'dir-create-form', class: 'form-horizontal' do + .form-group + = label_tag :dir_name, 'Directory Name', class: 'control-label' + .col-sm-10 + = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control' + = render 'shared/commit_message_container', params: params, placeholder: '' + - unless @project.empty_repo? + .form-group + = label_tag :branch_name, 'Branch', class: 'control-label' + .col-sm-10 + = text_field_tag 'new_branch', @ref, class: "form-control" + .form-group + .col-sm-offset-2.col-sm-10 + = submit_tag "Create directory", class: 'btn btn-primary btn-create' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + +:coffeescript + disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create"); diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 1a1df127703..e27f1707527 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -4,9 +4,6 @@ .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × %h3.page-title #{title} - %p.light - From branch - %strong= @ref .modal-body = form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do .dropzone @@ -18,6 +15,12 @@ .dropzone-alerts{class: "alert alert-danger data", style: "display:none"} = render 'shared/commit_message_container', params: params, placeholder: placeholder + - unless @project.empty_repo? + .form-group.branch + = label_tag 'branch', class: 'control-label' do + Branch + .col-sm-10 + = text_field_tag 'new_branch', @ref, class: "form-control" .form-group .col-sm-offset-2.col-sm-10 = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 1950586b112..d7987e24ef3 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -2,12 +2,7 @@ = render "header_title" .gray-content-block.top-block - Create a new file or - = link_to 'upload', '#modal-upload-blob', - { class: 'upload-link', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} - an existing one - -= render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post + Create a new file .file-editor = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml index 367a87927d7..457f8a4a585 100644 --- a/app/views/projects/tree/_tree.html.haml +++ b/app/views/projects/tree/_tree.html.haml @@ -8,11 +8,25 @@ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - else = link_to title, '#' - - if current_user && can_push_branch?(@project, @ref) + - if allowed_tree_edit? %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'New file', id: 'new-file-link' do - %small - %i.fa.fa-plus + %span.dropdown + %a.dropdown-toggle.btn.btn-xs.add-to-tree{href: '#', "data-toggle" => "dropdown"} + = icon('plus') + %ul.dropdown-menu + %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do + = icon('pencil fw') + Create file + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do + = icon('file fw') + Upload file + %li.divider + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do + = icon('folder fw') + New directory %div#tree-content-holder.tree-content-holder.prepend-top-20 %table#tree-slider{class: "table_#{@hex_path} tree-table" } @@ -46,6 +60,10 @@ %div.tree_progress +- if allowed_tree_edit? + = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post + = render 'projects/blob/new_dir' + :javascript // Load last commit log for each file in tree $('#tree-slider').waitForImages(function() { diff --git a/config/routes.rb b/config/routes.rb index ccce40589e7..035b996dd7a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -463,6 +463,15 @@ Gitlab::Application.routes.draw do ) end + scope do + post( + '/create_dir/*id', + to: 'tree#create_dir', + constraints: { id: /.+/ }, + as: 'create_dir' + ) + end + scope do get( '/blame/*id', diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 58574166ef3..377c5e1a9a7 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -21,12 +21,12 @@ Feature: Project Source Browse Files Then I should see raw file content Scenario: I can create file - Given I click on "new file" link in repo + Given I click on "New file" link in repo Then I can see new file page @javascript Scenario: I can create and commit file - Given I click on "new file" link in repo + Given I click on "New file" link in repo And I edit code And I fill the new file name And I fill the commit message @@ -36,14 +36,13 @@ Feature: Project Source Browse Files @javascript Scenario: I can upload file and commit - Given I click on "new file" link in repo - Then I can see new file page - And I can see "upload an existing one" - And I click on "upload" + Given I click on "Upload file" link in repo And I upload a new text file And I fill the upload file commit message + And I fill the new branch name And I click on "Upload file" Then I can see the new text file + And I am redirected to the uploaded file on new branch And I can see the new commit message @javascript @@ -59,7 +58,7 @@ Feature: Project Source Browse Files @javascript Scenario: I can create and commit file and specify new branch - Given I click on "new file" link in repo + Given I click on "New file" link in repo And I edit code And I fill the new file name And I fill the commit message @@ -83,7 +82,7 @@ Feature: Project Source Browse Files @javascript Scenario: If I enter an illegal file name I see an error message - Given I click on "new file" link in repo + Given I click on "New file" link in repo And I fill the new file name with an illegal name And I edit code And I fill the commit message @@ -138,6 +137,24 @@ Feature: Project Source Browse Files Then I am on the ".gitignore" edit file page And I see a commit error message + @javascript + Scenario: I can create directory in repo + When I click on "New directory" link in repo + And I fill the new directory name + And I fill the commit message + And I fill the new branch name + And I click on "Create directory" + Then I am redirected to the new directory + + @javascript + Scenario: I attempt to create an existing directory + When I click on "New directory" link in repo + And I fill an existing directory name + And I fill the commit message + And I click on "Create directory" + Then I see "Unable to create directory" + And I am redirected to the root directory + @javascript Scenario: I can see editing preview Given I click on ".gitignore" file in repo diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index a1a49dd58a6..cb100ca0f54 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -71,7 +71,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I fill the new branch name' do - fill_in :new_branch, with: 'new_branch_name' + fill_in :new_branch, with: 'new_branch_name', visible: true end step 'I fill the new file name with an illegal name' do @@ -90,6 +90,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps click_button 'Commit Changes' end + step 'I click on "Create directory"' do + click_button 'Create directory' + end + step 'I click on "Remove"' do click_button 'Remove' end @@ -110,8 +114,27 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).to have_css '.line_holder.new' end - step 'I click on "new file" link in repo' do - click_link 'new-file-link' + step 'I click on "New file" link in repo' do + find('.add-to-tree').click + click_link 'Create file' + end + + step 'I click on "Upload file" link in repo' do + find('.add-to-tree').click + click_link 'Upload file' + end + + step 'I click on "New directory" link in repo' do + find('.add-to-tree').click + click_link 'New directory' + end + + step 'I fill the new directory name' do + fill_in :dir_name, with: new_dir_name + end + + step 'I fill an existing directory name' do + fill_in :dir_name, with: 'files' end step 'I can see new file page' do @@ -119,14 +142,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).to have_content "Commit message" end - step 'I can see "upload an existing one"' do - expect(page).to have_content "upload an existing one" - end - - step 'I click on "upload"' do - click_link 'upload' - end - step 'I click on "Upload file"' do click_button 'Upload file' end @@ -228,10 +243,30 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps @project.namespace, @project, 'new_branch_name/' + new_file_name)) end + step 'I am redirected to the uploaded file on new branch' do + expect(current_path).to eq(namespace_project_blob_path( + @project.namespace, @project, + 'new_branch_name/' + File.basename(test_text_file))) + end + + step 'I am redirected to the new directory' do + expect(current_path).to eq(namespace_project_tree_path( + @project.namespace, @project, 'new_branch_name/' + new_dir_name)) + end + + step 'I am redirected to the root directory' do + expect(current_path).to eq(namespace_project_tree_path( + @project.namespace, @project, 'master/')) + end + step "I don't see the permalink link" do expect(page).not_to have_link('permalink') end + step 'I see "Unable to create directory"' do + expect(page).to have_content('Directory already exists') + end + step 'I see a commit error message' do expect(page).to have_content('Your changes could not be committed') end @@ -287,6 +322,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps 'not_a_file.md' end + # Constant value that is a valid directory and + # not a directory present at root of the seed repository. + def new_dir_name + 'new_dir/subdir' + end + def drop_in_dropzone(file_path) # Generate a fake input selector page.execute_script <<-JS diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb index 53915856357..a474574c6e5 100644 --- a/spec/controllers/projects/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -88,4 +88,40 @@ describe Projects::TreeController do end end end + + describe '#create_dir' do + render_views + + before do + post(:create_dir, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: 'master', + dir_name: path, + new_branch: target_branch, + commit_message: 'Test commit message') + end + + context 'successful creation' do + let(:path) { 'files/new_dir'} + let(:target_branch) { 'master-test'} + + it 'redirects to the new directory' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}") + expect(flash[:notice]).to eq('The directory has been successfully created') + end + end + + context 'unsuccessful creation' do + let(:path) { 'README.md' } + let(:target_branch) { 'master'} + + it 'does not allow overwriting of existing files' do + expect(subject). + to redirect_to("/#{project.path_with_namespace}/blob/master") + expect(flash[:alert]).to eq('Directory already exists as a file') + end + end + end end From 6f6097713ca593c7a7177df0f06420b124d478aa Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 14:26:07 +0200 Subject: [PATCH 018/119] Move status badge helper to CI project settings for now Signed-off-by: Dmitriy Zaporozhets --- app/views/ci/projects/show.html.haml | 16 ---------------- app/views/projects/ci_settings/_form.html.haml | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml index 888b1ea41d5..f5244696c5d 100644 --- a/app/views/ci/projects/show.html.haml +++ b/app/views/ci/projects/show.html.haml @@ -19,22 +19,6 @@ %li.pull-right = link_to 'Go to project', project_path(gl_project), class: 'btn btn-sm' -- if @ref - %p - Paste build status image for #{@ref} with next link - = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do - Status Badge - .badge-codes-block.bs-callout.bs-callout-info.hide - %p - Status badge for - %span.label.label-info #{@ref} - branch - %div - %label Markdown: - = text_field_tag 'badge_md', markdown_badge_code(@project, @ref), readonly: true, class: 'form-control' - %label Html: - = text_field_tag 'badge_html', html_badge_code(@project, @ref), readonly: true, class: 'form-control' - diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml index 9f891f557a9..34125550206 100644 --- a/app/views/projects/ci_settings/_form.html.haml +++ b/app/views/projects/ci_settings/_form.html.haml @@ -8,6 +8,22 @@ Edit your #{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@ci_project)} +- if @repository + %p + Paste build status image for #{@repository.root_ref} with next link + = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do + Status Badge + .badge-codes-block.bs-callout.bs-callout-info.hide + %p + Status badge for + %span.label.label-info #{@ref} + branch + %div + %label Markdown: + = text_field_tag 'badge_md', markdown_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control' + %label Html: + = text_field_tag 'badge_html', html_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control' + = nested_form_for @ci_project, url: namespace_project_ci_settings_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| - if @ci_project.errors.any? #error_explanation From c0b79a751c8cbf676900956b14b70edd5dca5010 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 14:35:54 +0200 Subject: [PATCH 019/119] Move no runners alert to project ci settings Signed-off-by: Dmitriy Zaporozhets --- app/views/ci/projects/_info.html.haml | 2 -- .../projects => projects/ci_settings}/_no_runners.html.haml | 2 +- app/views/projects/ci_settings/edit.html.haml | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) rename app/views/{ci/projects => projects/ci_settings}/_no_runners.html.haml (87%) diff --git a/app/views/ci/projects/_info.html.haml b/app/views/ci/projects/_info.html.haml index 1888e1bde93..e69de29bb2d 100644 --- a/app/views/ci/projects/_info.html.haml +++ b/app/views/ci/projects/_info.html.haml @@ -1,2 +0,0 @@ -- if no_runners_for_project?(@project) - = render 'no_runners' diff --git a/app/views/ci/projects/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml similarity index 87% rename from app/views/ci/projects/_no_runners.html.haml rename to app/views/projects/ci_settings/_no_runners.html.haml index c0a296fb17d..33038c52978 100644 --- a/app/views/ci/projects/_no_runners.html.haml +++ b/app/views/projects/ci_settings/_no_runners.html.haml @@ -4,5 +4,5 @@ %br You can add Specific runner for this project on Runners page - - if current_user.is_admin + - if current_user.admin or add Shared runner for whole application in admin are. diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml index e9040fe4337..eedf484bf00 100644 --- a/app/views/projects/ci_settings/edit.html.haml +++ b/app/views/projects/ci_settings/edit.html.haml @@ -6,6 +6,9 @@ yaml file which is based on your old jobs. Put this file to the root of your project and name it .gitlab-ci.yml +- if no_runners_for_project?(@ci_project) + = render 'no_runners' + = render 'form' - if @ci_project.generated_yaml_config From e52de6771f6bf9629fc85213b94ec60721354e80 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 14:43:58 +0200 Subject: [PATCH 020/119] Remove Continuous Integration from project menu Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 23 ++-------- app/views/ci/projects/_info.html.haml | 0 app/views/ci/projects/show.html.haml | 44 ------------------- app/views/layouts/ci/_nav_project.html.haml | 6 --- app/views/layouts/nav/_project.html.haml | 7 --- .../layouts/nav/_project_settings.html.haml | 10 +++++ 6 files changed, 13 insertions(+), 77 deletions(-) delete mode 100644 app/views/ci/projects/_info.html.haml delete mode 100644 app/views/ci/projects/show.html.haml diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index adb913a783f..5484ae643a6 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,27 +1,10 @@ module Ci class ProjectsController < Ci::ApplicationController - before_action :authenticate_user!, except: [:build, :badge, :show] - before_action :authenticate_public_page!, only: :show - before_action :project, only: [:build, :show, :badge, :toggle_shared_runners, :dumped_yaml] - before_action :authorize_access_project!, except: [:build, :badge, :show, :new] + before_action :authenticate_user!, except: [:build, :badge] + before_action :authorize_access_project!, except: [:badge] before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] - before_action :authenticate_token!, only: [:build] before_action :no_cache, only: [:badge] - protect_from_forgery except: :build - - layout 'ci/project', except: [:index] - - def show - @ref = params[:ref] - - @commits = @project.commits.reverse_order - if @ref - # unscope is required, because of default_scope defined in Ci::Build - builds = @project.builds.unscope(:select, :order).where(ref: @ref).select(:commit_id).distinct - @commits = @commits.where(id: builds) - end - @commits = @commits.page(params[:page]).per(20) - end + protect_from_forgery # Project status badge # Image with build status for sha or ref diff --git a/app/views/ci/projects/_info.html.haml b/app/views/ci/projects/_info.html.haml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml deleted file mode 100644 index f5244696c5d..00000000000 --- a/app/views/ci/projects/show.html.haml +++ /dev/null @@ -1,44 +0,0 @@ -= render 'ci/shared/guide' unless @project.setup_finished? - -- if current_user && can?(current_user, :manage_project, gl_project) && !@project.any_runners? - .alert.alert-danger - Builds for this project wont be served unless you configure runners on - = link_to "Runners page", runners_path(@project.gl_project) - -%ul.nav.nav-tabs.append-bottom-20 - %li{class: ref_tab_class} - = link_to 'All commits', ci_project_path(@project) - - @project.tracked_refs.each do |ref| - %li{class: ref_tab_class(ref)} - = link_to ref, ci_project_path(@project, ref: ref) - - - if @ref && !@project.tracked_refs.include?(@ref) - %li{class: 'active'} - = link_to @ref, ci_project_path(@project, ref: @ref) - - %li.pull-right - = link_to 'Go to project', project_path(gl_project), class: 'btn btn-sm' - - - - -%table.table.builds - %thead - %tr - %th Status - %th Commit - %th Message - %th Branch - %th Total duration - %th Finished at - - if @project.coverage_enabled? - %th Coverage - - = render @commits - -= paginate @commits - -- if @commits.empty? - .bs-callout - %h4 No commits yet - diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index 545abc23d99..db631f24656 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -5,12 +5,6 @@ %span Back to project %li.separate-item - = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do - = link_to ci_project_path(@project) do - = icon('list-alt fw') - %span - Commits - %span.count= @project.commits.count = nav_link path: ['services#index', 'services#edit'] do = link_to ci_project_services_path(@project) do = icon('share fw') diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index a218ec7486c..8ce46d4865b 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -76,13 +76,6 @@ Merge Requests %span.count.merge_counter= @project.merge_requests.opened.count - - if @project.gitlab_ci? - = nav_link(controller: [:ci, :project]) do - = link_to ci_project_path(@project.gitlab_ci_project), title: 'Continuous Integration', data: {placement: 'right'} do - = icon('building fw') - %span - Continuous Integration - - if project_nav_tab? :settings = nav_link(controller: [:project_members, :teams]) do = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 9279a846623..f618a18870b 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -60,3 +60,13 @@ = icon('building fw') %span CI Settings + = nav_link path: ['ci/services#index', 'ci/services#edit'] do + = link_to ci_project_services_path(@project.gitlab_ci_project) do + = icon('share fw') + %span + CI Services + = nav_link path: 'events#index' do + = link_to ci_project_events_path(@project.gitlab_ci_project) do + = icon('book fw') + %span + CI Events From 7d220c1e7936b39affe1d819b7731c608795c7e9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 14:56:21 +0200 Subject: [PATCH 021/119] Remove test for removed page and add menu highlight for build page Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/nav/_project.html.haml | 2 +- spec/features/ci/projects_spec.rb | 20 -------------------- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 spec/features/ci/projects_spec.rb diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 8ce46d4865b..e4c285d8023 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -32,7 +32,7 @@ Files - if project_nav_tab? :commits - = nav_link(controller: %w(commit commits compare repositories tags branches)) do + = nav_link(controller: %w(commit commits compare repositories tags branches builds)) do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do = icon('history fw') %span diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb deleted file mode 100644 index c633acf85fb..00000000000 --- a/spec/features/ci/projects_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Projects" do - let(:user) { create(:user) } - - before do - login_as(user) - @project = FactoryGirl.create :ci_project, name: "GitLab / gitlab-shell" - @project.gl_project.team << [user, :master] - end - - describe "GET /ci/projects/:id" do - before do - visit ci_project_path(@project) - end - - it { expect(page).to have_content @project.name } - it { expect(page).to have_content 'All commits' } - end -end From 3fa2cb93353720e1b70e01ec9e664ebf54d1fc29 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 14:59:37 +0200 Subject: [PATCH 022/119] Remove unused JS Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/ci/Chart.min.js | 39 -------------------- app/assets/javascripts/ci/projects.js.coffee | 3 -- 2 files changed, 42 deletions(-) delete mode 100644 app/assets/javascripts/ci/Chart.min.js diff --git a/app/assets/javascripts/ci/Chart.min.js b/app/assets/javascripts/ci/Chart.min.js deleted file mode 100644 index ab635881087..00000000000 --- a/app/assets/javascripts/ci/Chart.min.js +++ /dev/null @@ -1,39 +0,0 @@ -var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a= -Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);dc;)a=dc?c:!isNaN(parseFloat(b))&& -isFinite(b)&&a)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c? -b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)? -0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1== -a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);ea?-0.5*e*Math.pow(2,10* -(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)* -a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0, -scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce", -animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)", -scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a, -c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1, -onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0, -pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'", -scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]); -d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;fe&&(e=a[f].value),a[f].valuel&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE; -h=Number.MAX_VALUE;for(f=0;fe&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;gt?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0t?e:t;q/a.labels.lengthe&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]< -h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;ed?h:d;d+=10}r=q-d-t;m= -Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0 $('.badge-codes-block').toggleClass("hide") return false - -$(document).on 'click', '.sync-now', -> - $(this).find('i').addClass('fa-spin') From 1e06cabf4a8fa4d4c7acb9898682a5b4b41a9f58 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 15:24:32 +0200 Subject: [PATCH 023/119] Remove Ci::Commit and Ci::Build controllers Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/builds_controller.rb | 52 ------------------- app/controllers/ci/commits_controller.rb | 32 ------------ app/controllers/projects/builds_controller.rb | 30 +++++++++++ app/controllers/projects/commit_controller.rb | 8 +++ app/views/projects/builds/_build.html.haml | 4 +- app/views/projects/builds/show.html.haml | 4 +- app/views/projects/commit/ci.html.haml | 2 +- config/routes.rb | 24 +++------ spec/features/builds_spec.rb | 21 +++++++- spec/features/ci/builds_spec.rb | 31 ----------- 10 files changed, 71 insertions(+), 137 deletions(-) delete mode 100644 app/controllers/ci/builds_controller.rb delete mode 100644 app/controllers/ci/commits_controller.rb delete mode 100644 spec/features/ci/builds_spec.rb diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb deleted file mode 100644 index b0b8b62fced..00000000000 --- a/app/controllers/ci/builds_controller.rb +++ /dev/null @@ -1,52 +0,0 @@ -module Ci - class BuildsController < Ci::ApplicationController - before_action :authenticate_user!, except: [:status] - before_action :project - before_action :authorize_access_project!, except: [:status] - before_action :authorize_manage_project!, except: [:status, :retry, :cancel] - before_action :authorize_manage_builds!, only: [:retry, :cancel] - before_action :build - - def retry - if @build.commands.blank? - return page_404 - end - - build = Ci::Build.retry(@build) - - if params[:return_to] - redirect_to URI.parse(params[:return_to]).path - else - redirect_to build_path(build) - end - end - - def status - render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) - end - - def cancel - @build.cancel - - redirect_to build_path(@build) - end - - protected - - def project - @project = Ci::Project.find(params[:project_id]) - end - - def build - @build ||= project.builds.unscoped.find_by!(id: params[:id]) - end - - def commit_by_sha - @project.commits.find_by(sha: params[:id]) - end - - def build_path(build) - namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) - end - end -end diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb deleted file mode 100644 index 7e6705c9702..00000000000 --- a/app/controllers/ci/commits_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -module Ci - class CommitsController < Ci::ApplicationController - before_action :authenticate_user!, except: [:status, :show] - before_action :authenticate_public_page!, only: :show - before_action :project - before_action :authorize_access_project!, except: [:status, :show, :cancel] - before_action :authorize_manage_builds!, only: [:cancel] - - def status - commit = Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id]) - render json: commit.to_json(only: [:id, :sha], methods: [:status, :coverage]) - rescue ActiveRecord::RecordNotFound - render json: { status: "not_found" } - end - - def cancel - commit.builds.running_or_pending.each(&:cancel) - - redirect_to namespace_project_commit_path(commit.gl_project.namespace, commit.gl_project, commit.sha) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - - def commit - @commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id]) - end - end -end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 76c7f31f61b..4e4ac6689d3 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -2,6 +2,8 @@ class Projects::BuildsController < Projects::ApplicationController before_action :ci_project before_action :build + before_action :authorize_admin_project!, except: [:show, :status] + layout "project" def show @@ -17,9 +19,37 @@ class Projects::BuildsController < Projects::ApplicationController end end + def retry + if @build.commands.blank? + return page_404 + end + + build = Ci::Build.retry(@build) + + if params[:return_to] + redirect_to URI.parse(params[:return_to]).path + else + redirect_to build_path(build) + end + end + + def status + render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) + end + + def cancel + @build.cancel + + redirect_to build_path(@build) + end + private def build @build ||= ci_project.builds.unscoped.find_by!(id: params[:id]) end + + def build_path(build) + namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) + end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 1938c63c10c..c08a90bddf0 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -38,6 +38,14 @@ class Projects::CommitController < Projects::ApplicationController @ci_project = @project.gitlab_ci_project end + def cancel_builds + @ci_commit = @project.ci_commit(@commit.sha) + @ci_commit.builds.running_or_pending.each(&:cancel) + + redirect_to namespace_project_commit_path(project.namespace, project, commit.sha) + end + + def branches @branches = @project.repository.branch_names_contains(commit.id) @tags = @project.repository.tag_names_contains(commit.id) diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml index 21c543b38dd..65fd9413b60 100644 --- a/app/views/projects/builds/_build.html.haml +++ b/app/views/projects/builds/_build.html.haml @@ -43,8 +43,8 @@ - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project) .pull-right - if build.active? - = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do + = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), title: 'Cancel build' do %i.fa.fa-remove.cred - elsif build.commands.present? - = link_to retry_ci_project_build_path(build.project, build, return_to: request.original_url), method: :post, title: 'Retry build' do + = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), method: :post, title: 'Retry build' do %i.fa.fa-repeat diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 93cd4dcfd93..b561078e8c7 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -72,9 +72,9 @@ - if current_user && can?(current_user, :manage_builds, @project) .pull-right - if @build.active? - = link_to "Cancel", cancel_ci_project_build_path(@ci_project, @build), class: 'btn btn-sm btn-danger' + = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger' - elsif @build.commands.present? - = link_to "Retry", retry_ci_project_build_path(@ci_project, @build), class: 'btn btn-sm btn-primary', method: :post + = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post - if @build.duration %p diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index f4382e88046..26ab38445c2 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -6,7 +6,7 @@ - if @ci_project && current_user && can?(current_user, :manage_builds, @project) .pull-right - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel", cancel_ci_project_commits_path(@ci_project, @ci_commit), class: 'btn btn-sm btn-danger' + = link_to "Cancel", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-sm btn-danger' - if @ci_commit.yaml_errors.present? diff --git a/config/routes.rb b/config/routes.rb index ccce40589e7..4fb779e297c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,21 +28,6 @@ Gitlab::Application.routes.draw do end end - resources :commits, only: [] do - member do - get :status - get :cancel - end - end - - resources :builds, only: [] do - member do - get :cancel - get :status - post :retry - end - end - resources :runner_projects, only: [:create, :destroy] resources :events, only: [:index] @@ -486,6 +471,7 @@ Gitlab::Application.routes.draw do member do get :branches get :ci + post :cancel_builds end end @@ -590,7 +576,13 @@ Gitlab::Application.routes.draw do end end - resources :builds, only: [:show] + resources :builds, only: [:show] do + member do + get :cancel + get :status + post :retry + end + end resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do member do diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index d0d60491b65..924047a0d8f 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' describe "Builds" do - before do login_as(:user) @commit = FactoryGirl.create :ci_commit @@ -19,4 +18,24 @@ describe "Builds" do it { expect(page).to have_content @commit.git_commit_message } it { expect(page).to have_content @commit.git_author_name } end + + describe "GET /:project/builds/:id/cancel" do + before do + @build.run! + visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + end + + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content 'Retry' } + end + + describe "POST /:project/builds/:id/retry" do + before do + visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + click_link 'Retry' + end + + it { expect(page).to have_content 'pending' } + it { expect(page).to have_content 'Cancel' } + end end diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb deleted file mode 100644 index aa0df59c04d..00000000000 --- a/spec/features/ci/builds_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - before do - login_as(:user) - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - @gl_project = @commit.project.gl_project - @gl_project.team << [@user, :master] - end - - describe "GET /:project/builds/:id/cancel" do - before do - @build.run! - visit cancel_ci_project_build_path(@commit.project, @build) - end - - it { expect(page).to have_content 'canceled' } - it { expect(page).to have_content 'Retry' } - end - - describe "POST /:project/builds/:id/retry" do - before do - visit cancel_ci_project_build_path(@commit.project, @build) - click_link 'Retry' - end - - it { expect(page).to have_content 'pending' } - it { expect(page).to have_content 'Cancel' } - end -end From 75bb1087df785e75fb343a9e8721e8d636f7ce81 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 6 Oct 2015 21:08:27 -0700 Subject: [PATCH 024/119] Allow removing of project without confirmation when JavaScript is disabled Closes #2485 --- CHANGELOG | 1 + app/views/projects/edit.html.haml | 4 ++-- spec/features/projects_spec.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9d55622dd51..77e5ad26816 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - Add support for creating directories from Files page (Stan Hu) + - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) - Fix bug where transferring a project would result in stale commit links (Stan Hu) - Include full path of source and target branch names in New Merge Request page (Stan Hu) - Add user preference to view activities as default dashboard (Stan Hu) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 90dce739992..1882a82fba5 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -193,13 +193,13 @@ .panel.panel-default.panel.panel-danger .panel-heading Remove project .panel-body - = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, html: { class: 'form-horizontal'}) do + = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do %p Removing the project will delete its repository and all related resources including issues, merge requests etc. %br %strong Removed projects cannot be restored! - = link_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } + = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } - else .nothing-here-block Only project owner can remove a project diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index a362c54b9ad..aac93b17a38 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -50,7 +50,7 @@ feature 'Project', feature: true do end def remove_project - click_link "Remove project" + click_button "Remove project" fill_in 'confirm_name_input', with: project.path click_button 'Confirm' end From a30b68fe1ded96c5aafe4348d1afec269354c469 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 16:20:31 +0200 Subject: [PATCH 025/119] Move CI services to project settings area Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/services_controller.rb | 59 ------------------- .../projects/ci_services_controller.rb | 49 +++++++++++++++ app/views/layouts/ci/_nav_project.html.haml | 5 -- .../layouts/nav/_project_settings.html.haml | 4 +- .../ci_services}/_form.html.haml | 7 +-- .../ci_services}/edit.html.haml | 0 .../ci_services}/index.html.haml | 2 +- config/routes.rb | 12 ++-- .../controllers/ci/commits_controller_spec.rb | 23 -------- 10 files changed, 61 insertions(+), 101 deletions(-) delete mode 100644 app/controllers/ci/services_controller.rb create mode 100644 app/controllers/projects/ci_services_controller.rb rename app/views/{ci/services => projects/ci_services}/_form.html.haml (80%) rename app/views/{ci/services => projects/ci_services}/edit.html.haml (100%) rename app/views/{ci/services => projects/ci_services}/index.html.haml (81%) delete mode 100644 spec/controllers/ci/commits_controller_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 388fa2f8966..5ea1deea68b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -33,6 +33,7 @@ v 8.1.0 (unreleased) - Add user preference to change layout width (Peter Göbel) - Use commit status in merge request widget as preffered source of CI status - Integrate CI commit and build pages into project pages + - Move CI services page to project settings area v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/controllers/ci/services_controller.rb b/app/controllers/ci/services_controller.rb deleted file mode 100644 index 52c96a34ce8..00000000000 --- a/app/controllers/ci/services_controller.rb +++ /dev/null @@ -1,59 +0,0 @@ -module Ci - class ServicesController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_access_project! - before_action :authorize_manage_project! - before_action :service, only: [:edit, :update, :test] - - respond_to :html - - layout 'ci/project' - - def index - @project.build_missing_services - @services = @project.services.reload - end - - def edit - end - - def update - if @service.update_attributes(service_params) - redirect_to edit_ci_project_service_path(@project, @service.to_param) - else - render 'edit' - end - end - - def test - last_build = @project.builds.last - - if @service.execute(last_build) - message = { notice: 'We successfully tested the service' } - else - message = { alert: 'We tried to test the service but error occurred' } - end - - redirect_to :back, message - end - - private - - def project - @project = Ci::Project.find(params[:project_id]) - end - - def service - @service ||= @project.services.find { |service| service.to_param == params[:id] } - end - - def service_params - params.require(:service).permit( - :type, :active, :webhook, :notify_only_broken_builds, - :email_recipients, :email_only_broken_builds, :email_add_pusher, - :hipchat_token, :hipchat_room, :hipchat_server - ) - end - end -end diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb new file mode 100644 index 00000000000..6d2756eba3d --- /dev/null +++ b/app/controllers/projects/ci_services_controller.rb @@ -0,0 +1,49 @@ +class Projects::CiServicesController < Projects::ApplicationController + before_action :ci_project + before_action :authorize_admin_project! + + layout "project_settings" + + def index + @ci_project.build_missing_services + @services = @ci_project.services.reload + end + + def edit + service + end + + def update + if @service.update_attributes(service_params) + redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param) + else + render 'edit' + end + end + + def test + last_build = @project.builds.last + + if @service.execute(last_build) + message = { notice: 'We successfully tested the service' } + else + message = { alert: 'We tried to test the service but error occurred' } + end + + redirect_to :back, message + end + + private + + def service + @service ||= @ci_project.services.find { |service| service.to_param == params[:id] } + end + + def service_params + params.require(:service).permit( + :type, :active, :webhook, :notify_only_broken_builds, + :email_recipients, :email_only_broken_builds, :email_add_pusher, + :hipchat_token, :hipchat_room, :hipchat_server + ) + end +end diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml index db631f24656..f094edbfa87 100644 --- a/app/views/layouts/ci/_nav_project.html.haml +++ b/app/views/layouts/ci/_nav_project.html.haml @@ -5,11 +5,6 @@ %span Back to project %li.separate-item - = nav_link path: ['services#index', 'services#edit'] do - = link_to ci_project_services_path(@project) do - = icon('share fw') - %span - Services = nav_link path: 'events#index' do = link_to ci_project_events_path(@project) do = icon('book fw') diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index f618a18870b..954dbe5d2b9 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -60,8 +60,8 @@ = icon('building fw') %span CI Settings - = nav_link path: ['ci/services#index', 'ci/services#edit'] do - = link_to ci_project_services_path(@project.gitlab_ci_project) do + = nav_link controller: 'ci_services' do + = link_to namespace_project_ci_services_path(@project.namespace, @project) do = icon('share fw') %span CI Services diff --git a/app/views/ci/services/_form.html.haml b/app/views/projects/ci_services/_form.html.haml similarity index 80% rename from app/views/ci/services/_form.html.haml rename to app/views/projects/ci_services/_form.html.haml index 9110aaa0528..397832e56db 100644 --- a/app/views/ci/services/_form.html.haml +++ b/app/views/projects/ci_services/_form.html.haml @@ -4,13 +4,10 @@ %p= @service.description -.back-link - = link_to ci_project_services_path(@project) do - ← to services %hr -= form_for(@service, as: :service, url: ci_project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| += form_for(@service, as: :service, url: namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f| - if @service.errors.any? .alert.alert-danger %ul @@ -54,4 +51,4 @@ = f.submit 'Save', class: 'btn btn-save'   - if @service.valid? && @service.activated? && @service.can_test? - = link_to 'Test settings', test_ci_project_service_path(@project, @service.to_param), class: 'btn' + = link_to 'Test settings', test_namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), class: 'btn' diff --git a/app/views/ci/services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml similarity index 100% rename from app/views/ci/services/edit.html.haml rename to app/views/projects/ci_services/edit.html.haml diff --git a/app/views/ci/services/index.html.haml b/app/views/projects/ci_services/index.html.haml similarity index 81% rename from app/views/ci/services/index.html.haml rename to app/views/projects/ci_services/index.html.haml index 37e5723b541..c78b21884a3 100644 --- a/app/views/ci/services/index.html.haml +++ b/app/views/projects/ci_services/index.html.haml @@ -13,7 +13,7 @@ %td = boolean_to_icon service.activated? %td - = link_to edit_ci_project_service_path(@project, service.to_param) do + = link_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) do %strong= service.title %td = service.description diff --git a/config/routes.rb b/config/routes.rb index 4fb779e297c..e91b09de9b0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -22,12 +22,6 @@ Gitlab::Application.routes.draw do get :dumped_yaml end - resources :services, only: [:index, :edit, :update] do - member do - get :test - end - end - resources :runner_projects, only: [:create, :destroy] resources :events, only: [:index] @@ -576,6 +570,12 @@ Gitlab::Application.routes.draw do end end + resources :ci_services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do + member do + get :test + end + end + resources :builds, only: [:show] do member do get :cancel diff --git a/spec/controllers/ci/commits_controller_spec.rb b/spec/controllers/ci/commits_controller_spec.rb deleted file mode 100644 index cc39ce7687c..00000000000 --- a/spec/controllers/ci/commits_controller_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require "spec_helper" - -describe Ci::CommitsController do - describe "GET /status" do - it "returns status of commit" do - commit = FactoryGirl.create :ci_commit - get :status, id: commit.sha, ref_id: commit.ref, project_id: commit.project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "pending" - end - - it "returns not_found status" do - commit = FactoryGirl.create :ci_commit - get :status, id: commit.sha, ref_id: "deploy", project_id: commit.project.id - - expect(response).to be_success - expect(response.code).to eq('200') - JSON.parse(response.body)["status"] == "not_found" - end - end -end From dfbbc80611fbdafe6f5ed809f98fc63987d104a6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 6 Oct 2015 09:57:13 -0700 Subject: [PATCH 026/119] Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters Closes #2619 Closes https://github.com/gitlabhq/gitlabhq/issues/9631 --- CHANGELOG | 1 + app/finders/issuable_finder.rb | 28 +++++++++++++++------ app/helpers/labels_helper.rb | 4 ++- app/models/label.rb | 3 +++ app/views/shared/issuable/_filter.html.haml | 4 +-- spec/finders/issues_finder_spec.rb | 20 +++++++++++++++ spec/helpers/labels_helper_spec.rb | 5 ++++ 7 files changed, 55 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9d55622dd51..43fc8b1d3b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) - Add support for creating directories from Files page (Stan Hu) + - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu) - Fix bug where transferring a project would result in stale commit links (Stan Hu) - Include full path of source and target branch names in New Merge Request page (Stan Hu) - Add user preference to view activities as default dashboard (Stan Hu) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 6aa16673d63..97c7e74c294 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -72,11 +72,15 @@ class IssuableFinder params[:milestone_title].present? end + def no_milestones? + milestones? && params[:milestone_title] == Milestone::None.title + end + def milestones return @milestones if defined?(@milestones) @milestones = - if milestones? && params[:milestone_title] != Milestone::None.title + if milestones? Milestone.where(title: params[:milestone_title]) else nil @@ -183,7 +187,11 @@ class IssuableFinder def by_milestone(items) if milestones? - items = items.where(milestone_id: milestones.try(:pluck, :id)) + if no_milestones? + items = items.where(milestone_id: [-1, nil]) + else + items = items.where(milestone_id: milestones.try(:pluck, :id)) + end end items @@ -207,13 +215,19 @@ class IssuableFinder def by_label(items) if params[:label_name].present? - label_names = params[:label_name].split(",") + if params[:label_name] == Label::None.title + item_ids = LabelLink.where(target_type: klass.name).pluck(:target_id) - item_ids = LabelLink.joins(:label). - where('labels.title in (?)', label_names). - where(target_type: klass.name).pluck(:target_id) + items = items.where('id NOT IN (?)', item_ids) + else + label_names = params[:label_name].split(",") - items = items.where(id: item_ids) + item_ids = LabelLink.joins(:label). + where('labels.title in (?)', label_names). + where(target_type: klass.name).pluck(:target_id) + + items = items.where(id: item_ids) + end end items diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 8036303851b..662ace367b9 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -93,7 +93,9 @@ module LabelsHelper end def project_labels_options(project) - options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name]) + labels = project.labels.to_a + labels.unshift(Label::None) + options_from_collection_for_select(labels, 'name', 'name', params[:label_name]) end # Required for Gitlab::Markdown::LabelReferenceFilter diff --git a/app/models/label.rb b/app/models/label.rb index 4a22bd53400..14b544b3756 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -12,6 +12,9 @@ class Label < ActiveRecord::Base include Referable + # Represents a "No Label" state used for filtering Issues and Merge + # Requests that have no label assigned. + None = Struct.new(:title, :name).new('No Label', 'No Label') DEFAULT_COLOR = '#428BCA' diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 8f16773077e..6e6d497c1d2 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -39,13 +39,13 @@ .filter-item.inline.milestone-filter = select_tag('milestone_title', projects_milestones_options, - class: 'select2 trigger-submit', include_blank: true, + class: 'select2 trigger-submit', include_blank: 'Any', data: {placeholder: 'Milestone'}) - if @project .filter-item.inline.labels-filter = select_tag('label_name', project_labels_options(@project), - class: 'select2 trigger-submit', include_blank: true, + class: 'select2 trigger-submit', include_blank: 'Any', data: {placeholder: 'Label'}) .pull-right diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index db20b23f87d..b1648055462 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -6,9 +6,11 @@ describe IssuesFinder do let(:project1) { create(:project) } let(:project2) { create(:project) } let(:milestone) { create(:milestone, project: project1) } + let(:label) { create(:label, project: project2) } let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) } let(:issue2) { create(:issue, author: user, assignee: user, project: project2) } let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) } + let!(:label_link) { create(:label_link, label: label, target: issue2) } before do project1.team << [user, :master] @@ -48,6 +50,24 @@ describe IssuesFinder do expect(issues).to eq([issue1]) end + it 'should filter by no milestone id' do + params = { scope: "all", milestone_title: Milestone::None.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to match_array([issue2, issue3]) + end + + it 'should filter by label name' do + params = { scope: "all", label_name: label.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to eq([issue2]) + end + + it 'should filter by no label name' do + params = { scope: "all", label_name: Label::None.title, state: 'opened' } + issues = IssuesFinder.new(user, params).execute + expect(issues).to match_array([issue1, issue3]) + end + it 'should be empty for unauthorized user' do params = { scope: "all", state: 'opened' } issues = IssuesFinder.new(nil, params).execute diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 0c8d06b7059..fb70a36dc02 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -14,6 +14,11 @@ describe LabelsHelper do expect(label).not_to receive(:project) link_to_label(label) end + + it 'includes option for "No Label"' do + result = project_labels_options(project) + expect(result).to include('No Label') + end end context 'without @project set' do From 7ad4521f66b578c0aecf9afb09194626a4c2aa6a Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 5 Oct 2015 21:18:21 +0200 Subject: [PATCH 027/119] Fixes to dropdown, font colors for new project and merge request --- app/assets/stylesheets/generic/buttons.scss | 311 ++++++++++-------- app/assets/stylesheets/generic/common.scss | 8 + app/assets/stylesheets/generic/forms.scss | 24 +- app/assets/stylesheets/generic/issue_box.scss | 2 +- app/assets/stylesheets/generic/selects.scss | 42 ++- app/assets/stylesheets/generic/timeline.scss | 4 +- app/assets/stylesheets/pages/issuable.scss | 9 +- .../stylesheets/pages/merge_requests.scss | 23 +- app/assets/stylesheets/pages/note_form.scss | 8 +- app/assets/stylesheets/pages/notes.scss | 2 +- app/assets/stylesheets/pages/projects.scss | 37 ++- .../projects/merge_requests/_show.html.haml | 2 +- app/views/projects/new.html.haml | 20 +- app/views/projects/notes/_form.html.haml | 2 +- 14 files changed, 303 insertions(+), 191 deletions(-) diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index cf76f538e01..50c3de089f3 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -1,8 +1,182 @@ body { text-rendering: geometricPrecision; } + +@mixin btn-default { + @include border-radius(2px); + + border-width: 1px; + border-style: solid; + text-transform: uppercase; + font-size: 13px; + font-weight: 600; + line-height: 18px; + padding: 11px 16px; + letter-spacing: .4px; + + &:hover { + border-width: 1px; + border-style: solid; + } + + &:focus { + border-width: 1px; + border-style: solid; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + border-width: 1px; + border-style: solid; + } +} + +@mixin btn-middle { + @include border-radius(2px); + + border-width: 1px; + border-style: solid; + text-transform: uppercase; + font-size: 13px; + font-weight: 600; + line-height: 18px; + padding: 11px 24px; + letter-spacing: .4px; + + &:hover { + border-width: 1px; + border-style: solid; + } + + &:focus { + border-width: 1px; + border-style: solid; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + border-width: 1px; + border-style: solid; + } +} + + +@mixin btn-green { + background-color: #28b061; + border: 1px solid #26a65c; + color: #fff; + + &:hover { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:focus { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:active { + @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); + + background-color: #23a158 !important; + border: 1px solid #229954 !important; + color: #fff !important; + } +} + +/*Butons*/ + +@mixin btn-project { + background-color: #f0f2f5; + border-color: #dce0e5; + color: #313236; + + &:hover { + border-color:#dce0e5; + background-color: #ebeef2; + color: #313236; + } + + &:focus { + border-color: #dce0e5; + background-color: #ebeef2; + color: #313236; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border-color: #c6cacf !important; + background-color: #e4e7ed !important; + } +} + +@mixin btn-light { + background-color: #fff; + border-color: #dce0e5; + color: #313236; + + &:hover { + border-color:#dce0e5; + background-color: #f0f2f5; + color: #313236; + } + + &:focus { + border-color: #dce0e5; + background-color: #f0f2f5; + color: #313236; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border-color: #c6cacf !important; + background-color: #e4e7ed !important; + } +} + +@mixin btn-remove { + background-color: #f72e60; + border-color: #ee295a; + + &:hover { + background-color: #e82757; + border-color: #e32555; + } + + &:focus { + background-color: #e82757; + border-color: #e32555; + } + + &:active { + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + background-color: #d42450 !important; + border-color: #e12554 !important; + } + +} + + +.btn-info { + @include btn-default; + @include btn-project; +} + +.btn-success { + @include btn-default; + @include btn-green; + +} + .btn { - @extend .btn-default; + @include btn-default; &.btn-new { @extend .btn-success; @@ -90,139 +264,4 @@ body { color: #fff; } } -} - -@mixin btn-info { - @include border-radius(2px); - - border-width: 1px; - border-style: solid; - text-transform: uppercase; - font-size: 13px; - font-weight: 600; - line-height: 18px; - padding: 11px 16px; - letter-spacing: .4px; - - &:hover { - border-width: 1px; - border-style: solid; - } - - &:focus { - border-width: 1px; - border-style: solid; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border-width: 1px; - border-style: solid; - } -} - -@mixin btn-middle { - @include border-radius(2px); - - border-width: 1px; - border-style: solid; - text-transform: uppercase; - font-size: 13px; - font-weight: 600; - line-height: 18px; - padding: 11px 24px; - letter-spacing: .4px; - - &:hover { - border-width: 1px; - border-style: solid; - } - - &:focus { - border-width: 1px; - border-style: solid; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border-width: 1px; - border-style: solid; - } -} - - -@mixin btn-green { - background-color: #28b061; - border: 1px solid #26a65c; - color: #fff; - - &:hover { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; - } - - &:focus { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; - } - - &:active { - @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); - - background-color: #23a158 !important; - border: 1px solid #229954 !important; - color: #fff !important; - } -} - -/*Butons*/ - -@mixin bnt-project { - background-color: #f0f2f5; - border-color: #dce0e5; - color: #313236; - - &:hover { - border-color:#dce0e5; - background-color: #ebeef2; - color: #313236; - } - - &:focus { - border-color: #dce0e5; - background-color: #ebeef2; - color: #313236; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - - color: #313236 !important; - border-color: #c6cacf !important; - background-color: #e4e7ed !important; - } -} - -@mixin btn-remove { - background-color: #f72e60; - border-color: #ee295a; - - &:hover { - background-color: #e82757; - border-color: #e32555; - } - - &:focus { - background-color: #e82757; - border-color: #e32555; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - background-color: #d42450 !important; - border-color: #e12554 !important; - } - } \ No newline at end of file diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 45e284542d2..03919f15f1f 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -394,3 +394,11 @@ table { .dropzone .dz-preview .dz-progress .dz-upload { background: $gl-success !important; } + +.space-right { + margin-right: 10px; +} + +.in-line { + display: inline-block; +} diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index 4282832e2bf..98a7c3ad8ac 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -30,9 +30,6 @@ input[type='text'].danger { } @media (min-width: $screen-sm-min) { - .form-actions { - padding-left: 17%; - } } label { @@ -84,3 +81,24 @@ label { .wiki-content { margin-top: 35px; } + +.form-group .control-label { + font-weight: normal; + font-color: #313236 !important; +} + +.form-control { + @include border-radius (2px); +} + +.form-control::-webkit-input-placeholder{ + color:#7f8fa4; +} + +.input-group { + @include border-radius (2px); + + .input-group-addon { + background-color: #f7f8fa; + } +} diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss index b1fb87a6830..93377e45e70 100644 --- a/app/assets/stylesheets/generic/issue_box.scss +++ b/app/assets/stylesheets/generic/issue_box.scss @@ -5,7 +5,7 @@ */ .issue-box { - @include border-radius(3px); + @include border-radius(2px); display: inline-block; padding: 10px $gl-padding; diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss index f0860de1c49..633dfea5228 100644 --- a/app/assets/stylesheets/generic/selects.scss +++ b/app/assets/stylesheets/generic/selects.scss @@ -8,7 +8,7 @@ font-size: $gl-font-size; line-height: 1.42857143; - @include border-radius(4px); + @include border-radius(2px); .select2-arrow { background: #FFF; @@ -18,8 +18,39 @@ } } +.select2-container .select2-choice, .select2-container.select2-drop-above .select2-choice{ + color: #7f8fa4; + border: 1px solid #e7e9ed; +} + +.select2-drop { + @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); + @include border-radius (0px); + + padding: 16px 20px; + border: none !important; +} + +.select2-results .select2-result-label { + padding: 16px 20px; +} + +.select2-drop{ + color: #7f8fa4; +} + +.select2-highlighted { + background: #3084bb !important; +} + +.select2-results li.select2-result-with-children > .select2-result-label { + font-weight: 600; + color: #313236; +} + + .select2-container-multi .select2-choices { - @include border-radius(4px); + @include border-radius(2px); border-color: #CCC; } @@ -63,7 +94,7 @@ .ajax-users-dropdown, .ajax-project-users-dropdown { .select2-search { - padding-top: 4px; + padding-top: 2px; } } @@ -97,9 +128,6 @@ } .user-name { } - .user-username { - color: #999; - } } .namespace-result { @@ -115,4 +143,4 @@ .ajax-users-dropdown { min-width: 225px !important; -} +} \ No newline at end of file diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss index 74bbaabad39..bf21d7fce76 100644 --- a/app/assets/stylesheets/generic/timeline.scss +++ b/app/assets/stylesheets/generic/timeline.scss @@ -10,8 +10,8 @@ margin-left: -$gl-padding; margin-right: -$gl-padding; color: $gl-gray; - border-bottom: 1px solid #f1f2f4; - border-right: 1px solid #f1f2f4; + border-bottom: 1px solid #ECEEF1; + border-right: 1px solid #ECEEF1; &:last-child { border-bottom: none; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index b5c61f7f91d..9da085a3473 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -54,21 +54,22 @@ margin-top: -15px; padding: 10px 0; margin-bottom: 0; - color: $gl-gray; + color: #5c5d5e; font-size: 16px; .author { - color: $gl-gray; + color: #5c5d5e; } .issue-id { - font-size: 19px; - color: $gl-text-color; + color: #5c5d5e; } } .issue-title { margin: 0; + font-size: 23px; + color: #313236; } .description { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index d8c8e5ad0a4..fe69d16fc4b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -3,12 +3,11 @@ * */ .mr-state-widget { - background: #f8fafc; + background: #F7F8FA; margin-bottom: 20px; color: $gl-gray; - border: 1px solid #eef0f2; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05)); - @include border-radius(3px); + border: 1px solid #dce0e6; + @include border-radius(2px); form { margin-bottom: 0; @@ -76,11 +75,17 @@ .mr-widget-footer { padding: 15px; } + + .normal { + color: #5c5d5e; + } .mr-widget-body { h4 { - font-weight: bold; + font-weight: 600; + font-size: 17px; margin: 5px 0; + color: #313236; } p:last-child { @@ -102,9 +107,7 @@ margin: -$gl-padding; padding: $gl-padding; text-align: center; - border-top: 1px solid #e7e9ed; - margin-top: 18px; - margin-bottom: 3px; + margin-bottom: 1px; } .mr_source_commit, @@ -120,10 +123,12 @@ } .label-branch { - color: #222; + color: #313236; font-family: $monospace_font; font-weight: bold; overflow: hidden; + font-size: 14px; + margin: 0 3px; } .mr-list { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index fdc2c3332df..dcd1aed7196 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -72,12 +72,12 @@ .common-note-form { margin: 0; - background: #f8fafc; + background: #F7F8FA; padding: $gl-padding; margin-left: -$gl-padding; margin-right: -$gl-padding; - border-right: 1px solid #f1f2f4; - border-top: 1px solid #f1f2f4; + border-right: 1px solid #ECEEF1; + border-top: 1px solid #ECEEF1; margin-bottom: -$gl-padding; } @@ -168,7 +168,7 @@ .comment-hints { color: #999; background: #FFF; - padding: 5px; + padding: 7px; margin-top: -11px; border: 1px solid $border-color; font-size: 13px; diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 2a77f065aed..abb03b07f51 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -18,7 +18,7 @@ ul.notes { font-size: 14px; padding-top: 10px; padding-bottom: 10px; - background: #f8fafc; + background: #FDFDFD; .timeline-icon { .avatar { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 818aa10aefe..38b17be7980 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,11 +13,19 @@ .edit_project { fieldset.features { .control-label { - font-weight: bold; + font-weight: normal; } } } +.btn, .commits-compare-switch { + @include btn-light; +} + +.project-edit-content { + padding: 7px; +} + .project-name-holder { .help-inline { vertical-align: top; @@ -92,8 +100,8 @@ margin-bottom: 0px; .btn { - @include bnt-project; - @include btn-info; + @include btn-project; + @include btn-default; .count { display: inline-block; @@ -139,7 +147,11 @@ } } } +.btn-green { + @include btn-green; + cursor: pointer; +} .projects-search-form { .input-group .form-control { @@ -149,7 +161,7 @@ .input-group-btn { .btn { - @include bnt-project; + @include btn-project; @include btn-middle; &:hover { @@ -183,8 +195,8 @@ margin: 0 12px 0 12px; .btn{ - @include bnt-project; - @include btn-info; + @include btn-project; + @include btn-default; } .dropdown-toggle { @@ -251,18 +263,19 @@ margin-bottom: 10px; i { - margin: 0 3px; + margin: 2px 0; font-size: 20px; } .option-title { - font-weight: bold; + font-weight: normal; display: inline-block; + color: #313236; } .option-descr { - margin-left: 36px; - color: $gray; + margin-left: 29px; + color: #54565b; } } } @@ -376,8 +389,8 @@ table.table.protected-branches-list tr.no-border { } .nav > li > a { - @include btn-info; - @include bnt-project; + @include btn-default; + @include btn-project; background-color: transparent; border: 1px solid #f7f8fa; diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 0b0f52c653c..58d8478744e 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -24,7 +24,7 @@ %ul.dropdown-menu %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) - .light + .normal %span Request to merge %span.label-branch #{source_branch_with_namespace(@merge_request)} %span into diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index bccea21e7a8..1b093c8f514 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -8,7 +8,7 @@ = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f| .form-group.project-name-holder = f.label :path, class: 'control-label' do - %strong Project path + Project path .col-sm-10 .input-group = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true @@ -23,7 +23,6 @@ = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - if import_sources_enabled? - %hr .project-import.js-toggle-container .form-group @@ -35,7 +34,7 @@ %i.fa.fa-github GitHub - else - = link_to '#', class: 'how_to_import_link light btn import_github' do + = link_to '#', class: 'how_to_import_link btn import_github' do %i.fa.fa-github GitHub = render 'github_import_modal' @@ -46,7 +45,7 @@ %i.fa.fa-bitbucket Bitbucket - else - = link_to status_import_bitbucket_path, class: 'how_to_import_link light btn import_bitbucket', "data-no-turbolink" => "true" do + = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do %i.fa.fa-bitbucket Bitbucket = render 'bitbucket_import_modal' @@ -57,7 +56,7 @@ %i.fa.fa-heart GitLab.com - else - = link_to status_import_gitlab_path, class: 'how_to_import_link light btn import_gitlab' do + = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do %i.fa.fa-heart GitLab.com = render 'gitlab_import_modal' @@ -97,7 +96,7 @@ %li To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}. - %hr.prepend-botton-10 + .prepend-botton-10 .form-group = f.label :description, class: 'control-label' do @@ -112,10 +111,11 @@ - if current_user.can_create_group? .pull-right - .light - Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-xs" do - Create a group + .light.in-line + .space-right + Need a group for several dependent projects? + = link_to new_group_path, class: "btn btn-xs" do + Create a group .save-project-loader.hide .center diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index d99445da59a..512ccd48b38 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -14,6 +14,6 @@ .note-form-actions .buttons.clearfix - = f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button" + = f.submit 'Add Comment', class: "btn btn-green comment-btn btn-grouped js-comment-button" = yield(:note_actions) %a.btn.grouped.js-close-discussion-note-form Cancel From 2acb030beaf4a04969340d7d0ede12992f8dd6fe Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 17:21:15 +0200 Subject: [PATCH 028/119] Refactor button css and do some cleanup Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/gl_variables.scss | 4 +- app/assets/stylesheets/base/layout.scss | 1 + app/assets/stylesheets/generic/buttons.scss | 131 ++++++++---------- app/assets/stylesheets/generic/forms.scss | 14 +- app/assets/stylesheets/generic/selects.scss | 10 +- app/assets/stylesheets/pages/projects.scss | 15 +- 6 files changed, 76 insertions(+), 99 deletions(-) diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss index 7378d404008..18632da4f2a 100644 --- a/app/assets/stylesheets/base/gl_variables.scss +++ b/app/assets/stylesheets/base/gl_variables.scss @@ -22,8 +22,8 @@ $brand-info: $gl-info; $brand-warning: $gl-warning; $brand-danger: $gl-danger; -$border-radius-base: 3px !default; -$border-radius-large: 5px !default; +$border-radius-base: 2px !default; +$border-radius-large: 2px !default; $border-radius-small: 2px !default; diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/base/layout.scss index b91c15d8910..c7b3b60e769 100644 --- a/app/assets/stylesheets/base/layout.scss +++ b/app/assets/stylesheets/base/layout.scss @@ -5,6 +5,7 @@ html { body { padding-top: $header-height; + text-rendering: geometricPrecision; } } diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 50c3de089f3..086a056bbf5 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -1,10 +1,6 @@ -body { - text-rendering: geometricPrecision; -} - @mixin btn-default { @include border-radius(2px); - + border-width: 1px; border-style: solid; text-transform: uppercase; @@ -13,17 +9,17 @@ body { line-height: 18px; padding: 11px 16px; letter-spacing: .4px; - + &:hover { border-width: 1px; border-style: solid; } - + &:focus { border-width: 1px; border-style: solid; } - + &:active { @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); border-width: 1px; @@ -33,7 +29,7 @@ body { @mixin btn-middle { @include border-radius(2px); - + border-width: 1px; border-style: solid; text-transform: uppercase; @@ -42,22 +38,22 @@ body { line-height: 18px; padding: 11px 24px; letter-spacing: .4px; - + &:hover { border-width: 1px; border-style: solid; } - + &:focus { border-width: 1px; border-style: solid; } - + &:active { @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); border-width: 1px; border-style: solid; - } + } } @@ -65,118 +61,113 @@ body { background-color: #28b061; border: 1px solid #26a65c; color: #fff; - - &:hover { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; - } - - &:focus { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; - } - - &:active { - @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); - - background-color: #23a158 !important; - border: 1px solid #229954 !important; - color: #fff !important; - } + + &:hover { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:focus { + background-color: #26ab5d; + border: 1px solid #229954; + color: #fff; + } + + &:active { + @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); + + background-color: #23a158 !important; + border: 1px solid #229954 !important; + color: #fff !important; + } } -/*Butons*/ - -@mixin btn-project { +@mixin btn-gray { background-color: #f0f2f5; border-color: #dce0e5; color: #313236; - + &:hover { border-color:#dce0e5; background-color: #ebeef2; color: #313236; } - + &:focus { border-color: #dce0e5; background-color: #ebeef2; color: #313236; } - + &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - - color: #313236 !important; - border-color: #c6cacf !important; - background-color: #e4e7ed !important; + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border-color: #c6cacf !important; + background-color: #e4e7ed !important; } } -@mixin btn-light { +@mixin btn-white { background-color: #fff; border-color: #dce0e5; color: #313236; - + &:hover { border-color:#dce0e5; background-color: #f0f2f5; color: #313236; } - + &:focus { border-color: #dce0e5; background-color: #f0f2f5; color: #313236; } - + &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - - color: #313236 !important; - border-color: #c6cacf !important; - background-color: #e4e7ed !important; + @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); + + color: #313236 !important; + border-color: #c6cacf !important; + background-color: #e4e7ed !important; } } -@mixin btn-remove { +@mixin btn-red { background-color: #f72e60; border-color: #ee295a; - + &:hover { background-color: #e82757; border-color: #e32555; } - + &:focus { background-color: #e82757; border-color: #e32555; } - + &:active { @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); background-color: #d42450 !important; border-color: #e12554 !important; } - } -.btn-info { - @include btn-default; - @include btn-project; -} - -.btn-success { - @include btn-default; - @include btn-green; - -} - .btn { @include btn-default; + @include btn-white; + + &.btn-success { + @include btn-green; + } + + &.btn-gray { + @include btn-gray; + } &.btn-new { @extend .btn-success; @@ -264,4 +255,4 @@ body { color: #fff; } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss index 98a7c3ad8ac..0edfe24f195 100644 --- a/app/assets/stylesheets/generic/forms.scss +++ b/app/assets/stylesheets/generic/forms.scss @@ -29,9 +29,6 @@ input[type='text'].danger { border-top: 1px solid $border-color; } -@media (min-width: $screen-sm-min) { -} - label { &.control-label { @extend .col-sm-2; @@ -84,20 +81,13 @@ label { .form-group .control-label { font-weight: normal; - font-color: #313236 !important; } -.form-control { - @include border-radius (2px); -} - -.form-control::-webkit-input-placeholder{ - color:#7f8fa4; +.form-control::-webkit-input-placeholder { + color: #7f8fa4; } .input-group { - @include border-radius (2px); - .input-group-addon { background-color: #f7f8fa; } diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss index 633dfea5228..cba621635b6 100644 --- a/app/assets/stylesheets/generic/selects.scss +++ b/app/assets/stylesheets/generic/selects.scss @@ -26,13 +26,13 @@ .select2-drop { @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); @include border-radius (0px); - - padding: 16px 20px; + + padding: 16px; border: none !important; } .select2-results .select2-result-label { - padding: 16px 20px; + padding: 16px; } .select2-drop{ @@ -142,5 +142,5 @@ } .ajax-users-dropdown { - min-width: 225px !important; -} \ No newline at end of file + min-width: 250px !important; +} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 38b17be7980..c36f5a394d8 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -18,10 +18,6 @@ } } -.btn, .commits-compare-switch { - @include btn-light; -} - .project-edit-content { padding: 7px; } @@ -100,8 +96,7 @@ margin-bottom: 0px; .btn { - @include btn-project; - @include btn-default; + @include btn-gray; .count { display: inline-block; @@ -161,7 +156,7 @@ .input-group-btn { .btn { - @include btn-project; + @include btn-gray; @include btn-middle; &:hover { @@ -195,7 +190,7 @@ margin: 0 12px 0 12px; .btn{ - @include btn-project; + @include btn-gray; @include btn-default; } @@ -390,7 +385,7 @@ table.table.protected-branches-list tr.no-border { .nav > li > a { @include btn-default; - @include btn-project; + @include btn-gray; background-color: transparent; border: 1px solid #f7f8fa; @@ -450,7 +445,7 @@ pre.light-well { .btn-remove { @include btn-middle; - @include btn-remove; + @include btn-red; float: left !important; } From b8e4d6bdbec8631663a017c1b651dc223f0cba16 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 17:32:00 +0200 Subject: [PATCH 029/119] Refactor buttons Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/buttons.scss | 33 ++++++--------------- app/assets/stylesheets/pages/projects.scss | 4 --- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 086a056bbf5..62922e6a330 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -156,12 +156,15 @@ } } - .btn { @include btn-default; @include btn-white; - &.btn-success { + &.btn-success, + &.btn-new, + &.btn-create, + &.btn-save, + &.btn-green { @include btn-green; } @@ -169,20 +172,10 @@ @include btn-gray; } - &.btn-new { - @extend .btn-success; - } - - &.btn-create { - @extend .btn-success; - } - - &.btn-save { - @extend .btn-success; - } - - &.btn-remove { - @extend .btn-danger; + &.btn-danger, + &.btn-remove, + &.btn-red { + @include btn-red; } &.btn-cancel { @@ -212,14 +205,6 @@ margin-right: 0px; } } - - &.btn-save { - @extend .btn-primary; - } - - &.btn-new, &.btn-create { - @extend .btn-success; - } } .btn-block { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c36f5a394d8..0031ab5151b 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -142,11 +142,7 @@ } } } -.btn-green { - @include btn-green; - cursor: pointer; -} .projects-search-form { .input-group .form-control { From 7f63a8787ce454a61f72393ccbe22fc283ba8313 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 7 Oct 2015 17:54:49 +0200 Subject: [PATCH 030/119] Fix tests and few CI features Signed-off-by: Dmitriy Zaporozhets --- app/controllers/ci/projects_controller.rb | 1 + app/controllers/projects/commit_controller.rb | 2 +- app/views/projects/ci_settings/_form.html.haml | 2 +- config/routes.rb | 2 +- spec/requests/ci/builds_spec.rb | 17 ----------------- spec/requests/ci/commits_spec.rb | 16 ---------------- 6 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 spec/requests/ci/builds_spec.rb delete mode 100644 spec/requests/ci/commits_spec.rb diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 5484ae643a6..7777aa18031 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -1,5 +1,6 @@ module Ci class ProjectsController < Ci::ApplicationController + before_action :project before_action :authenticate_user!, except: [:build, :badge] before_action :authorize_access_project!, except: [:badge] before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index c08a90bddf0..7886f3c6deb 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -42,7 +42,7 @@ class Projects::CommitController < Projects::ApplicationController @ci_commit = @project.ci_commit(@commit.sha) @ci_commit.builds.running_or_pending.each(&:cancel) - redirect_to namespace_project_commit_path(project.namespace, project, commit.sha) + redirect_to ci_namespace_project_commit_path(project.namespace, project, commit.sha) end diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml index 34125550206..d711413c6b9 100644 --- a/app/views/projects/ci_settings/_form.html.haml +++ b/app/views/projects/ci_settings/_form.html.haml @@ -8,7 +8,7 @@ Edit your #{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@ci_project)} -- if @repository +- unless @project.empty_repo? %p Paste build status image for #{@repository.root_ref} with next link = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do diff --git a/config/routes.rb b/config/routes.rb index e91b09de9b0..efaf818a9e1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -465,7 +465,7 @@ Gitlab::Application.routes.draw do member do get :branches get :ci - post :cancel_builds + get :cancel_builds end end diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb deleted file mode 100644 index f68116c52aa..00000000000 --- a/spec/requests/ci/builds_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe "Builds" do - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - end - - describe "GET /:project/builds/:id/status.json" do - before do - get status_ci_project_build_path(@commit.project, @build), format: :json - end - - it { expect(response.status).to eq(200) } - it { expect(response.body).to include(@build.sha) } - end -end diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb deleted file mode 100644 index f43a3982d71..00000000000 --- a/spec/requests/ci/commits_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe "Commits" do - before do - @commit = FactoryGirl.create :ci_commit - end - - describe "GET /:project/refs/:ref_name/commits/:id/status.json" do - before do - get status_ci_project_commits_path(@commit.project, @commit.sha), format: :json - end - - it { expect(response.status).to eq(200) } - it { expect(response.body).to include(@commit.sha) } - end -end From 38fbfb9fe6d52e1a36b16ccc2a9da9ae01561cca Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 5 Oct 2015 18:03:14 -0400 Subject: [PATCH 031/119] Require jquery.turbolinks in all JS specs Side-effect: This simplifies the requiresInput handler --- app/assets/javascripts/behaviors/requires_input.js.coffee | 3 +-- spec/javascripts/spec_helper.coffee | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/behaviors/requires_input.js.coffee b/app/assets/javascripts/behaviors/requires_input.js.coffee index 8318fe435b3..79d750d1847 100644 --- a/app/assets/javascripts/behaviors/requires_input.js.coffee +++ b/app/assets/javascripts/behaviors/requires_input.js.coffee @@ -34,6 +34,5 @@ $.fn.requiresInput = -> $form.on 'change input', fieldSelector, requireInput -# Triggered on standard document `ready` and on Turbolinks `page:load` events -$(document).on 'ready page:load', -> +$ -> $('form.js-requires-input').requiresInput() diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee index 47b41dd2c81..90b02a6aec5 100644 --- a/spec/javascripts/spec_helper.coffee +++ b/spec/javascripts/spec_helper.coffee @@ -9,6 +9,7 @@ # require the specific files that are being used in the spec that tests them. #= require jquery +#= require jquery.turbolinks #= require bootstrap #= require underscore From 01d0926bc16bb82ef676c35a5a61b8e90a82654c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 5 Oct 2015 18:19:11 -0400 Subject: [PATCH 032/119] Add "Quick Submit" JS behavior --- .../behaviors/quick_submit.js.coffee | 29 +++++++++ .../behaviors/quick_submit_spec.js.coffee | 65 +++++++++++++++++++ .../fixtures/behaviors/quick_submit.html.haml | 6 ++ 3 files changed, 100 insertions(+) create mode 100644 app/assets/javascripts/behaviors/quick_submit.js.coffee create mode 100644 spec/javascripts/behaviors/quick_submit_spec.js.coffee create mode 100644 spec/javascripts/fixtures/behaviors/quick_submit.html.haml diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee new file mode 100644 index 00000000000..dc9bd4d94cc --- /dev/null +++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee @@ -0,0 +1,29 @@ +# Quick Submit behavior +# +# When an input field with the `js-quick-submit` class receives a "Meta+Enter" +# (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, its parent form is +# submitted. +# +#= require extensions/jquery +# +# ### Example Markup +# +#
+# +# +#
+# +$(document).on 'keydown.quick_submit', '.js-quick-submit', (e) -> + return if e.repeat + return unless e.keyCode == 13 # Enter + + if navigator.userAgent.match(/Macintosh/) + return unless (e.metaKey && !e.altKey && !e.ctrlKey && !e.shiftKey) + else + return unless (e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey) + + e.preventDefault() + + $form = $(e.target).closest('form') + $form.find('input[type=submit], button[type=submit]').disable() + $form.submit() diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee new file mode 100644 index 00000000000..6beef28741b --- /dev/null +++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee @@ -0,0 +1,65 @@ +#= require behaviors/quick_submit + +describe 'Quick Submit behavior', -> + fixture.preload('behaviors/quick_submit.html') + + beforeEach -> + fixture.load('behaviors/quick_submit.html') + + # Prevent a form submit from moving us off the testing page + $('form').submit (e) -> e.preventDefault() + + @spies = { + submit: spyOnEvent('form', 'submit') + } + + it 'does not respond to other keyCodes', -> + $('input').trigger(keydownEvent(keyCode: 32)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + it 'does not respond to Enter alone', -> + $('input').trigger(keydownEvent(ctrlKey: false, metaKey: false)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + it 'disables submit buttons', -> + $('textarea').trigger(keydownEvent()) + + expect($('input[type=submit]')).toBeDisabled() + expect($('button[type=submit]')).toBeDisabled() + + # We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll + # only run the tests that apply to the current platform + if navigator.userAgent.match(/Macintosh/) + it 'responds to Meta+Enter', -> + $('input').trigger(keydownEvent()) + + expect(@spies.submit).toHaveBeenTriggered() + + it 'excludes other modifier keys', -> + $('input').trigger(keydownEvent(altKey: true)) + $('input').trigger(keydownEvent(ctrlKey: true)) + $('input').trigger(keydownEvent(shiftKey: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + else + it 'responds to Ctrl+Enter', -> + $('input').trigger(keydownEvent()) + + expect(@spies.submit).toHaveBeenTriggered() + + it 'excludes other modifier keys', -> + $('input').trigger(keydownEvent(altKey: true)) + $('input').trigger(keydownEvent(metaKey: true)) + $('input').trigger(keydownEvent(shiftKey: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + + keydownEvent = (options) -> + if navigator.userAgent.match(/Macintosh/) + defaults = { keyCode: 13, metaKey: true } + else + defaults = { keyCode: 13, ctrlKey: true } + + $.Event('keydown', $.extend({}, defaults, options)) diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml new file mode 100644 index 00000000000..b80a28a33ea --- /dev/null +++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml @@ -0,0 +1,6 @@ +%form{ action: '/foo' } + %input.js-quick-submit{ type: 'text' } + %textarea.js-quick-submit + + %input{ type: 'submit'} Submit + %button.btn{ type: 'submit' } Submit From 2ed5a5b70df6e4c9b4b9afd0e1cac12f267676e8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 5 Oct 2015 18:20:00 -0400 Subject: [PATCH 033/119] Remove "quick submit" behavior specific to the Notes JS --- app/assets/javascripts/notes.js.coffee | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 4b9f0d68912..ea75c656bcc 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -63,12 +63,6 @@ class @Notes # fetch notes when tab becomes visible $(document).on "visibilitychange", @visibilityChange - # Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown. - $(document).on 'keydown', '.js-note-text', (e) -> - return if e.originalEvent.repeat - if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13) - $(@).closest('form').submit() - cleanBinding: -> $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" @@ -82,7 +76,6 @@ class @Notes $(document).off "click", ".js-discussion-reply-button" $(document).off "click", ".js-add-diff-note-button" $(document).off "visibilitychange" - $(document).off "keydown", ".js-note-text" $(document).off "keyup", ".js-note-text" $(document).off "click", ".js-note-target-reopen" $(document).off "click", ".js-note-target-close" From de3ff174e70a9048fc8f23cd5ee0c709471f3651 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 5 Oct 2015 18:20:43 -0400 Subject: [PATCH 034/119] Quick Submit all the things! Adds quick submit behavior to the forms for: - Blobs ("new file") - Commit messages - Issuables - Notes - Labels - Milestones - Wikis --- app/views/projects/blob/_editor.html.haml | 2 +- app/views/projects/blob/new.html.haml | 2 +- app/views/projects/labels/_form.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 4 ++-- app/views/projects/notes/_edit_form.html.haml | 2 +- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/wikis/_form.html.haml | 2 +- app/views/shared/_commit_message_container.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 9c3e1703c89..f1ad0c3c403 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -11,7 +11,7 @@ - if current_action?(:new) || current_action?(:create) \/ = text_field_tag 'file_name', params[:file_name], placeholder: "File name", - required: true, class: 'form-control new-file-name' + required: true, class: 'form-control new-file-name js-quick-submit' .pull-right = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control' diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index d7987e24ef3..7975137c37f 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -15,7 +15,7 @@ = label_tag 'branch', class: 'control-label' do Branch .col-sm-10 - = text_field_tag 'new_branch', @ref, class: "form-control" + = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit" = hidden_field_tag 'content', '', id: 'file-content' = render 'projects/commit_button', ref: @ref, diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 534c545329b..4cf13492e99 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -10,7 +10,7 @@ .form-group = f.label :title, class: 'control-label' .col-sm-10 - = f.text_field :title, class: "form-control", required: true + = f.text_field :title, class: "form-control js-quick-submit", required: true .form-group = f.label :color, "Background Color", class: 'control-label' .col-sm-10 diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 74e9668052d..255ddab479f 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -16,13 +16,13 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control", required: true + = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true %p.hint Required .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :description, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit' .hint .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}. .pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index a0e26f9827e..a21c019986a 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -2,7 +2,7 @@ = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f| = note_target_fields(note) = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do - = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' + = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field js-quick-submit' = render 'projects/notes/hints' .note-form-actions diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 512ccd48b38..13dfa0a1bb3 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -8,7 +8,7 @@ = f.hidden_field :noteable_type = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text' + = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-quick-submit' = render 'projects/notes/hints' .error-alert diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 05d754adbe5..261d4a92d7d 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -22,7 +22,7 @@ = f.label :content, class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do - = render 'projects/zen', f: f, attr: :content, classes: 'description form-control' + = render 'projects/zen', f: f, attr: :content, classes: 'description form-control js-quick-submit' .col-sm-12.hint .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'} .pull-right Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index 5071ff640f1..cc3f1268f8b 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -6,7 +6,7 @@ .max-width-marker = text_area_tag 'commit_message', (params[:commit_message] || local_assigns[:text]), - class: 'form-control', placeholder: local_assigns[:placeholder], + class: 'form-control js-quick-submit', placeholder: local_assigns[:placeholder], required: true, rows: (local_assigns[:rows] || 3) - if local_assigns[:hint] %p.hint diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 33ec726e93c..594e54f404c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -10,7 +10,7 @@ %strong= 'Title *' .col-sm-10 = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', - class: 'form-control pad js-gfm-input', required: true + class: 'form-control pad js-gfm-input js-quick-submit', required: true - if issuable.is_a?(MergeRequest) %p.help-block @@ -26,7 +26,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', f: f, attr: :description, - classes: 'description form-control' + classes: 'description form-control js-quick-submit' .col-sm-12.hint .pull-left Parsed with From f96c6c43c49c8c423f42a99ae27e02d47f04c748 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 5 Oct 2015 18:31:18 -0400 Subject: [PATCH 035/119] Add CHANGELOG entry for "Quick Submit" [ci skip] --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 8ab2d9f08da..74d9ff97326 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -37,6 +37,8 @@ v 8.1.0 (unreleased) - Use commit status in merge request widget as preffered source of CI status - Integrate CI commit and build pages into project pages - Move CI services page to project settings area + - Add "Quick Submit" behavior to input fields throughout the application. Use + Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux. v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) From fcf99d0d09d63d0662290658ebc1e366bfc11bf0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 15:19:26 -0400 Subject: [PATCH 036/119] Check originalEvent.repeat --- .../javascripts/behaviors/quick_submit.js.coffee | 2 +- spec/javascripts/behaviors/quick_submit_spec.js.coffee | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee index dc9bd4d94cc..0748eed763c 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js.coffee +++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee @@ -14,7 +14,7 @@ # # $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) -> - return if e.repeat + return if e.originalEvent.repeat return unless e.keyCode == 13 # Enter if navigator.userAgent.match(/Macintosh/) diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee index 6beef28741b..fd20f674746 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js.coffee +++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee @@ -23,6 +23,11 @@ describe 'Quick Submit behavior', -> expect(@spies.submit).not.toHaveBeenTriggered() + it 'does not respond to repeated events', -> + $('input').trigger(keydownEvent(repeat: true)) + + expect(@spies.submit).not.toHaveBeenTriggered() + it 'disables submit buttons', -> $('textarea').trigger(keydownEvent()) @@ -62,4 +67,7 @@ describe 'Quick Submit behavior', -> else defaults = { keyCode: 13, ctrlKey: true } - $.Event('keydown', $.extend({}, defaults, options)) + args = $.extend({}, defaults, options) + originalEvent = new KeyboardEvent('keydown', args) + + $.Event('keydown', $.extend({}, args, {originalEvent: originalEvent})) From c3d9c55b23183a51c941800d94ed0ff085e5a3b8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 15:46:18 -0400 Subject: [PATCH 037/119] Add gitlab:two_factor:disable_for_all_users task --- lib/tasks/gitlab/two_factor.rake | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 lib/tasks/gitlab/two_factor.rake diff --git a/lib/tasks/gitlab/two_factor.rake b/lib/tasks/gitlab/two_factor.rake new file mode 100644 index 00000000000..acd4b7da39b --- /dev/null +++ b/lib/tasks/gitlab/two_factor.rake @@ -0,0 +1,10 @@ +namespace :gitlab do + namespace :two_factor do + desc "GitLab | Disable Two-factor authentication (2FA) for all users" + task disable_for_all_users: :environment do + User.with_two_factor.find_each do |user| + user.disable_two_factor! + end + end + end +end From 5a28a5b72634ba180180dfeaeca1d2b0d988e177 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 15:46:54 -0400 Subject: [PATCH 038/119] Add docs for gitlab:two_factor:disable_for_all_users task --- doc/raketasks/user_management.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md index 4fbd20762da..629d38efc53 100644 --- a/doc/raketasks/user_management.md +++ b/doc/raketasks/user_management.md @@ -56,3 +56,17 @@ bundle exec rake gitlab:import:all_users_to_all_groups RAILS_ENV=production ``` block_auto_created_users: false ``` + +## Disable Two-factor Authentication (2FA) for all users + +This task will disable 2FA for all users that have it enabled. This can be +useful if GitLab's `.secret` file has been lost and users are unable to login, +for example. + +```bash +# omnibus-gitlab +sudo gitlab-rake gitlab:two_factor:disable_for_all_users + +# installation from source +bundle exec rake gitlab:two_factor:disable_for_all_users RAILS_ENV=production +``` From 85c6a3743abe5683c2317f1957a9f047ad2b4b8e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Oct 2015 12:30:10 +0200 Subject: [PATCH 039/119] Added methods for detecting MySQL/PostgreSQL These two methods remove the need for manually going into ActiveRecord::Base.connection all over the place. --- lib/gitlab/database.rb | 11 +++++++++++ spec/lib/gitlab/database_spec.rb | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 lib/gitlab/database.rb create mode 100644 spec/lib/gitlab/database_spec.rb diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb new file mode 100644 index 00000000000..741a52714ac --- /dev/null +++ b/lib/gitlab/database.rb @@ -0,0 +1,11 @@ +module Gitlab + module Database + def self.mysql? + ActiveRecord::Base.connection.adapter_name.downcase == 'mysql' + end + + def self.postgresql? + ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql' + end + end +end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb new file mode 100644 index 00000000000..7cdebdf209a --- /dev/null +++ b/spec/lib/gitlab/database_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Database do + # These are just simple smoke tests to check if the methods work (regardless + # of what they may return). + describe '.mysql?' do + subject { described_class.mysql? } + + it { is_expected.to satisfy { |val| val == true || val == false } } + end + + describe '.postgresql?' do + subject { described_class.postgresql? } + + it { is_expected.to satisfy { |val| val == true || val == false } } + end +end From 1190d0ab3dc7a3025bf55b666f34d1a0b51a8d89 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Oct 2015 14:03:18 +0200 Subject: [PATCH 040/119] Added concern for case-insensitive WHERE queries On PostgreSQL these queries use LOWER(...) to compare columns and values. For MySQL a regular WHERE is performed as MySQL is already case-insensitive. --- app/models/concerns/case_sensitivity.rb | 28 +++ spec/models/concerns/case_sensitivity_spec.rb | 176 ++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 app/models/concerns/case_sensitivity.rb create mode 100644 spec/models/concerns/case_sensitivity_spec.rb diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb new file mode 100644 index 00000000000..49d350e092b --- /dev/null +++ b/app/models/concerns/case_sensitivity.rb @@ -0,0 +1,28 @@ +# Concern for querying columns with specific case sensitivity handling. +module CaseSensitivity + extend ActiveSupport::Concern + + module ClassMethods + # Queries the given columns regardless of the casing used. + # + # Unlike other ActiveRecord methods this method only operates on a Hash. + def case_insensitive_where(params) + criteria = self + cast_lower = Gitlab::Database.postgresql? + + params.each do |key, value| + column = ActiveRecord::Base.connection.quote_table_name(key) + + if cast_lower + condition = "LOWER(#{column}) = LOWER(:value)" + else + condition = "#{column} = :value" + end + + criteria = criteria.where(condition, value: value) + end + + criteria + end + end +end diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb new file mode 100644 index 00000000000..8b9f50aada7 --- /dev/null +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -0,0 +1,176 @@ +require 'spec_helper' + +describe CaseSensitivity do + describe '.case_insensitive_where' do + let(:connection) { ActiveRecord::Base.connection } + let(:model) { Class.new { include CaseSensitivity } } + + describe 'using PostgreSQL' do + describe 'with a single column/value pair' do + it 'returns the criteria for a column and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('"foo"') + + expect(model).to receive(:where). + with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.case_insensitive_where(foo: 'bar')).to eq(criteria) + end + + it 'returns the criteria for a column with a table, and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('"foo"."bar"') + + expect(model).to receive(:where). + with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria) + end + end + + describe 'with multiple column/value pairs' do + it 'returns the criteria for a column and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('"foo"') + + expect(connection).to receive(:quote_table_name). + with(:bar). + and_return('"bar"') + + expect(model).to receive(:where). + with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.case_insensitive_where(foo: 'bar', bar: 'baz') + + expect(got).to eq(final) + end + + it 'returns the criteria for a column with a table, and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('"foo"."bar"') + + expect(connection).to receive(:quote_table_name). + with(:'foo.baz'). + and_return('"foo"."baz"') + + expect(model).to receive(:where). + with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz') + + expect(got).to eq(final) + end + end + end + + describe 'using MySQL' do + describe 'with a single column/value pair' do + it 'returns the criteria for a column and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('`foo`') + + expect(model).to receive(:where). + with(%q{LOWER(`foo`) = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.case_insensitive_where(foo: 'bar')).to eq(criteria) + end + + it 'returns the criteria for a column with a table, and a value' do + criteria = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('`foo`.`bar`') + + expect(model).to receive(:where). + with(%q{LOWER(`foo`.`bar`) = LOWER(:value)}, value: 'bar'). + and_return(criteria) + + expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria) + end + end + + describe 'with multiple column/value pairs' do + it 'returns the criteria for a column and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:foo). + and_return('`foo`') + + expect(connection).to receive(:quote_table_name). + with(:bar). + and_return('`bar`') + + expect(model).to receive(:where). + with(%q{LOWER(`foo`) = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER(`bar`) = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.case_insensitive_where(foo: 'bar', bar: 'baz') + + expect(got).to eq(final) + end + + it 'returns the criteria for a column with a table, and a value' do + initial = double(:criteria) + final = double(:criteria) + + expect(connection).to receive(:quote_table_name). + with(:'foo.bar'). + and_return('`foo`.`bar`') + + expect(connection).to receive(:quote_table_name). + with(:'foo.baz'). + and_return('`foo`.`baz`') + + expect(model).to receive(:where). + with(%q{LOWER(`foo`.`bar`) = LOWER(:value)}, value: 'bar'). + and_return(initial) + + expect(initial).to receive(:where). + with(%q{LOWER(`foo`.`baz`) = LOWER(:value)}, value: 'baz'). + and_return(final) + + got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz') + + expect(got).to eq(final) + end + end + end + end +end From dbf9ccbcfe788beb24cda34b0d86b7e12ecdf4bb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 17:59:10 -0400 Subject: [PATCH 041/119] Check originalEvent.repeat *and* event.repeat phantomjs < 2.0 doesn't support creating `KeyboardEvent` so the tests were failing on CI --- app/assets/javascripts/behaviors/quick_submit.js.coffee | 2 +- spec/javascripts/behaviors/quick_submit_spec.js.coffee | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee index 0748eed763c..4ec8531d580 100644 --- a/app/assets/javascripts/behaviors/quick_submit.js.coffee +++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee @@ -14,7 +14,7 @@ # # $(document).on 'keydown.quick_submit', '.js-quick-submit', (e) -> - return if e.originalEvent.repeat + return if (e.originalEvent && e.originalEvent.repeat) || e.repeat return unless e.keyCode == 13 # Enter if navigator.userAgent.match(/Macintosh/) diff --git a/spec/javascripts/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee index fd20f674746..09708c12ed4 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js.coffee +++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee @@ -67,7 +67,4 @@ describe 'Quick Submit behavior', -> else defaults = { keyCode: 13, ctrlKey: true } - args = $.extend({}, defaults, options) - originalEvent = new KeyboardEvent('keydown', args) - - $.Event('keydown', $.extend({}, args, {originalEvent: originalEvent})) + $.Event('keydown', $.extend({}, defaults, options)) From 511fb3ff2ac08537675ea44af0e340921368fc82 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 21:51:05 -0400 Subject: [PATCH 042/119] Update slack-notifier to ~> 1.2.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 26ffb8c4b36..67a8a4b1219 100644 --- a/Gemfile +++ b/Gemfile @@ -163,7 +163,7 @@ gem "gitlab-flowdock-git-hook", "~> 1.0.1" gem "gemnasium-gitlab-service", "~> 0.2" # Slack integration -gem "slack-notifier", "~> 1.0.0" +gem "slack-notifier", "~> 1.2.0" # Asana integration gem 'asana', '~> 0.0.6' diff --git a/Gemfile.lock b/Gemfile.lock index fc9244304a3..9cf834cf761 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -666,7 +666,7 @@ GEM rack-protection (~> 1.4) tilt (>= 1.3, < 3) six (0.2.0) - slack-notifier (1.0.0) + slack-notifier (1.2.1) slim (2.0.3) temple (~> 0.6.6) tilt (>= 1.3.3, < 2.1) @@ -912,7 +912,7 @@ DEPENDENCIES simplecov (~> 0.10.0) sinatra (~> 1.4.4) six (~> 0.2.0) - slack-notifier (~> 1.0.0) + slack-notifier (~> 1.2.0) slim (~> 2.0.2) spinach-rails (~> 0.2.1) spring (~> 1.3.6) From a40cd65d3bd4f284e16c7e8b02b6157d4a882811 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 21:54:15 -0400 Subject: [PATCH 043/119] Update tinder to ~> 1.10.0 --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 26ffb8c4b36..3739df0e7f4 100644 --- a/Gemfile +++ b/Gemfile @@ -151,7 +151,7 @@ gem 'version_sorter', '~> 2.0.0' gem "redis-rails", '~> 4.0.0' # Campfire integration -gem 'tinder', '~> 1.9.2' +gem 'tinder', '~> 1.10.0' # HipChat integration gem 'hipchat', '~> 1.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index fc9244304a3..3cd08cb77ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -182,8 +182,8 @@ GEM factory_girl_rails (4.3.0) factory_girl (~> 4.3.0) railties (>= 3.0.0) - faraday (0.8.10) - multipart-post (~> 1.2.0) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) faraday_middleware (0.10.0) faraday (>= 0.7.4, < 0.10) fastercsv (1.5.5) @@ -335,7 +335,7 @@ GEM activesupport (>= 4.0.1) haml (>= 3.1, < 5.0) railties (>= 4.0.1) - hashie (2.1.2) + hashie (3.4.2) highline (1.6.21) hike (1.2.3) hipchat (1.5.2) @@ -396,7 +396,7 @@ GEM mousetrap-rails (1.4.6) multi_json (1.11.2) multi_xml (0.5.5) - multipart-post (1.2.0) + multipart-post (2.0.0) mysql2 (0.3.20) nenv (0.2.0) nested_form (0.3.2) @@ -721,13 +721,13 @@ GEM timers (4.0.4) hitimes timfel-krb5-auth (0.8.3) - tinder (1.9.4) + tinder (1.10.1) eventmachine (~> 1.0) - faraday (~> 0.8.9) + faraday (~> 0.9.0) faraday_middleware (~> 0.9) - hashie (>= 1.0, < 3) + hashie (>= 1.0) json (~> 1.8.0) - mime-types (~> 1.19) + mime-types multi_json (~> 1.7) twitter-stream (~> 0.1) tins (1.6.0) @@ -927,7 +927,7 @@ DEPENDENCIES teaspoon-jasmine (~> 2.2.0) test_after_commit (~> 0.2.2) thin (~> 1.6.1) - tinder (~> 1.9.2) + tinder (~> 1.10.0) turbolinks (~> 2.5.0) uglifier (~> 2.3.2) underscore-rails (~> 1.4.4) From 53f43910addad75e14a0860235a4ade96bfc5009 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 21:59:16 -0400 Subject: [PATCH 044/119] Update haml-rails to ~> 0.9.0 --- Gemfile | 2 +- Gemfile.lock | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 26ffb8c4b36..205d9044f3f 100644 --- a/Gemfile +++ b/Gemfile @@ -80,7 +80,7 @@ gem 'enumerize', '~> 0.7.0' gem "kaminari", "~> 0.16.3" # HAML -gem "haml-rails", '~> 0.5.3' +gem "haml-rails", '~> 0.9.0' # Files attachments gem "carrierwave", '~> 0.9.0' diff --git a/Gemfile.lock b/Gemfile.lock index fc9244304a3..56466ad95c0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -330,10 +330,11 @@ GEM rspec (>= 2.14, < 4.0) haml (4.0.7) tilt - haml-rails (0.5.3) + haml-rails (0.9.0) actionpack (>= 4.0.1) activesupport (>= 4.0.1) - haml (>= 3.1, < 5.0) + haml (>= 4.0.6, < 5.0) + html2haml (>= 1.0.1) railties (>= 4.0.1) hashie (2.1.2) highline (1.6.21) @@ -345,6 +346,11 @@ GEM html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) + html2haml (2.0.0) + erubis (~> 2.7.0) + haml (~> 4.0.0) + nokogiri (~> 1.6.0) + ruby_parser (~> 3.5) http-cookie (1.0.2) domain_name (~> 0.5) http_parser.rb (0.5.3) @@ -845,7 +851,7 @@ DEPENDENCIES grape-entity (~> 0.4.2) growl guard-rspec (~> 4.2.0) - haml-rails (~> 0.5.3) + haml-rails (~> 0.9.0) hipchat (~> 1.5.0) html-pipeline (~> 1.11.0) httparty (~> 0.13.3) From f9744ea9a42f206f00b16fd7866540bb9edcd93c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 22:08:30 -0400 Subject: [PATCH 045/119] Update rack-cors to ~> 0.4.0 --- Gemfile | 6 +++--- Gemfile.lock | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 26ffb8c4b36..05306374f10 100644 --- a/Gemfile +++ b/Gemfile @@ -65,9 +65,9 @@ gem 'gollum-lib', '~> 4.0.2' gem "gitlab-linguist", "~> 3.0.1", require: "linguist" # API -gem "grape", "~> 0.6.1" -gem "grape-entity", "~> 0.4.2" -gem 'rack-cors', '~> 0.2.9', require: 'rack/cors' +gem 'grape', '~> 0.6.1' +gem 'grape-entity', '~> 0.4.2' +gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' # Format dates and times # based on human-friendly examples diff --git a/Gemfile.lock b/Gemfile.lock index fc9244304a3..5336f4bf957 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -496,7 +496,7 @@ GEM rack (>= 0.4) rack-attack (4.3.0) rack - rack-cors (0.2.9) + rack-cors (0.4.0) rack-mini-profiler (0.9.7) rack (>= 1.1.3) rack-mount (0.8.3) @@ -883,7 +883,7 @@ DEPENDENCIES pry-rails quiet_assets (~> 1.0.2) rack-attack (~> 4.3.0) - rack-cors (~> 0.2.9) + rack-cors (~> 0.4.0) rack-mini-profiler (~> 0.9.0) rack-oauth2 (~> 1.0.5) rails (= 4.1.12) From 5e01e1bb901c967e71d165a1e664b49538277c10 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 22:08:37 -0400 Subject: [PATCH 046/119] Use `methods: :any` in our rack-cors configuration This was added in rack-cors 0.4.0 and allows all types of CORS requests to the API. --- config/application.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/application.rb b/config/application.rb index a96e22211e6..bfa2a809dd7 100644 --- a/config/application.rb +++ b/config/application.rb @@ -74,7 +74,7 @@ module Gitlab origins '*' resource '/api/*', headers: :any, - methods: [:get, :post, :options, :put, :delete], + methods: :any, expose: ['Link'] end end From 26cfb98e4fcfb1bea1b4a5c723940e2a51ef10dc Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 22:37:48 -0400 Subject: [PATCH 047/119] Update whenever to ~> 0.9.4 --- Gemfile | 2 +- Gemfile.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 26ffb8c4b36..e592d5ab208 100644 --- a/Gemfile +++ b/Gemfile @@ -301,7 +301,7 @@ gem 'activerecord-session_store', '~> 0.1.0' gem "nested_form", '~> 0.3.2' # Scheduled -gem 'whenever', '~> 0.8.4', require: false +gem 'whenever', '~> 0.9.4', require: false # OAuth gem 'oauth2', '~> 1.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index fc9244304a3..29ec33bf523 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -770,8 +770,7 @@ GEM websocket-driver (0.6.2) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) - whenever (0.8.4) - activesupport (>= 2.3.4) + whenever (0.9.4) chronic (>= 0.6.3) wikicloth (0.8.1) builder @@ -937,7 +936,7 @@ DEPENDENCIES version_sorter (~> 2.0.0) virtus (~> 1.0.1) webmock (~> 1.21.0) - whenever (~> 0.8.4) + whenever (~> 0.9.4) wikicloth (= 0.8.1) BUNDLED WITH From 63f2510740d8f8756b04dbdf0f3b78d1f6a1ff84 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Oct 2015 11:00:10 +0200 Subject: [PATCH 048/119] Fix build highlight Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/ci/builds.scss | 1 - app/assets/stylesheets/ci/xterm.scss | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/ci/builds.scss index a27dd0db581..74dc3e321c1 100644 --- a/app/assets/stylesheets/ci/builds.scss +++ b/app/assets/stylesheets/ci/builds.scss @@ -73,4 +73,3 @@ margin-bottom: 2px; } } - diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/ci/xterm.scss index 532dede0b23..9a50096c0d0 100644 --- a/app/assets/stylesheets/ci/xterm.scss +++ b/app/assets/stylesheets/ci/xterm.scss @@ -1,4 +1,4 @@ -.ci-body { +.build-page { // color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg // see also: https://gist.github.com/jasonm23/2868981 From 057b1c317bd27461cce93d93399909e67869b159 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Oct 2015 13:29:14 +0200 Subject: [PATCH 049/119] Add new GitLab colors Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/base/variables.scss | 62 ++++++- app/assets/stylesheets/generic/buttons.scss | 154 +++++------------- app/assets/stylesheets/pages/commits.scss | 3 +- app/views/help/ui.html.haml | 1 + .../projects/merge_requests/_show.html.haml | 2 +- 5 files changed, 98 insertions(+), 124 deletions(-) diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss index befd63832d5..eb9a2966389 100644 --- a/app/assets/stylesheets/base/variables.scss +++ b/app/assets/stylesheets/base/variables.scss @@ -23,15 +23,67 @@ $gl-gray: #7f8fa4; $gl-padding: 16px; $gl-avatar-size: 46px; +/* + * Color schema + */ + +$white-light: #FFFFFF; +$white-normal: #DCE0E5; +$white-dark: #E4E7ED; + +$gray-light: #F0F2F5; +$gray-normal: #DCE0E5; +$gray-dark: #E4E7ED; + +$green-light: #31AF64; +$green-normal: #2FAA60; +$green-dark: #2CA05B; + +$blue-light: #2EA8E5; +$blue-normal: #2D9FD8; +$blue-dark: #2897CE; + +$orange-light: #FC6443; +$orange-normal: #E75E40; +$orange-dark: #CE5237; + +$red-light: #F43263; +$red-normal: #E52C5A; +$red-dark: #D22852; + +$border-white-light: #E3E7EC; +$border-white-normal: #D6DAE2; +$border-white-dark: #C6CACF; + +$border-gray-light: #DCE0E5; +$border-gray-normal: #D6DAE2; +$border-gray-dark: #C6CACF; + +$border-green-light: #2FAA60; +$border-green-normal: #2CA05B; +$border-green-dark: #279654; + +$border-blue-light: #2D9FD8; +$border-blue-normal: #2897CE; +$border-blue-dark: #258DC1; + +$border-orange-light: #ED5C3D; +$border-orange-normal: #CE5237; +$border-orange-dark: #C14E35; + +$border-red-light: #E52C5A; +$border-red-normal: #D22852; +$border-red-dark: #CA264F; + /* * State colors: */ -$gl-primary: #446e9b; -$gl-success: #44c679; -$gl-info: #00aaff; -$gl-warning: #EB9532; -$gl-danger: #d9534f; +$gl-primary: $blue-normal; +$gl-success: $green-normal; +$gl-info: $blue-normal; +$gl-warning: $orange-normal; +$gl-danger: $red-normal; /* * Commit Diff Colors diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 62922e6a330..11acbe3adfa 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -1,6 +1,5 @@ @mixin btn-default { @include border-radius(2px); - border-width: 1px; border-style: solid; text-transform: uppercase; @@ -10,150 +9,62 @@ padding: 11px 16px; letter-spacing: .4px; - &:hover { - border-width: 1px; - border-style: solid; - } - - &:focus { - border-width: 1px; - border-style: solid; - } - + &:focus, &:active { + outline: none; @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border-width: 1px; - border-style: solid; } } @mixin btn-middle { + @include btn-default; @include border-radius(2px); - - border-width: 1px; - border-style: solid; - text-transform: uppercase; - font-size: 13px; - font-weight: 600; - line-height: 18px; padding: 11px 24px; - letter-spacing: .4px; - - &:hover { - border-width: 1px; - border-style: solid; - } - - &:focus { - border-width: 1px; - border-style: solid; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - border-width: 1px; - border-style: solid; - } } +@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { + background-color: $light; + border-color: $border-light; + color: $color; -@mixin btn-green { - background-color: #28b061; - border: 1px solid #26a65c; - color: #fff; - - &:hover { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; - } - + &:hover, &:focus { - background-color: #26ab5d; - border: 1px solid #229954; - color: #fff; + background-color: $normal; + border-color: $border-normal; + color: $color; } &:active { @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12)); - background-color: #23a158 !important; - border: 1px solid #229954 !important; - color: #fff !important; + background-color: $dark; + border-color: $border-dark; + color: $color; } } -@mixin btn-gray { - background-color: #f0f2f5; - border-color: #dce0e5; - color: #313236; - - &:hover { - border-color:#dce0e5; - background-color: #ebeef2; - color: #313236; - } - - &:focus { - border-color: #dce0e5; - background-color: #ebeef2; - color: #313236; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - - color: #313236 !important; - border-color: #c6cacf !important; - background-color: #e4e7ed !important; - } +@mixin btn-green { + @include btn-color($green-light, $border-green-light, $green-normal, $border-green-normal, $green-dark, $border-green-dark, #FFFFFF); } -@mixin btn-white { - background-color: #fff; - border-color: #dce0e5; - color: #313236; +@mixin btn-blue { + @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF); +} - &:hover { - border-color:#dce0e5; - background-color: #f0f2f5; - color: #313236; - } - - &:focus { - border-color: #dce0e5; - background-color: #f0f2f5; - color: #313236; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - - color: #313236 !important; - border-color: #c6cacf !important; - background-color: #e4e7ed !important; - } +@mixin btn-orange { + @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF); } @mixin btn-red { - background-color: #f72e60; - border-color: #ee295a; + @include btn-color($red-light, $border-red-light, $red-normal, $border-red-normal, $red-dark, $border-red-dark, #FFFFFF); +} - &:hover { - background-color: #e82757; - border-color: #e32555; - } +@mixin btn-gray { + @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, #313236); +} - &:focus { - background-color: #e82757; - border-color: #e32555; - } - - &:active { - @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12)); - background-color: #d42450 !important; - border-color: #e12554 !important; - } +@mixin btn-white { + @include btn-color($white-light, $border-white-light, $white-normal, $border-white-normal, $white-dark, $border-white-dark, #313236); } .btn { @@ -172,6 +83,15 @@ @include btn-gray; } + &.btn-primary, + &.btn-info { + @include btn-blue; + } + + &.btn-warning { + @include btn-orange; + } + &.btn-danger, &.btn-remove, &.btn-red { diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index de2ae93df37..4e121b95d13 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -1,5 +1,6 @@ .commits-compare-switch{ - @extend .btn; + @include btn-default; + @include btn-white; background: image-url("switch_icon.png") no-repeat center center; text-indent: -9999px; float: left; diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 7c89457ace3..1bd271477e4 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -107,6 +107,7 @@ .example %button.btn.btn-default{:type => "button"} Default + %button.btn.btn-gray{:type => "button"} Gray %button.btn.btn-primary{:type => "button"} Primary %button.btn.btn-success{:type => "button"} Success %button.btn.btn-info{:type => "button"} Info diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 58d8478744e..e7ac7a0eaa4 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -34,7 +34,7 @@ = render "projects/merge_requests/widget/show.html.haml" - if @merge_request.open? && @merge_request.can_be_merged? - .light + .light.append-bottom-20 You can also accept this merge request manually using the = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" From dc400a7414e5b26893d002ce42bfaf15c0ccae79 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 8 Oct 2015 13:47:59 +0200 Subject: [PATCH 050/119] Improve help/ui page Signed-off-by: Dmitriy Zaporozhets --- app/views/help/ui.html.haml | 51 ++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 1bd271477e4..2169a821fb2 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -14,6 +14,8 @@ = link_to 'Lists', '#lists' %li = link_to 'Tables', '#tables' + %li + = link_to 'Nav', '#nav' %li = link_to 'Buttons', '#buttons' %li @@ -30,16 +32,31 @@ %h2#blocks Blocks %h3 - %code .well + %code .gray-content-block - .well - %h4 Something + + .gray-content-block.middle-block + %h4 Normal block inside content + = lorem + + .gray-content-block.second-block + %h4 Second block = lorem %h2#lists Lists + %h3 + %code .content-list + %ul.content-list + %li + One item + %li + One item + %li + One item + %h3 %code .well-list %ul.well-list @@ -102,6 +119,34 @@ %td the Bird %td @twitter + %h2#navs Navigation + + %h3 + %code .center-top-menu + .example + %ul.center-top-menu + %li.active + %a Open + %li + %a Closed + + %h3 + %code .btn-group.btn-group-next + .example + %div.btn-group.btn-group-next + %a.btn.active Open + %a.btn Closed + + + %h3 + %code .nav.nav-tabs + .example + %ul.nav.nav-tabs + %li.active + %a Open + %li + %a Closed + %h2#buttons Buttons From 03417456f0b7db408bfefd28e5b9342889b7f711 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Oct 2015 17:37:39 +0200 Subject: [PATCH 051/119] Revamp finding projects by namespaces By using a JOIN we can remove the need for using 2 separate queries to find a project by its namespace. Combined with an index (only needed for PostgreSQL) this reduces the query time from ~245 ms (~520 ms for the first call) down to roughly 10 ms (~15 ms for the first call). --- app/models/concerns/case_sensitivity.rb | 2 +- app/models/project.rb | 16 ++++--- ..._namespaces_projects_path_lower_indexes.rb | 17 ++++++++ db/schema.rb | 2 +- spec/benchmarks/models/project_spec.rb | 20 +++++++++ spec/models/concerns/case_sensitivity_spec.rb | 43 ++++++++++++------- 6 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb create mode 100644 spec/benchmarks/models/project_spec.rb diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb index 49d350e092b..fe0cea8465f 100644 --- a/app/models/concerns/case_sensitivity.rb +++ b/app/models/concerns/case_sensitivity.rb @@ -6,7 +6,7 @@ module CaseSensitivity # Queries the given columns regardless of the casing used. # # Unlike other ActiveRecord methods this method only operates on a Hash. - def case_insensitive_where(params) + def iwhere(params) criteria = self cast_lower = Gitlab::Database.postgresql? diff --git a/app/models/project.rb b/app/models/project.rb index bb47b9abb03..f75082a35d0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -40,6 +40,7 @@ class Project < ActiveRecord::Base include Referable include Sortable include AfterCommitQueue + include CaseSensitivity extend Gitlab::ConfigHelper extend Enumerize @@ -235,13 +236,18 @@ class Project < ActiveRecord::Base end def find_with_namespace(id) - return nil unless id.include?('/') + namespace_path, project_path = id.split('/') - id = id.split('/') - namespace = Namespace.by_path(id.first) - return nil unless namespace + return nil if !namespace_path || !project_path - where(namespace_id: namespace.id).where("LOWER(projects.path) = :path", path: id.second.downcase).first + # Use of unscoped ensures we're not secretly adding any ORDER BYs, which + # have a negative impact on performance (and aren't needed for this + # query). + unscoped. + joins(:namespace). + iwhere('namespaces.path' => namespace_path). + iwhere('projects.path' => project_path). + take end def visibility_levels diff --git a/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb new file mode 100644 index 00000000000..7f6cd6d5a78 --- /dev/null +++ b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb @@ -0,0 +1,17 @@ +class NamespacesProjectsPathLowerIndexes < ActiveRecord::Migration + disable_ddl_transaction! + + def up + return unless Gitlab::Database.postgresql? + + execute 'CREATE INDEX CONCURRENTLY index_on_namespaces_lower_path ON namespaces (LOWER(path));' + execute 'CREATE INDEX CONCURRENTLY index_on_projects_lower_path ON projects (LOWER(path));' + end + + def down + return unless Gitlab::Database.postgresql? + + remove_index :namespaces, name: :index_on_namespaces_lower_path + remove_index :projects, name: :index_on_projects_lower_path + end +end diff --git a/db/schema.rb b/db/schema.rb index 93202f16111..c5c462c2e57 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: 20151005162154) do +ActiveRecord::Schema.define(version: 20151007120511) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/spec/benchmarks/models/project_spec.rb b/spec/benchmarks/models/project_spec.rb new file mode 100644 index 00000000000..0c6b533ac2b --- /dev/null +++ b/spec/benchmarks/models/project_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Project, benchmark: true do + describe '.find_with_namespace' do + let(:group) { create(:group, name: 'sisinmaru') } + let(:project) { create(:project, name: 'maru', namespace: group) } + + describe 'using a capitalized namespace' do + benchmark_subject { described_class.find_with_namespace('sisinmaru/MARU') } + + it { is_expected.to iterate_per_second(600) } + end + + describe 'using a lowercased namespace' do + benchmark_subject { described_class.find_with_namespace('sisinmaru/maru') } + + it { is_expected.to iterate_per_second(600) } + end + end +end diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb index 8b9f50aada7..f7ed30f8198 100644 --- a/spec/models/concerns/case_sensitivity_spec.rb +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -1,11 +1,16 @@ require 'spec_helper' describe CaseSensitivity do - describe '.case_insensitive_where' do + describe '.iwhere' do let(:connection) { ActiveRecord::Base.connection } let(:model) { Class.new { include CaseSensitivity } } describe 'using PostgreSQL' do + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + allow(Gitlab::Database).to receive(:mysql?).and_return(false) + end + describe 'with a single column/value pair' do it 'returns the criteria for a column and a value' do criteria = double(:criteria) @@ -18,7 +23,7 @@ describe CaseSensitivity do with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar'). and_return(criteria) - expect(model.case_insensitive_where(foo: 'bar')).to eq(criteria) + expect(model.iwhere(foo: 'bar')).to eq(criteria) end it 'returns the criteria for a column with a table, and a value' do @@ -32,7 +37,7 @@ describe CaseSensitivity do with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar'). and_return(criteria) - expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria) + expect(model.iwhere(:'foo.bar' => 'bar')).to eq(criteria) end end @@ -57,7 +62,7 @@ describe CaseSensitivity do with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz'). and_return(final) - got = model.case_insensitive_where(foo: 'bar', bar: 'baz') + got = model.iwhere(foo: 'bar', bar: 'baz') expect(got).to eq(final) end @@ -82,7 +87,8 @@ describe CaseSensitivity do with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz'). and_return(final) - got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz') + got = model.iwhere(:'foo.bar' => 'bar', + :'foo.baz' => 'baz') expect(got).to eq(final) end @@ -90,6 +96,11 @@ describe CaseSensitivity do end describe 'using MySQL' do + before do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + allow(Gitlab::Database).to receive(:mysql?).and_return(true) + end + describe 'with a single column/value pair' do it 'returns the criteria for a column and a value' do criteria = double(:criteria) @@ -99,10 +110,10 @@ describe CaseSensitivity do and_return('`foo`') expect(model).to receive(:where). - with(%q{LOWER(`foo`) = LOWER(:value)}, value: 'bar'). + with(%q{`foo` = :value}, value: 'bar'). and_return(criteria) - expect(model.case_insensitive_where(foo: 'bar')).to eq(criteria) + expect(model.iwhere(foo: 'bar')).to eq(criteria) end it 'returns the criteria for a column with a table, and a value' do @@ -113,10 +124,11 @@ describe CaseSensitivity do and_return('`foo`.`bar`') expect(model).to receive(:where). - with(%q{LOWER(`foo`.`bar`) = LOWER(:value)}, value: 'bar'). + with(%q{`foo`.`bar` = :value}, value: 'bar'). and_return(criteria) - expect(model.case_insensitive_where('foo.bar': 'bar')).to eq(criteria) + expect(model.iwhere(:'foo.bar' => 'bar')). + to eq(criteria) end end @@ -134,14 +146,14 @@ describe CaseSensitivity do and_return('`bar`') expect(model).to receive(:where). - with(%q{LOWER(`foo`) = LOWER(:value)}, value: 'bar'). + with(%q{`foo` = :value}, value: 'bar'). and_return(initial) expect(initial).to receive(:where). - with(%q{LOWER(`bar`) = LOWER(:value)}, value: 'baz'). + with(%q{`bar` = :value}, value: 'baz'). and_return(final) - got = model.case_insensitive_where(foo: 'bar', bar: 'baz') + got = model.iwhere(foo: 'bar', bar: 'baz') expect(got).to eq(final) end @@ -159,14 +171,15 @@ describe CaseSensitivity do and_return('`foo`.`baz`') expect(model).to receive(:where). - with(%q{LOWER(`foo`.`bar`) = LOWER(:value)}, value: 'bar'). + with(%q{`foo`.`bar` = :value}, value: 'bar'). and_return(initial) expect(initial).to receive(:where). - with(%q{LOWER(`foo`.`baz`) = LOWER(:value)}, value: 'baz'). + with(%q{`foo`.`baz` = :value}, value: 'baz'). and_return(final) - got = model.case_insensitive_where('foo.bar': 'bar', 'foo.baz': 'baz') + got = model.iwhere(:'foo.bar' => 'bar', + :'foo.baz' => 'baz') expect(got).to eq(final) end From c8f18fc56283859f47ab50a5e14a7b292f8e54a5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Oct 2015 17:42:47 +0200 Subject: [PATCH 052/119] Added dedicated Rake task for setting up Postgres This ensures any PostgreSQL specific schema changes (e.g. expression indexes) are created when setting up the database. --- lib/tasks/gitlab/setup.rake | 1 + lib/tasks/migrate/setup_postgresql.rake | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 lib/tasks/migrate/setup_postgresql.rake diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake index 0ac4b0fa8a3..4cbccf2ca89 100644 --- a/lib/tasks/gitlab/setup.rake +++ b/lib/tasks/gitlab/setup.rake @@ -16,6 +16,7 @@ namespace :gitlab do Rake::Task["db:setup"].invoke Rake::Task["add_limits_mysql"].invoke + Rake::Task["setup_postgresql"].invoke Rake::Task["db:seed_fu"].invoke rescue Gitlab::TaskAbortedByUserError puts "Quitting...".red diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake new file mode 100644 index 00000000000..bf6894a8351 --- /dev/null +++ b/lib/tasks/migrate/setup_postgresql.rake @@ -0,0 +1,6 @@ +require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes') + +desc 'GitLab | Sets up PostgreSQL' +task setup_postgresql: :environment do + NamespacesProjectsPathLowerIndexes.new.up +end From 2d779f70ae51d6b23fd1e5d6b14c19762ba000cc Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 7 Oct 2015 18:12:15 +0200 Subject: [PATCH 053/119] Added changelog for project namespace performance --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 8ab2d9f08da..e578411c589 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.1.0 (unreleased) - Add support for creating directories from Files page (Stan Hu) - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu) + - Improved performance of finding projects by their namespace - Fix bug where transferring a project would result in stale commit links (Stan Hu) - Include full path of source and target branch names in New Merge Request page (Stan Hu) - Add user preference to view activities as default dashboard (Stan Hu) From 380392212f87fc80bd45c322eda54461d47745b0 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 30 Sep 2015 17:12:33 -0400 Subject: [PATCH 054/119] merge_request: coerce work_in_progress? into a boolean --- app/models/merge_request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index eb468c6cd53..f69b7a13c4b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -227,7 +227,7 @@ class MergeRequest < ActiveRecord::Base end def work_in_progress? - title =~ /\A\[?WIP\]?:? /i + !!(title =~ /\A\[?WIP\]?:? /i) end def mergeable? From b7def88c02b3726259800a25ee8bb82c866fe673 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 8 Oct 2015 14:59:46 -0500 Subject: [PATCH 055/119] Fix ldap email downcasing bug --- lib/gitlab/ldap/user.rb | 2 +- spec/lib/gitlab/ldap/user_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb index 1ea7751e27d..4be99dd88c2 100644 --- a/lib/gitlab/ldap/user.rb +++ b/lib/gitlab/ldap/user.rb @@ -35,7 +35,7 @@ module Gitlab end def find_by_email - ::User.find_by(email: auth_hash.email) + ::User.find_by(email: auth_hash.email.downcase) end def update_user_attributes diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb index fd2e5f6d0e1..b5b56a34952 100644 --- a/spec/lib/gitlab/ldap/user_spec.rb +++ b/spec/lib/gitlab/ldap/user_spec.rb @@ -13,6 +13,17 @@ describe Gitlab::LDAP::User do let(:auth_hash) do OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info) end + let(:ldap_user_upper_case) { Gitlab::LDAP::User.new(auth_hash_upper_case) } + let(:info_upper_case) do + { + name: 'John', + email: 'John@Example.com', # Email address has upper case chars + nickname: 'john' + } + end + let(:auth_hash_upper_case) do + OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case) + end describe :changed? do it "marks existing ldap user as changed" do @@ -57,6 +68,16 @@ describe Gitlab::LDAP::User do expect(existing_user.id).to eql ldap_user.gl_user.id end + it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do + existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain') + expect{ ldap_user_upper_case.save }.not_to change{ User.count } + + existing_user.reload + expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid' + expect(existing_user.ldap_identity.provider).to eql 'ldapmain' + expect(existing_user.id).to eql ldap_user.gl_user.id + end + it 'maintains an identity per provider' do existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter') expect(existing_user.identities.count).to eql(1) From fce4f1380319d3436e66cc530f272e25350c3cf9 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Fri, 25 Sep 2015 13:38:12 -0500 Subject: [PATCH 056/119] Show message if user tries to fork and has no available namespaces --- app/views/projects/forks/new.html.haml | 63 ++++++++++++++------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index cd5f3a5d39e..f0b0a11c04a 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -1,36 +1,41 @@ - page_title "Fork project" -%h3.page-title Fork project -%p.lead - Click to fork the project to a user or group -%hr +- if @namespaces.present? + %h3.page-title Fork project + %p.lead + Click to fork the project to a user or group + %hr -.fork-namespaces - - @namespaces.in_groups_of(6, false) do |group| - .row - - group.each do |namespace| - .col-md-2.col-sm-3 - - if fork = namespace.find_fork_of(@project) - .fork-thumbnail - = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do - = image_tag namespace_icon(namespace, 100) - .caption - %strong - = namespace.human_name - %div.text-primary - Already forked + .fork-namespaces + - @namespaces.in_groups_of(6, false) do |group| + .row + - group.each do |namespace| + .col-md-2.col-sm-3 + - if fork = namespace.find_fork_of(@project) + .fork-thumbnail + = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do + = image_tag namespace_icon(namespace, 100) + .caption + %strong + = namespace.human_name + %div.text-primary + Already forked - - else - .fork-thumbnail - = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do - = image_tag namespace_icon(namespace, 100) - .caption - %strong - = namespace.human_name + - else + .fork-thumbnail + = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do + = image_tag namespace_icon(namespace, 100) + .caption + %strong + = namespace.human_name - %p.light - Fork is a copy of a project repository. - %br - Forking a repository allows you to do changes without affecting the original project. + %p.light + Fork is a copy of a project repository. + %br + Forking a repository allows you to do changes without affecting the original project. +- else + %h3 No available namespaces to fork the project + %p.slead + You must have permission to create a project in a namespace before forking. .save-project-loader.hide .center From 37e935943289a6fd1111cf8c2ac181fd4491a89b Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 23 Sep 2015 08:27:21 -0500 Subject: [PATCH 057/119] Clarify support request message --- PROCESS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PROCESS.md b/PROCESS.md index 1b6b3e7d32d..9f4b708d2b5 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -79,7 +79,11 @@ Thanks for the issue report but we only support issues for the latest stable ver ### Support requests and configuration questions -Thanks for your interest in GitLab. We don't use the issue tracker for support requests and configuration questions. Please use the \[support forum\]\(https://groups.google.com/forum/#!forum/gitlabhq), \[Stack Overflow\]\(http://stackoverflow.com/questions/tagged/gitlab), the #gitlab IRC channel on Freenode or the http://about.gitlab.com paid services for this purpose. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. +Thanks for your interest in GitLab. We don't use the issue tracker for support +requests and configuration questions. Please check our +\[getting help\]\(https://about.gitlab.com/getting-help/) page to see all of the available +support options. Also, have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) +for more information. ### Code format From 6b739027330cb07484b71c748e30d5966f63d647 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Fri, 25 Sep 2015 16:07:08 -0400 Subject: [PATCH 058/119] merge_request: add work_in_progress to MR hooks --- CHANGELOG | 1 + app/models/merge_request.rb | 3 ++- doc/web_hooks/web_hooks.md | 6 ++++-- spec/models/merge_request_spec.rb | 11 +++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 66b6e9fdf19..60d2600c497 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -53,6 +53,7 @@ v 8.0.3 - Fix URL shown in Slack notifications - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu) - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu) + - Add work_in_progress key to MR web hooks (Ben Boeckel) v 8.0.2 - Fix default avatar not rendering in network graph (Stan Hu) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f69b7a13c4b..c83b15c7d39 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -275,7 +275,8 @@ class MergeRequest < ActiveRecord::Base attrs = { source: source_project.hook_attrs, target: target_project.hook_attrs, - last_commit: nil + last_commit: nil, + work_in_progress: work_in_progress? } unless last_commit.nil? diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index c185ccfcac3..ef99a69f60a 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -314,7 +314,8 @@ X-Gitlab-Event: Note Hook "name": "John Smith", "email": "john@example.com" } - } + }, + "work_in_progress": false } } ``` @@ -500,6 +501,7 @@ X-Gitlab-Event: Merge Request Hook "email": "gitlabdev@dv6700.(none)" } }, + "work_in_progress": false, "url": "http://example.com/diaspora/merge_requests/1", "action": "open" } @@ -537,4 +539,4 @@ When you press 'Test Hook' in GitLab, you should see something like this in the {"before":"077a85dd266e6f3573ef7e9ef8ce3343ad659c4e","after":"95cd4a99e93bc4bbabacfa2cd10e6725b1403c60",} example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0 - -> / -``` \ No newline at end of file +``` diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 17a49013d25..6aaf1c036b0 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -165,6 +165,17 @@ describe MergeRequest do end end + describe "#hook_attrs" do + it "has all the required keys" do + attrs = subject.hook_attrs + attrs = attrs.to_h + expect(attrs).to include(:source) + expect(attrs).to include(:target) + expect(attrs).to include(:last_commit) + expect(attrs).to include(:work_in_progress) + end + end + it_behaves_like 'an editable mentionable' do subject { create(:merge_request) } From 6f79b5336fb9ac011dd19d5720b8bfda8b6fa53a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Oct 2015 18:04:16 -0400 Subject: [PATCH 059/119] Just kidding, we're deleting whenever entirely --- Gemfile | 3 --- Gemfile.lock | 4 ---- 2 files changed, 7 deletions(-) diff --git a/Gemfile b/Gemfile index e592d5ab208..e75fa37dea1 100644 --- a/Gemfile +++ b/Gemfile @@ -300,9 +300,6 @@ gem 'activerecord-deprecated_finders', '~> 1.0.3' gem 'activerecord-session_store', '~> 0.1.0' gem "nested_form", '~> 0.3.2' -# Scheduled -gem 'whenever', '~> 0.9.4', require: false - # OAuth gem 'oauth2', '~> 1.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 29ec33bf523..896c240d59f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,7 +105,6 @@ GEM celluloid (0.16.0) timers (~> 4.0.0) charlock_holmes (0.6.9.4) - chronic (0.10.2) chunky_png (1.3.4) cliver (0.3.2) coderay (1.1.0) @@ -770,8 +769,6 @@ GEM websocket-driver (0.6.2) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) - whenever (0.9.4) - chronic (>= 0.6.3) wikicloth (0.8.1) builder expression_parser @@ -936,7 +933,6 @@ DEPENDENCIES version_sorter (~> 2.0.0) virtus (~> 1.0.1) webmock (~> 1.21.0) - whenever (~> 0.9.4) wikicloth (= 0.8.1) BUNDLED WITH From 69b41ba04331e95739b25c77b3f5f198c0e64bae Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Oct 2015 20:34:50 -0400 Subject: [PATCH 060/119] Normalize space-like characters in keys before output to gitlab-shell gitlab-shell expects only one tab separator per key, and an SSH key with a tab character in the comment, for example, would break things. Closes #2970 --- lib/gitlab/backend/shell.rb | 3 ++- spec/lib/gitlab/backend/shell_spec.rb | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 14ee4701e7b..01b8bda05c6 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -4,7 +4,8 @@ module Gitlab class KeyAdder < Struct.new(:io) def add_key(id, key) - io.puts("#{id}\t#{key.strip}") + key.gsub!(/[[:space:]]+/, ' ').strip! + io.puts("#{id}\t#{key}") end end diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index b6d04330599..b60e23454d6 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -15,4 +15,17 @@ describe Gitlab::Shell do it { is_expected.to respond_to :fork_repository } it { expect(gitlab_shell.url_to_repo('diaspora')).to eq(Gitlab.config.gitlab_shell.ssh_path_prefix + "diaspora.git") } + + describe Gitlab::Shell::KeyAdder do + describe '#add_key' do + it 'normalizes space characters in the key' do + io = spy + adder = described_class.new(io) + + adder.add_key('key-42', "sha-rsa foo\tbar\tbaz") + + expect(io).to have_received(:puts).with("key-42\tsha-rsa foo bar baz") + end + end + end end From 29fe488b0cf9f8a79aad774c912be94bb737d36a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Oct 2015 20:53:29 -0400 Subject: [PATCH 061/119] Make the loading spinner toggle more explicit Occasionally the loading spinner would stay visible after the tab finished loading. This change makes the toggle explicit so that it's always shown on `beforeSend`, and always hidden on `complete`. Plus a bonus semi-colon eradication! :boom: --- app/assets/javascripts/merge_request_tabs.js.coffee | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 4e56791bde4..3e77ea515f8 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -69,7 +69,7 @@ class @MergeRequestTabs scrollToElement: (container) -> if window.location.hash top = $(container + " " + window.location.hash).offset().top - $('body').scrollTo(top); + $('body').scrollTo(top) # Activate a tab based on the current action activateTab: (action) -> @@ -139,13 +139,16 @@ class @MergeRequestTabs @diffsLoaded = true @scrollToElement(".diffs") - toggleLoading: -> - $('.mr-loading-status .loading').toggle() + # Show or hide the loading spinner + # + # status - Boolean, true to show, false to hide + toggleLoading: (status) -> + $('.mr-loading-status .loading').toggle(status) _get: (options) -> defaults = { - beforeSend: @toggleLoading - complete: @toggleLoading + beforeSend: => @toggleLoading(true) + complete: => @toggleLoading(false) dataType: 'json' type: 'GET' } From 0c8ac9815314d91bc32eff18502788480cd62d67 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 7 Oct 2015 22:20:22 -0400 Subject: [PATCH 062/119] Remove slim --- Gemfile | 1 - Gemfile.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/Gemfile b/Gemfile index 044dc30ecd4..abb76b7f7d0 100644 --- a/Gemfile +++ b/Gemfile @@ -128,7 +128,6 @@ gem 'after_commit_queue' gem 'acts-as-taggable-on', '~> 3.4' # Background jobs -gem 'slim', '~> 2.0.2' gem 'sinatra', '~> 1.4.4', require: nil gem 'sidekiq', '3.3.0' gem 'sidetiq', '~> 0.6.3' diff --git a/Gemfile.lock b/Gemfile.lock index f716c0254ec..ac4a1f5b007 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -673,9 +673,6 @@ GEM tilt (>= 1.3, < 3) six (0.2.0) slack-notifier (1.2.1) - slim (2.0.3) - temple (~> 0.6.6) - tilt (>= 1.3.3, < 2.1) slop (3.6.0) spinach (0.8.10) colorize @@ -711,7 +708,6 @@ GEM railties (>= 3.2.5, < 5) teaspoon-jasmine (2.2.0) teaspoon (>= 1.0.0) - temple (0.6.10) term-ansicolor (1.3.2) tins (~> 1.0) terminal-table (1.5.2) @@ -919,7 +915,6 @@ DEPENDENCIES sinatra (~> 1.4.4) six (~> 0.2.0) slack-notifier (~> 1.2.0) - slim (~> 2.0.2) spinach-rails (~> 0.2.1) spring (~> 1.3.6) spring-commands-rspec (~> 1.0.4) From 95411f7151a8e1058731a6eb5fefcf7d91312b58 Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Fri, 9 Oct 2015 14:05:55 +1100 Subject: [PATCH 063/119] fixed positioning of hamburger menu on header - Changed to margin for vertical allign as used in bootstrap 3 original - Also added an `active` state to show when the extra items are enabled --- CHANGELOG | 1 + app/assets/javascripts/application.js.coffee | 1 + app/assets/stylesheets/generic/header.scss | 7 +++++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b1a35c3c2ab..20bd95f03a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -41,6 +41,7 @@ v 8.1.0 (unreleased) - Move CI services page to project settings area - Add "Quick Submit" behavior to input fields throughout the application. Use Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux. + - Fix position of hamburger in header for smaller screens (Han Loong Liauw) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 8e987ac4e83..945ffb660e6 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -180,6 +180,7 @@ $ -> $('.navbar-toggle').on 'click', -> $('.header-content .title').toggle() $('.header-content .navbar-collapse').toggle() + $('.navbar-toggle').toggleClass('active') # Show/hide comments on diff $("body").on "click", ".js-toggle-diff-comments", (e) -> diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/generic/header.scss index 543ce41ab52..91e6975e269 100644 --- a/app/assets/stylesheets/generic/header.scss +++ b/app/assets/stylesheets/generic/header.scss @@ -50,15 +50,17 @@ header { .navbar-toggle { color: #666; - margin: 0; + margin: 6px 0; border-radius: 0; position: absolute; right: 2px; - top: 15px; &:hover { background-color: #EEE; } + &.active { + color: #7f8fa4; + } } } } @@ -87,6 +89,7 @@ header { .navbar-collapse { float: right; + border-top: none; } } From cd46f4c36736ed548e440566095e51a3a43ca6d6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 8 Oct 2015 23:19:56 -0400 Subject: [PATCH 064/119] Add output and confirmation to gitlab:two_factor:disable_for_all_users --- lib/tasks/gitlab/two_factor.rake | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/tasks/gitlab/two_factor.rake b/lib/tasks/gitlab/two_factor.rake index acd4b7da39b..9196677a017 100644 --- a/lib/tasks/gitlab/two_factor.rake +++ b/lib/tasks/gitlab/two_factor.rake @@ -2,8 +2,21 @@ namespace :gitlab do namespace :two_factor do desc "GitLab | Disable Two-factor authentication (2FA) for all users" task disable_for_all_users: :environment do - User.with_two_factor.find_each do |user| - user.disable_two_factor! + scope = User.with_two_factor + count = scope.count + + if count > 0 + puts "This will disable 2FA for #{count.to_s.red} users..." + + begin + ask_to_continue + scope.find_each(&:disable_two_factor!) + puts "Successfully disabled 2FA for #{count} users.".green + rescue Gitlab::TaskAbortedByUserError + puts "Quitting...".red + end + else + puts "There are currently no users with 2FA enabled.".yellow end end end From b60bc655f2e1318f448f3a3d884437677c1135b7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 8 Oct 2015 21:24:55 -0700 Subject: [PATCH 065/119] Restore placeholders in issue filters from "Any" to "Milestone" and "Label" 1f11096c included new filters but made it hard to determine which dropdown did what. This patch restores the original placeholders. --- app/helpers/labels_helper.rb | 3 ++- app/helpers/milestones_helper.rb | 3 ++- app/models/group_milestone.rb | 2 ++ app/models/label.rb | 4 +++- app/models/milestone.rb | 6 +++++- app/views/shared/issuable/_filter.html.haml | 4 ++-- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 662ace367b9..66b18eea699 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -95,7 +95,8 @@ module LabelsHelper def project_labels_options(project) labels = project.labels.to_a labels.unshift(Label::None) - options_from_collection_for_select(labels, 'name', 'name', params[:label_name]) + labels.unshift(Label::Any) + options_from_collection_for_select(labels, 'name', 'title', params[:label_name]) end # Required for Gitlab::Markdown::LabelReferenceFilter diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 132a893e532..37a5b58cce8 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -30,7 +30,8 @@ module MilestonesHelper grouped_milestones = Milestones::GroupService.new(milestones).execute grouped_milestones.unshift(Milestone::None) + grouped_milestones.unshift(Milestone::Any) - options_from_collection_for_select(grouped_milestones, 'title', 'title', params[:milestone_title]) + options_from_collection_for_select(grouped_milestones, 'name', 'title', params[:milestone_title]) end end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index ab055f6b80b..1dd2be68ebf 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -1,5 +1,7 @@ class GroupMilestone + alias_attribute :name, :title + def initialize(title, milestones) @title = title @milestones = milestones diff --git a/app/models/label.rb b/app/models/label.rb index 14b544b3756..1bb4b5f55cf 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -14,7 +14,9 @@ class Label < ActiveRecord::Base include Referable # Represents a "No Label" state used for filtering Issues and Merge # Requests that have no label assigned. - None = Struct.new(:title, :name).new('No Label', 'No Label') + LabelStruct = Struct.new(:title, :name) + None = LabelStruct.new('No Label', 'No Label') + Any = LabelStruct.new('Any', '') DEFAULT_COLOR = '#428BCA' diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d979a35084b..84acba30b6b 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,7 +16,9 @@ class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. - None = Struct.new(:title).new('No Milestone') + MilestoneStruct = Struct.new(:title, :name) + None = MilestoneStruct.new('No Milestone', 'No Milestone') + Any = MilestoneStruct.new('Any', '') include InternalId include Sortable @@ -47,6 +49,8 @@ class Milestone < ActiveRecord::Base state :active end + alias_attribute :name, :title + class << self def search(query) query = "%#{query}%" diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 6e6d497c1d2..8f16773077e 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -39,13 +39,13 @@ .filter-item.inline.milestone-filter = select_tag('milestone_title', projects_milestones_options, - class: 'select2 trigger-submit', include_blank: 'Any', + class: 'select2 trigger-submit', include_blank: true, data: {placeholder: 'Milestone'}) - if @project .filter-item.inline.labels-filter = select_tag('label_name', project_labels_options(@project), - class: 'select2 trigger-submit', include_blank: 'Any', + class: 'select2 trigger-submit', include_blank: true, data: {placeholder: 'Label'}) .pull-right From b5d9a613d98dc5ae629bdc004f19be7c8e5fcd8e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 8 Oct 2015 13:22:34 -0700 Subject: [PATCH 066/119] Make diff file view easier to use on mobile screens --- CHANGELOG | 1 + app/assets/stylesheets/pages/diff.scss | 114 +++++++++++------- .../stylesheets/pages/merge_requests.scss | 18 ++- app/views/projects/diffs/_file.html.haml | 2 +- 4 files changed, 90 insertions(+), 45 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b1a35c3c2ab..c4f3b6693f8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.1.0 (unreleased) + - Make diff file view easier to use on mobile screens (Stan Hu) - Add support for creating directories from Files page (Stan Hu) - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 5e7e59a6af8..d9ef06dc6b6 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -1,3 +1,4 @@ +// Common .diff-file { margin-left: -$gl-padding; margin-right: -$gl-padding; @@ -12,24 +13,17 @@ color: #555; z-index: 10; - > span { + .diff-title { font-family: $monospace_font; word-break: break-all; - margin-right: 200px; display: block; .file-mode { - margin-left: 10px; color: #777; } } - .diff-btn-group { - float: right; - position: absolute; - top: 5px; - right: 15px; - + .diff-controls { .btn { padding: 0px 10px; font-size: 13px; @@ -90,12 +84,12 @@ } } - tr.line_holder.parallel{ + tr.line_holder.parallel { .old_line, .new_line, .diff_line { min-width: 50px; } - td.line_content.parallel{ + td.line_content.parallel { width: 50%; } } @@ -105,7 +99,7 @@ padding: 0px; border: none; background: $background-color; - color: rgba(0,0,0,0.3); + color: rgba(0, 0, 0, 0.3); padding: 0px 5px; border-right: 1px solid $border-color; text-align: right; @@ -117,7 +111,7 @@ float: left; width: 35px; font-weight: normal; - color: rgba(0,0,0,0.3); + color: rgba(0, 0, 0, 0.3); &:hover { text-decoration: underline; } @@ -168,7 +162,7 @@ background: #ddd; text-align: center; padding: 30px; - .wrap{ + .wrap { display: inline-block; } @@ -176,7 +170,7 @@ display: inline-block; background-color: #fff; line-height: 0; - img{ + img { border: 1px solid #FFF; background: image-url('trans_bg.gif'); max-width: 100%; @@ -189,21 +183,21 @@ border: 1px solid $added; } } - .image-info{ + .image-info { font-size: 12px; margin: 5px 0 0 0; color: grey; } - .view.swipe{ + .view.swipe { position: relative; - .swipe-frame{ + .swipe-frame { display: block; margin: auto; position: relative; } - .swipe-wrap{ + .swipe-wrap { overflow: hidden; border-left: 1px solid #999; position: absolute; @@ -211,33 +205,33 @@ top: 13px; right: 7px; } - .frame{ + .frame { top: 0; right: 0; position: absolute; - &.deleted{ + &.deleted { margin: 0; display: block; top: 13px; right: 7px; } } - .swipe-bar{ + .swipe-bar { display: block; height: 100%; width: 15px; z-index: 100; position: absolute; cursor: pointer; - &:hover{ - .top-handle{ + &:hover { + .top-handle { background-position: -15px 3px; } - .bottom-handle{ + .bottom-handle { background-position: -15px -11px; } - }; - .top-handle{ + } + .top-handle { display: block; height: 14px; width: 15px; @@ -245,7 +239,7 @@ top: 0px; background: image-url('swipemode_sprites.gif') 0 3px no-repeat; } - .bottom-handle{ + .bottom-handle { display: block; height: 14px; width: 15px; @@ -254,9 +248,10 @@ background: image-url('swipemode_sprites.gif') 0 -11px no-repeat; } } - } //.view.swipe - .view.onion-skin{ - .onion-skin-frame{ + } + //.view.swipe + .view.onion-skin { + .onion-skin-frame { display: block; margin: auto; position: relative; @@ -267,7 +262,7 @@ top: 0px; left: 0px; } - .controls{ + .controls { display: block; height: 14px; width: 300px; @@ -277,7 +272,7 @@ left: 50%; margin-left: -150px; - .drag-track{ + .drag-track { display: block; position: absolute; left: 12px; @@ -317,39 +312,40 @@ background: image-url('onion_skin_sprites.gif') -2px -10px no-repeat; } } - } //.view.onion-skin + } + //.view.onion-skin } - .view-modes{ + .view-modes { padding: 10px; text-align: center; background: #EEE; - ul, li{ + ul, li { list-style: none; margin: 0; padding: 0; display: inline-block; } - li{ + li { color: grey; border-left: 1px solid #c1c1c1; padding: 0 12px 0 16px; cursor: pointer; - &:first-child{ + &:first-child { border-left: none; } - &:hover{ + &:hover { text-decoration: underline; } - &.active{ - &:hover{ + &.active { + &:hover { text-decoration: none; } cursor: default; color: #333; } - &.disabled{ + &.disabled { display: none; } } @@ -373,3 +369,37 @@ float: right; margin-top: -5px; } + +// Mobile +@media (max-width: 480px) { + .diff-title { + margin: 0; + + .file-mode { + display: none; + } + } + + .diff-controls { + position: static; + text-align: center; + } +} + +// Bigger screens +@media (min-width: 481px) { + .diff-title { + margin-right: 200px; + + .file-mode { + margin-left: 10px; + } + } + + .diff-controls { + float: right; + position: absolute; + top: 5px; + right: 15px; + } +} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index fe69d16fc4b..a1a5208c59c 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -75,7 +75,7 @@ .mr-widget-footer { padding: 15px; } - + .normal { color: #5c5d5e; } @@ -102,7 +102,7 @@ } } -.merge-request .merge-request-tabs{ +.merge-request .merge-request-tabs { @include nav-menu; margin: -$gl-padding; padding: $gl-padding; @@ -110,6 +110,20 @@ margin-bottom: 1px; } +// Mobile +@media (max-width: 480px) { + .merge-request .merge-request-tabs { + margin: 0; + padding: 0; + + li { + a { + padding: 0; + } + } + } +} + .mr_source_commit, .mr_target_commit { .commit { diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 4617b188150..9698921f6da 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -16,7 +16,7 @@ - if diff_file.mode_changed? %span.file-mode= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" - .diff-btn-group + .diff-controls - if blob.text? = link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do %i.fa.fa-comments From 972b6370819f61eb04d1f0e795b4b6c4ddc2327a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 9 Oct 2015 05:30:42 -0700 Subject: [PATCH 067/119] In SSH key detailed view, align the "Remove" button with the right container --- app/views/profiles/keys/_key_details.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml index e0ae4d9720f..0ca8bd95157 100644 --- a/app/views/profiles/keys/_key_details.html.haml +++ b/app/views/profiles/keys/_key_details.html.haml @@ -18,5 +18,6 @@ %code.key-fingerprint= @key.fingerprint %pre.well-pre = @key.key - .pull-right - = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key" + .col-md-12 + .pull-right + = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key" From 7713a709544c86539a8060cdeed8692745ddaeca Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 9 Oct 2015 06:41:32 -0700 Subject: [PATCH 068/119] Update CHANGELOG for Emoji fix in a3c6ed5c4 [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 20bd95f03a9..416ec54b132 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -42,6 +42,7 @@ v 8.1.0 (unreleased) - Add "Quick Submit" behavior to input fields throughout the application. Use Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux. - Fix position of hamburger in header for smaller screens (Han Loong Liauw) + - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) From 766688d0fd714ab2b42cc8e5bce2675991c73478 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 9 Oct 2015 18:11:18 -0400 Subject: [PATCH 069/119] Update monthly release post template [ci skip] --- doc/release/monthly.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index c56e99a7005..bd8a67d1d85 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -74,20 +74,19 @@ Xth: (1 working day before the 22nd) - [ ] Update GitLab.com with the latest RC (#LINK) - [ ] Update ci.gitLab.com with the latest RC (#LINK) -22nd before 12AM CET: +22nd before 1200 CET: -Release before 12AM CET / 3AM PST, to make sure the majority of our users +Release before 1200 CET / 2AM PST, to make sure the majority of our users get the new version on the 22nd and there is sufficient time in the European workday to quickly fix any issues. - [ ] Merge CE stable into EE stable (#LINK) - [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK) - [ ] Create the 'x.y.0' version on version.gitlab.com -- [ ] Try to do before 11AM CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK) -- [ ] Try to do before 12AM CET: Publish the release blog post (#LINK) +- [ ] Try to do before 1100 CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK) +- [ ] Try to do before 1200 CET: Publish the release blog post (#LINK) - [ ] Tweet about the release (blog post) (#LINK) -- [ ] Schedule a second tweet of the release announcement with the same text at 6PM CET / 9AM PST - +- [ ] Schedule a second tweet of the release announcement with the same text at 1800 CET / 8AM PST ``` - - - @@ -223,4 +222,4 @@ Consider creating a post on Hacker News. ## Create a WIP blogpost for the next release -Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md). \ No newline at end of file +Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md). From a972ce17caadc64fc6fa1888301e99c149848f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A7=D0=B8=D0=BD=D0=B3=D0=B8=D0=B7=20=D0=90=D1=83=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D0=BE=D0=B2?= Date: Sat, 10 Oct 2015 14:07:12 +0600 Subject: [PATCH 070/119] + and Titleize New Project button on dashboard --- app/views/dashboard/projects/_projects.html.haml | 2 +- app/views/groups/_projects.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index e09e032a7f1..30372e98430 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -5,6 +5,6 @@ - if current_user.can_create_project? %span.input-group-btn = link_to new_project_path, class: 'btn btn-green' do - New project + + New Project = render 'shared/projects/list', projects: @projects, ci: true diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 2b27a88794d..9c73f7a0ef7 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -5,6 +5,6 @@ - if can? current_user, :create_projects, @group %span.input-group-btn = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do - New project + + New Project = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false From 78de79d779c1eb8b462fd1b5beb9f9e38bfb6f36 Mon Sep 17 00:00:00 2001 From: Jerry Lukins Date: Thu, 8 Oct 2015 07:53:05 -0400 Subject: [PATCH 071/119] Persist filters when sorting on user admin page --- app/views/admin/users/index.html.haml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index e3698ac1c46..97e4bb2fd73 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -54,19 +54,19 @@ %b.caret %ul.dropdown-menu %li - = link_to admin_users_path(sort: sort_value_name) do + = link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do = sort_title_name - = link_to admin_users_path(sort: sort_value_recently_signin) do + = link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do = sort_title_recently_signin - = link_to admin_users_path(sort: sort_value_oldest_signin) do + = link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do = sort_title_oldest_signin - = link_to admin_users_path(sort: sort_value_recently_created) do + = link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do = sort_title_recently_created - = link_to admin_users_path(sort: sort_value_oldest_created) do + = link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do = sort_title_oldest_created - = link_to admin_users_path(sort: sort_value_recently_updated) do + = link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do = sort_title_recently_updated - = link_to admin_users_path(sort: sort_value_oldest_updated) do + = link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do = sort_title_oldest_updated = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm" From c007d4a9819b1d7992259bc70ef3059955c25a65 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 10 Oct 2015 14:24:00 -0400 Subject: [PATCH 072/119] Add CHANGELOG entry for !1543 [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index f9a4d17fa16..a3d796bea66 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -44,6 +44,7 @@ v 8.1.0 (unreleased) Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux. - Fix position of hamburger in header for smaller screens (Han Loong Liauw) - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji) + - Persist filters when sorting on admin user page (Jerry Lukins) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) From 4ee8888498d7fc3921326d4929cf0b57e64411f4 Mon Sep 17 00:00:00 2001 From: Dale Noe Date: Sun, 11 Oct 2015 12:19:05 +0000 Subject: [PATCH 073/119] removed extra_sign_in_text examples for gitlab.rb [ci skip] --- doc/customization/welcome_message.md | 30 ++-------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md index 6c141d1fb7a..e993230bb88 100644 --- a/doc/customization/welcome_message.md +++ b/doc/customization/welcome_message.md @@ -8,31 +8,5 @@ It is possible to add a markdown-formatted welcome message to your GitLab sign-in page. Users of GitLab Enterprise Edition should use the [branded login page feature](/ee/customization/branded_login_page.html) instead. -## Omnibus-gitlab example - -In `/etc/gitlab/gitlab.rb`: - -```ruby -gitlab_rails['extra_sign_in_text'] = <<'EOS' -# ACME GitLab -Welcome to the [ACME](http://www.example.com) GitLab server! -EOS -``` - -Run `sudo gitlab-ctl reconfigure` for changes to take effect. - -## Installation from source - -In `/home/git/gitlab/config/gitlab.yml`: - -```yaml -# snip -production: - # snip - extra: - sign_in_text: | - # ACME GitLab - Welcome to the [ACME](http://www.example.com) GitLab server! -``` - -Run `sudo service gitlab reload` for the change to take effect. +The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI. +Admin area > Settings \ No newline at end of file From bf23ce191ba6daf3dfca604243dd88d2e3996eff Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 11 Oct 2015 18:17:49 +0200 Subject: [PATCH 074/119] Enable arbitration in MailRoom --- Gemfile | 2 +- Gemfile.lock | 4 ++-- config/mail_room.yml.example | 12 +++++++++++- doc/incoming_email/README.md | 30 ++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index e8cdb7a4a97..967092994a6 100644 --- a/Gemfile +++ b/Gemfile @@ -290,7 +290,7 @@ gem 'newrelic-grape' gem 'octokit', '~> 3.7.0' -gem "mail_room", "~> 0.5.2" +gem "mail_room", "~> 0.6.0" gem 'email_reply_parser', '~> 0.5.8' diff --git a/Gemfile.lock b/Gemfile.lock index 9911904d304..58426a60683 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -392,7 +392,7 @@ GEM systemu (~> 2.6.2) mail (2.6.3) mime-types (>= 1.16, < 3) - mail_room (0.5.2) + mail_room (0.6.0) method_source (0.8.2) mime-types (1.25.1) mimemagic (0.3.0) @@ -854,7 +854,7 @@ DEPENDENCIES jquery-ui-rails (~> 4.2.1) kaminari (~> 0.16.3) letter_opener (~> 1.1.2) - mail_room (~> 0.5.2) + mail_room (~> 0.6.0) minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example index ed4a5193a6a..bb624e8a187 100644 --- a/config/mail_room.yml.example +++ b/config/mail_room.yml.example @@ -12,8 +12,10 @@ # :email: "gitlab-incoming@gmail.com" # # Email account password # :password: "password" + # # The name of the mailbox where incoming mail will end up. Usually "inbox". # :name: "inbox" + # # Always "sidekiq". # :delivery_method: sidekiq # # Always true. @@ -25,5 +27,13 @@ # :namespace: resque:gitlab # # Always "incoming_email". # :queue: incoming_email - # # Always "EmailReceiverWorker" + # # Always "EmailReceiverWorker". # :worker: EmailReceiverWorker + + # # Always "redis". + # :arbitration_method: redis + # :arbitration_options: + # # The URL to the Redis server. Should match the URL in config/resque.yml. + # :redis_url: redis://localhost:6379 + # # Always "mail_room:gitlab". + # :namespace: mail_room:gitlab diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md index 87267423471..076c8c049a8 100644 --- a/doc/incoming_email/README.md +++ b/doc/incoming_email/README.md @@ -118,8 +118,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :email: "incoming" # Email account password :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". :name: "inbox" + # Always "sidekiq". :delivery_method: sidekiq # Always true. @@ -133,6 +135,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :queue: incoming_email # Always "EmailReceiverWorker" :worker: EmailReceiverWorker + + # Always "redis". + :arbitration_method: redis + :arbitration_options: + # The URL to the Redis server. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "mail_room:gitlab". + :namespace: mail_room:gitlab ``` ```yaml @@ -151,8 +161,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :email: "gitlab-incoming@gmail.com" # Email account password :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". :name: "inbox" + # Always "sidekiq". :delivery_method: sidekiq # Always true. @@ -166,6 +178,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :queue: incoming_email # Always "EmailReceiverWorker" :worker: EmailReceiverWorker + + # Always "redis". + :arbitration_method: redis + :arbitration_options: + # The URL to the Redis server. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "mail_room:gitlab". + :namespace: mail_room:gitlab ``` 5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`: @@ -228,8 +248,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :email: "gitlab-incoming@gmail.com" # Email account password :password: "[REDACTED]" + # The name of the mailbox where incoming mail will end up. Usually "inbox". :name: "inbox" + # Always "sidekiq". :delivery_method: sidekiq # Always true. @@ -243,6 +265,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these :queue: incoming_email # Always "EmailReceiverWorker" :worker: EmailReceiverWorker + + # Always "redis". + :arbitration_method: redis + :arbitration_options: + # The URL to the Redis server. Should match the URL in config/resque.yml. + :redis_url: redis://localhost:6379 + # Always "mail_room:gitlab". + :namespace: mail_room:gitlab ``` 4. Uncomment the `mail_room` line in your `Procfile`: From 914cfbd2f154ed3154a7dc3cee3309713eea786f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 6 Oct 2015 12:01:16 +0200 Subject: [PATCH 075/119] Implement Commit Status API --- CHANGELOG | 1 + app/models/ability.rb | 2 + app/models/ci/build.rb | 87 ++-------- app/models/ci/commit.rb | 88 +++++----- app/models/commit.rb | 8 + app/models/commit_status.rb | 79 +++++++++ app/models/generic_commit_status.rb | 15 ++ app/services/ci/create_commit_service.rb | 2 + app/views/projects/builds/_build.html.haml | 50 ------ app/views/projects/builds/show.html.haml | 6 +- app/views/projects/commit/ci.html.haml | 38 ++++- .../commit_statuses/_commit_status.html.haml | 52 ++++++ ...3042_add_type_and_description_to_builds.rb | 9 + ..._migrate_name_to_description_for_builds.rb | 5 + db/schema.rb | 7 +- doc/api/commits.md | 3 +- lib/api/api.rb | 1 + lib/api/commit_statuses.rb | 79 +++++++++ lib/api/entities.rb | 8 +- lib/ci/api/entities.rb | 4 +- spec/factories/ci/builds.rb | 1 + spec/factories/commit_statuses.rb | 15 ++ spec/features/commits_spec.rb | 1 + spec/models/{ci => }/build_spec.rb | 111 ------------- spec/models/ci/commit_spec.rb | 57 +++++-- .../ci/project_services/mail_service_spec.rb | 8 +- spec/models/commit_status_spec.rb | 157 ++++++++++++++++++ spec/models/generic_commit_status_spec.rb | 39 +++++ spec/requests/api/commit_status_spec.rb | 135 +++++++++++++++ spec/requests/api/commits_spec.rb | 13 ++ 30 files changed, 768 insertions(+), 313 deletions(-) create mode 100644 app/models/commit_status.rb create mode 100644 app/models/generic_commit_status.rb delete mode 100644 app/views/projects/builds/_build.html.haml create mode 100644 app/views/projects/commit_statuses/_commit_status.html.haml create mode 100644 db/migrate/20151008123042_add_type_and_description_to_builds.rb create mode 100644 db/migrate/20151008130321_migrate_name_to_description_for_builds.rb create mode 100644 lib/api/commit_statuses.rb create mode 100644 spec/factories/commit_statuses.rb rename spec/models/{ci => }/build_spec.rb (74%) create mode 100644 spec/models/commit_status_spec.rb create mode 100644 spec/models/generic_commit_status_spec.rb create mode 100644 spec/requests/api/commit_status_spec.rb diff --git a/CHANGELOG b/CHANGELOG index a3d796bea66..e38d465dd96 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.1.0 (unreleased) - Move CI charts to project graphs area - Fix cases where Markdown did not render links in activity feed (Stan Hu) - Add first and last to pagination (Zeger-Jan van de Weg) + - Added Commit Status API - Show CI status on commit page - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard diff --git a/app/models/ability.rb b/app/models/ability.rb index a020b24a550..77c121ca5e8 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -135,6 +135,8 @@ class Ability def project_report_rules project_guest_rules + [ + :create_commit_status, + :read_commit_statuses, :download_code, :fork_project, :create_project_snippet, diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 5d17f4418ed..41ce522b2ff 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -24,32 +24,19 @@ # module Ci - class Build < ActiveRecord::Base - extend Ci::Model - + class Build < CommitStatus LAZY_ATTRIBUTES = ['trace'] - belongs_to :commit, class_name: 'Ci::Commit' belongs_to :runner, class_name: 'Ci::Runner' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' - belongs_to :user serialize :options - validates :commit, presence: true - validates :status, presence: true validates :coverage, numericality: true, allow_blank: true validates_presence_of :ref - scope :running, ->() { where(status: "running") } - scope :pending, ->() { where(status: "pending") } - scope :success, ->() { where(status: "success") } - scope :failed, ->() { where(status: "failed") } scope :unstarted, ->() { where(runner_id: nil) } - scope :running_or_pending, ->() { where(status:[:running, :pending]) } - scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) } scope :ignore_failures, ->() { where(allow_failure: false) } - scope :for_ref, ->(ref) { where(ref: ref) } scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) } acts_as_taggable @@ -74,13 +61,14 @@ module Ci def create_from(build) new_build = build.dup - new_build.status = :pending + new_build.status = 'pending' new_build.runner_id = nil + new_build.trigger_request_id = nil new_build.save end def retry(build) - new_build = Ci::Build.new(status: :pending) + new_build = Ci::Build.new(status: 'pending') new_build.ref = build.ref new_build.tag = build.tag new_build.options = build.options @@ -98,28 +86,7 @@ module Ci end state_machine :status, initial: :pending do - event :run do - transition pending: :running - end - - event :drop do - transition running: :failed - end - - event :success do - transition running: :success - end - - event :cancel do - transition [:pending, :running] => :canceled - end - - after_transition pending: :running do |build, transition| - build.update_attributes started_at: Time.now - end - after_transition any => [:success, :failed, :canceled] do |build, transition| - build.update_attributes finished_at: Time.now project = build.project if project.web_hooks? @@ -136,19 +103,10 @@ module Ci build.update_coverage end end - - state :pending, value: 'pending' - state :running, value: 'running' - state :failed, value: 'failed' - state :success, value: 'success' - state :canceled, value: 'canceled' end - delegate :sha, :short_sha, :project, :gl_project, - to: :commit, prefix: false - - def before_sha - Gitlab::Git::BLANK_SHA + def ignored? + failed? && allow_failure? end def trace_html @@ -156,22 +114,6 @@ module Ci html || '' end - def started? - !pending? && !canceled? && started_at - end - - def active? - running? || pending? - end - - def complete? - canceled? || success? || failed? - end - - def ignored? - failed? && allow_failure? - end - def timeout project.timeout end @@ -180,14 +122,6 @@ module Ci yaml_variables + project_variables + trigger_variables end - def duration - if started_at && finished_at - finished_at - started_at - elsif started_at - Time.now - started_at - end - end - def project commit.project end @@ -278,6 +212,15 @@ module Ci "#{dir_to_trace}/#{id}.log" end + def description + name + end + + def target_url + Gitlab::Application.routes.url_helpers. + namespace_project_build_url(gl_project.namespace, gl_project, self) + end + private def yaml_variables diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index fde754a92a1..042a68681bb 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -20,7 +20,8 @@ module Ci extend Ci::Model belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id - has_many :builds, dependent: :destroy, class_name: 'Ci::Build' + has_many :statuses, dependent: :destroy, class_name: 'CommitStatus' + has_many :builds, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' validates_presence_of :sha @@ -81,12 +82,11 @@ module Ci end def stage - running_or_pending = builds_without_retry.running_or_pending - running_or_pending.limit(1).pluck(:stage).first + running_or_pending = statuses.latest.running_or_pending + running_or_pending.first.try(:stage) end def create_builds(ref, tag, user, trigger_request = nil) - return if skip_ci? && trigger_request.blank? return unless config_processor config_processor.stages.any? do |stage| CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present? @@ -94,7 +94,6 @@ module Ci end def create_next_builds(ref, tag, user, trigger_request) - return if skip_ci? && trigger_request.blank? return unless config_processor stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage) @@ -107,39 +106,47 @@ module Ci end def refs - builds.group(:ref).pluck(:ref) + statuses.pluck(:ref).compact.uniq end - def last_ref - builds.latest.first.try(:ref) + def statuses_for_ref(ref = nil) + if ref + statuses.for_ref(ref) + else + statuses + end end - def builds_without_retry - builds.latest + def builds_without_retry(ref = nil) + if ref + builds.for_ref(ref).latest + else + builds.latest + end end - def builds_without_retry_for_ref(ref) - builds.for_ref(ref).latest + def retried + @retried ||= (statuses.order(id: :desc) - statuses.latest) end - def retried_builds - @retried_builds ||= (builds.order(id: :desc) - builds_without_retry) - end - - def status - if skip_ci? - return 'skipped' - elsif yaml_errors.present? + def status(ref = nil) + if yaml_errors.present? return 'failed' - elsif builds.none? + end + + latest_statuses = statuses.latest.to_a + latest_statuses.reject! { |status| status.try(&:allow_failure?) } + latest_statuses.select! { |status| status.ref == nil || status.ref == ref } if ref + + if latest_statuses.none? return 'skipped' - elsif success? + elsif latest_statuses.all?(&:success?) 'success' - elsif pending? + elsif latest_statuses.all?(&:pending?) 'pending' - elsif running? + elsif latest_statuses.any?(&:running?) || latest_statuses.any?(&:pending?) 'running' - elsif canceled? + elsif latest_statuses.all?(&:canceled?) 'canceled' else 'failed' @@ -147,21 +154,15 @@ module Ci end def pending? - builds_without_retry.all? do |build| - build.pending? - end + status == 'pending' end def running? - builds_without_retry.any? do |build| - build.running? || build.pending? - end + status == 'running' end def success? - builds_without_retry.all? do |build| - build.success? || build.ignored? - end + status == 'success' end def failed? @@ -169,21 +170,15 @@ module Ci end def canceled? - builds_without_retry.all? do |build| - build.canceled? - end + status == 'canceled' end - def duration - @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i - end - - def duration_for_ref(ref) - builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i + def duration(ref = nil) + statuses_for_ref(ref).latest.select(&:duration).sum(&:duration).to_i end def finished_at - @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at) + @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at) end def coverage @@ -195,8 +190,8 @@ module Ci end end - def matrix_for_ref?(ref) - builds_without_retry_for_ref(ref).pluck(:id).size > 1 + def matrix?(ref) + builds_without_retry(ref).pluck(:id).size > 1 end def config_processor @@ -217,7 +212,6 @@ module Ci end def skip_ci? - return false if builds.any? git_commit_message =~ /(\[ci skip\])/ if git_commit_message end diff --git a/app/models/commit.rb b/app/models/commit.rb index aff329d71fa..d5c50013525 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -184,4 +184,12 @@ class Commit def parents @parents ||= Commit.decorate(super, project) end + + def ci_commit + project.ci_commit(sha) + end + + def status + ci_commit.try(:status) || :not_found + end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb new file mode 100644 index 00000000000..4c6de18527b --- /dev/null +++ b/app/models/commit_status.rb @@ -0,0 +1,79 @@ +class CommitStatus < ActiveRecord::Base + self.table_name = 'ci_builds' + + belongs_to :commit, class_name: 'Ci::Commit' + belongs_to :user + + validates :commit, presence: true + validates :status, inclusion: {in: %w(pending running failed success canceled)} + + validates_presence_of :name + + scope :running, ->() { where(status: 'running') } + scope :pending, ->() { where(status: 'pending') } + scope :success, ->() { where(status: 'success') } + scope :failed, ->() { where(status: 'failed') } + scope :running_or_pending, ->() { where(status:[:running, :pending]) } + scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) } + scope :for_ref, ->(ref) { where(ref: [ref, nil]) } + scope :running_or_pending, ->() { where(status: [:running, :pending]) } + + state_machine :status, initial: :pending do + event :run do + transition pending: :running + end + + event :drop do + transition running: :failed + end + + event :success do + transition [:pending, :running] => :success + end + + event :cancel do + transition [:pending, :running] => :canceled + end + + after_transition pending: :running do |build, transition| + build.update_attributes started_at: Time.now + end + + after_transition any => [:success, :failed, :canceled] do |build, transition| + build.update_attributes finished_at: Time.now + end + + state :pending, value: 'pending' + state :running, value: 'running' + state :failed, value: 'failed' + state :success, value: 'success' + state :canceled, value: 'canceled' + end + + delegate :sha, :short_sha, :gl_project, + to: :commit, prefix: false + + def before_sha + Gitlab::Git::BLANK_SHA + end + + def started? + !pending? && !canceled? && started_at + end + + def active? + running? || pending? + end + + def complete? + canceled? || success? || failed? + end + + def duration + if started_at && finished_at + finished_at - started_at + elsif started_at + Time.now - started_at + end + end +end diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb new file mode 100644 index 00000000000..fa54e3540d0 --- /dev/null +++ b/app/models/generic_commit_status.rb @@ -0,0 +1,15 @@ +class GenericCommitStatus < CommitStatus + before_validation :set_default_values + + # GitHub compatible API + alias_attribute :context, :name + + def set_default_values + self.context ||= 'default' + self.stage ||= 'external' + end + + def tags + [:external] + end +end diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb index fc1ae5774d5..0ae35387579 100644 --- a/app/services/ci/create_commit_service.rb +++ b/app/services/ci/create_commit_service.rb @@ -17,6 +17,8 @@ module Ci tag = origin_ref.start_with?('refs/tags/') commit = project.gl_project.ensure_ci_commit(sha) + return false if commit.skip_ci? + commit.update_committed! commit.create_builds(ref, tag, user) diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml deleted file mode 100644 index 65fd9413b60..00000000000 --- a/app/views/projects/builds/_build.html.haml +++ /dev/null @@ -1,50 +0,0 @@ -- gl_project = build.project.gl_project -%tr.build - %td.status - = ci_status_with_icon(build.status) - - %td.build-link - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do - %strong Build ##{build.id} - - - if defined?(ref) - %td - = build.ref - - %td - = build.stage - - %td - = build.name - .pull-right - - if build.tags.any? - - build.tag_list.each do |tag| - %span.label.label-primary - = tag - - if build.trigger_request - %span.label.label-info triggered - - if build.allow_failure - %span.label.label-danger allowed to fail - - %td.duration - - if build.duration - #{duration_in_words(build.finished_at, build.started_at)} - - %td.timestamp - - if build.finished_at - %span #{time_ago_in_words build.finished_at} ago - - - if build.project.coverage_enabled? - %td.coverage - - if build.coverage - #{build.coverage}% - - %td - - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project) - .pull-right - - if build.active? - = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), title: 'Cancel build' do - %i.fa.fa-remove.cred - - elsif build.commands.present? - = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), method: :post, title: 'Retry build' do - %i.fa.fa-repeat diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index b561078e8c7..66e668f3771 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -7,9 +7,9 @@ %code #{@build.ref} #up-build-trace - - if @commit.matrix_for_ref?(@build.ref) + - if @commit.matrix?(@build.ref) %ul.center-top-menu.build-top-menu - - @commit.builds_without_retry_for_ref(@build.ref).each do |build| + - @commit.builds_without_retry(@build.ref).each do |build| %li{class: ('active' if build == @build) } = link_to namespace_project_build_path(@project.namespace, @project, build) do = ci_icon_for_status(build.status) @@ -20,7 +20,7 @@ = build.id - - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build) + - unless @commit.builds_without_retry(@build.ref).include?(@build) %li.active %a Build ##{@build.id} diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index 26ab38445c2..7f106631cac 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -20,13 +20,13 @@ .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit -- @ci_commit.refs.each do |ref| +- if @ci_commit.refs.blank? .gray-content-block.second-block - Builds for #{ref} - - if @ci_commit.duration_for_ref(ref) > 0 + Latest builds + - if @ci_commit.duration > 0 %small.pull-right %i.fa.fa-time - #{time_interval_in_words @ci_commit.duration_for_ref(ref)} + #{time_interval_in_words @ci_commit.duration} %table.table.builds %thead @@ -40,10 +40,32 @@ - if @ci_project && @ci_project.coverage_enabled? %th Coverage %th - = render partial: "projects/builds/build", collection: @ci_commit.builds_without_retry.for_ref(ref), controls: true + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.latest, coverage: @ci_project.try(:coverage_enabled?), controls: true -- if @ci_commit.retried_builds.any? - %h3 +- @ci_commit.refs.sort.each do |ref| + .gray-content-block.second-block + Builds for #{ref} + - if @ci_commit.duration(ref) > 0 + %small.pull-right + %i.fa.fa-time + #{time_interval_in_words @ci_commit.duration(ref)} + + %table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Stage + %th Name + %th Duration + %th Finished at + - if @ci_project && @ci_project.coverage_enabled? + %th Coverage + %th + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest, coverage: @ci_project.try(:coverage_enabled?), controls: true + +- if @ci_commit.retried.any? + .gray-content-block.second-block Retried builds %table.table.builds @@ -59,4 +81,4 @@ - if @ci_project && @ci_project.coverage_enabled? %th Coverage %th - = render partial: "projects/builds/build", collection: @ci_commit.retried_builds, ref: true + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?), ref: true diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml new file mode 100644 index 00000000000..f79929c70bf --- /dev/null +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -0,0 +1,52 @@ +%tr.commit_status + %td.status + = ci_status_with_icon(commit_status.status) + + %td.commit_status-link + - if commit_status.target_url + = link_to commit_status.target_url do + %strong Build ##{commit_status.id} + - else + %strong Build ##{commit_status.id} + + - if defined?(ref) + %td + = commit_status.ref + + %td + = commit_status.stage + + %td + = commit_status.description + .pull-right + - if commit_status.tags.any? + - commit_status.tags.each do |tag| + %span.label.label-primary + = tag + - if commit_status.try(:trigger_request) + %span.label.label-info triggered + - if commit_status.try(:allow_failure) + %span.label.label-danger allowed to fail + + %td.duration + - if commit_status.duration + #{duration_in_words(commit_status.finished_at, commit_status.started_at)} + + %td.timestamp + - if commit_status.finished_at + %span #{time_ago_in_words commit_status.finished_at} ago + + - if defined?(coverage) + %td.coverage + - if commit_status.try(:coverage) + #{commit_status.coverage}% + + %td + - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project) + .pull-right + - if commit_status.active? + = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, commit_status, return_to: request.original_url), title: 'Cancel commit_status' do + %i.fa.fa-remove.cred + - elsif commit_status.commands.present? + = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, commit_status, return_to: request.original_url), method: :post, title: 'Retry commit_status' do + %i.fa.fa-repeat diff --git a/db/migrate/20151008123042_add_type_and_description_to_builds.rb b/db/migrate/20151008123042_add_type_and_description_to_builds.rb new file mode 100644 index 00000000000..c72b1c611c6 --- /dev/null +++ b/db/migrate/20151008123042_add_type_and_description_to_builds.rb @@ -0,0 +1,9 @@ +class AddTypeAndDescriptionToBuilds < ActiveRecord::Migration + def change + add_column :ci_builds, :type, :string + add_column :ci_builds, :target_url, :string + add_column :ci_builds, :description, :string + add_index :ci_builds, [:commit_id, :type, :ref] + add_index :ci_builds, [:commit_id, :type, :name, :ref] + end +end diff --git a/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb new file mode 100644 index 00000000000..f5c44babd84 --- /dev/null +++ b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb @@ -0,0 +1,5 @@ +class MigrateNameToDescriptionForBuilds < ActiveRecord::Migration + def change + execute("UPDATE ci_builds SET type='Ci::Build' WHERE type IS NULL") + end +end diff --git a/db/schema.rb b/db/schema.rb index c5c462c2e57..7a11dfca034 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: 20151007120511) do +ActiveRecord::Schema.define(version: 20151008130321) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -103,9 +103,14 @@ ActiveRecord::Schema.define(version: 20151007120511) do t.boolean "tag" t.string "ref" t.integer "user_id" + t.string "type" + t.string "target_url" + t.string "description" end add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree + add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree + add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree diff --git a/doc/api/commits.md b/doc/api/commits.md index eb8d6a43592..b22d040bf0d 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -62,7 +62,8 @@ Parameters: "authored_date": "2012-09-20T09:06:12+03:00", "parent_ids": [ "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" - ] + ], + "status": "running" } ``` diff --git a/lib/api/api.rb b/lib/api/api.rb index c09488d3547..afc0402f9e1 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -46,6 +46,7 @@ module API mount Services mount Files mount Commits + mount CommitStatus mount Namespaces mount Branches mount Labels diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb new file mode 100644 index 00000000000..cfe8739b175 --- /dev/null +++ b/lib/api/commit_statuses.rb @@ -0,0 +1,79 @@ +require 'mime/types' + +module API + # Project commit statuses API + class CommitStatus < Grape::API + resource :projects do + before { authenticate! } + before { authorize! :read_commit_statuses, user_project } + + # Get a commit's statuses + # + # Parameters: + # id (required) - The ID of a project + # sha (required) - The commit hash + # ref (optional) - The ref + # stage (optional) - The stage + # name (optional) - The name + # all (optional) - Show all statuses, default: false + # Examples: + # GET /projects/:id/repository/commits/:sha/statuses + get ':id/repository/commits/:sha/statuses' do + sha = params[:sha] + ci_commit = user_project.ci_commit(sha) + not_found! 'Commit' unless ci_commit + statuses = ci_commit.statuses + statuses = statuses.latest unless parse_boolean(params[:all]) + statuses = statuses.where(ref: params[:ref]) if params[:ref].present? + statuses = statuses.where(name: params[:stage]) if params[:stage].present? + statuses = statuses.where(name: params[:name]) if params[:name].present? + present paginate(statuses), with: Entities::CommitStatus + end + + # Post status to commit + # + # Parameters: + # id (required) - The ID of a project + # sha (required) - The commit hash + # ref (optional) - The ref + # state (required) - The state of the status. Can be: pending, running, success, error or failure + # target_url (optional) - The target URL to associate with this status + # description (optional) - A short description of the status + # name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default" + # Examples: + # POST /projects/:id/repository/commits/:sha/status + post ':id/statuses/:sha' do + required_attributes! [:state] + attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name] + commit = @project.commit(params[:sha]) + not_found! 'Commit' unless commit + + ci_commit = @project.ensure_ci_commit(commit.sha) + + name = params[:name] || params[:context] + status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref]) + status = GenericCommitStatus.new(commit: ci_commit) unless status + status.update(attrs) + + case params[:state].to_s + when 'running' + status.run + when 'success' + status.success + when 'failed' + status.drop + when 'canceled' + status.cancel + else + status.status = params[:state].to_s + end + + if status.save + present status, with: Entities::CommitStatus + else + render_validation_error!(status) + end + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9620d36ac41..e1c5a459751 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -65,7 +65,7 @@ module API expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at expose :creator_id expose :namespace - expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } + expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda { |project, options| project.forked? } expose :avatar_url expose :star_count, :forks_count end @@ -149,6 +149,7 @@ module API class RepoCommitDetail < RepoCommit expose :parent_ids, :committed_date, :authored_date + expose :status end class ProjectSnippet < Grape::Entity @@ -228,6 +229,11 @@ module API expose :created_at end + class CommitStatus < Grape::Entity + expose :id, :sha, :ref, :status, :name, :target_url, :description, + :created_at, :started_at, :finished_at + end + class Event < Grape::Entity expose :title, :project_id, :action_name expose :target_id, :target_type, :author_id diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index f47bc1236b8..b80c0b8b273 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -2,7 +2,7 @@ module Ci module API module Entities class Commit < Grape::Entity - expose :id, :ref, :sha, :project_id, :before_sha, :created_at + expose :id, :sha, :project_id, :created_at expose :status, :finished_at, :duration expose :git_commit_message, :git_author_name, :git_author_email end @@ -12,7 +12,7 @@ module Ci end class Build < Grape::Entity - expose :id, :commands, :ref, :sha, :project_id, :repo_url, + expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url, :before_sha, :allow_git_fetch, :project_name expose :options do |model| diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 21b582afba4..2fcd70182b9 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -27,6 +27,7 @@ FactoryGirl.define do factory :ci_build, class: Ci::Build do + name 'test' ref 'master' tag false started_at 'Di 29. Okt 09:51:28 CET 2013' diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb new file mode 100644 index 00000000000..52de437052d --- /dev/null +++ b/spec/factories/commit_statuses.rb @@ -0,0 +1,15 @@ +FactoryGirl.define do + factory :commit_status, class: CommitStatus do + started_at 'Di 29. Okt 09:51:28 CET 2013' + finished_at 'Di 29. Okt 09:53:28 CET 2013' + name 'default' + status 'success' + description 'commit status' + commit factory: :ci_commit + + factory :generic_commit_status, class: GenericCommitStatus do + name 'generic' + description 'external commit status' + end + end +end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 5da220859e3..cbb6360069b 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -12,6 +12,7 @@ describe "Commits" do @ci_project = project.ensure_gitlab_ci_project @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha @build = FactoryGirl.create :ci_build, commit: @commit + @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit end before do diff --git a/spec/models/ci/build_spec.rb b/spec/models/build_spec.rb similarity index 74% rename from spec/models/ci/build_spec.rb rename to spec/models/build_spec.rb index da56f6e31ae..d875015b991 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/build_spec.rb @@ -30,17 +30,9 @@ describe Ci::Build do let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } let(:build) { FactoryGirl.create :ci_build, commit: commit } - subject { build } - it { is_expected.to belong_to(:commit) } - it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of :status } it { is_expected.to validate_presence_of :ref } - it { is_expected.to respond_to :success? } - it { is_expected.to respond_to :failed? } - it { is_expected.to respond_to :running? } - it { is_expected.to respond_to :pending? } it { is_expected.to respond_to :trace_html } describe :first_pending do @@ -67,72 +59,6 @@ describe Ci::Build do end end - describe :started? do - subject { build.started? } - - context 'without started_at' do - before { build.started_at = nil } - - it { is_expected.to be_falsey } - end - - %w(running success failed).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_truthy } - end - end - - %w(pending canceled).each do |status| - context "if build status is #{status}" do - before { build.status = status } - - it { is_expected.to be_falsey } - end - end - end - - describe :active? do - subject { build.active? } - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - - describe :complete? do - subject { build.complete? } - - %w(success failed canceled).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_truthy } - end - end - - %w(pending running).each do |state| - context "if build.status is #{state}" do - before { build.status = state } - - it { is_expected.to be_falsey } - end - end - end - describe :ignored? do subject { build.ignored? } @@ -200,31 +126,6 @@ describe Ci::Build do it { is_expected.to eq(commit.project.timeout) } end - describe :duration do - subject { build.duration } - - it { is_expected.to eq(120.0) } - - context 'if the building process has not started yet' do - before do - build.started_at = nil - build.finished_at = nil - end - - it { is_expected.to be_nil } - end - - context 'if the building process has started' do - before do - build.started_at = Time.now - 1.minute - build.finished_at = nil - end - - it { is_expected.to be_a(Float) } - it { is_expected.to be > 0.0 } - end - end - describe :options do let(:options) do { @@ -239,18 +140,6 @@ describe Ci::Build do it { is_expected.to eq(options) } end - describe :sha do - subject { build.sha } - - it { is_expected.to eq(commit.sha) } - end - - describe :short_sha do - subject { build.short_sha } - - it { is_expected.to eq(commit.short_sha) } - end - describe :allow_git_fetch do subject { build.allow_git_fetch } diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index acff1ddf0fc..371add4ee59 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -23,6 +23,8 @@ describe Ci::Commit do let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } it { is_expected.to belong_to(:gl_project) } + it { is_expected.to have_many(:statuses) } + it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:builds) } it { is_expected.to validate_presence_of :sha } @@ -47,10 +49,12 @@ describe Ci::Commit do @second = FactoryGirl.create :ci_build, commit: commit end - it "creates new build" do + it "creates only a new build" do expect(commit.builds.count(:all)).to eq 2 + expect(commit.statuses.count(:all)).to eq 2 commit.retry expect(commit.builds.count(:all)).to eq 3 + expect(commit.statuses.count(:all)).to eq 3 end end @@ -78,8 +82,8 @@ describe Ci::Commit do subject { commit.stage } before do - @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending - @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending + @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending' + @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending' end it 'returns first running stage' do @@ -88,7 +92,7 @@ describe Ci::Commit do context 'first build succeeded' do before do - @first.update_attributes(status: :success) + @first.success end it 'returns last running stage' do @@ -98,8 +102,8 @@ describe Ci::Commit do context 'all builds succeeded' do before do - @first.update_attributes(status: :success) - @second.update_attributes(status: :success) + @first.success + @second.success end it 'returns nil' do @@ -111,6 +115,33 @@ describe Ci::Commit do describe :create_next_builds do end + describe :refs do + subject { commit.refs } + + before do + FactoryGirl.create :commit_status, commit: commit, name: 'deploy' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop' + FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master' + end + + it 'returns all refs' do + is_expected.to contain_exactly('master', 'develop') + end + end + + describe :retried do + subject { commit.retried } + + before do + @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy' + end + + it 'returns old builds' do + is_expected.to contain_exactly(@commit1) + end + end + describe :create_builds do let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } @@ -252,10 +283,10 @@ describe Ci::Commit do describe :should_create_next_builds? do before do - @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success - @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed - @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed - @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success + @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: 'success' + @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: 'failed' + @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: 'failed' + @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'success' end context 'for success' do @@ -266,7 +297,7 @@ describe Ci::Commit do context 'for failed' do before do - @build4.update_attributes(status: :failed) + @build4.update_attributes(status: 'failed') end it 'to not create' do @@ -286,7 +317,7 @@ describe Ci::Commit do context 'for running' do before do - @build4.update_attributes(status: :running) + @build4.update_attributes(status: 'running') end it 'to not create' do @@ -296,7 +327,7 @@ describe Ci::Commit do context 'for retried' do before do - @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed + @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'failed' end it 'to not create' do diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index 04e870dce7f..2ce5c2b4707 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -58,7 +58,7 @@ describe Ci::MailService do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -86,7 +86,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -115,7 +115,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -144,7 +144,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) } before do allow(mail).to receive_messages( diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb new file mode 100644 index 00000000000..c1af50afb7c --- /dev/null +++ b/spec/models/commit_status_spec.rb @@ -0,0 +1,157 @@ +require 'spec_helper' + +describe CommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:commit_status) { FactoryGirl.create :commit_status, commit: commit } + + it { is_expected.to belong_to(:commit) } + it { is_expected.to belong_to(:user) } + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) } + + it { is_expected.to delegate_method(:sha).to(:commit) } + it { is_expected.to delegate_method(:short_sha).to(:commit) } + it { is_expected.to delegate_method(:gl_project).to(:commit) } + + it { is_expected.to respond_to :success? } + it { is_expected.to respond_to :failed? } + it { is_expected.to respond_to :running? } + it { is_expected.to respond_to :pending? } + + describe :started? do + subject { commit_status.started? } + + context 'without started_at' do + before { commit_status.started_at = nil } + + it { is_expected.to be_falsey } + end + + %w(running success failed).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + it { is_expected.to be_truthy } + end + end + + %w(pending canceled).each do |status| + context "if commit status is #{status}" do + before { commit_status.status = status } + + it { is_expected.to be_falsey } + end + end + end + + describe :active? do + subject { commit_status.active? } + + %w(pending running).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(success failed canceled).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_falsey } + end + end + end + + describe :complete? do + subject { commit_status.complete? } + + %w(success failed canceled).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_truthy } + end + end + + %w(pending running).each do |state| + context "if commit_status.status is #{state}" do + before { commit_status.status = state } + + it { is_expected.to be_falsey } + end + end + end + + describe :duration do + subject { commit_status.duration } + + it { is_expected.to eq(120.0) } + + context 'if the building process has not started yet' do + before do + commit_status.started_at = nil + commit_status.finished_at = nil + end + + it { is_expected.to be_nil } + end + + context 'if the building process has started' do + before do + commit_status.started_at = Time.now - 1.minute + commit_status.finished_at = nil + end + + it { is_expected.to be_a(Float) } + it { is_expected.to be > 0.0 } + end + end + + describe :latest do + subject { CommitStatus.latest.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success' + end + + it 'return unique statuses' do + is_expected.to eq([@commit2, @commit3, @commit4, @commit5]) + end + end + + describe :for_ref do + subject { CommitStatus.for_ref('bb').order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + end + + it 'return statuses with equal and nil ref set' do + is_expected.to eq([@commit1, @commit3]) + end + end + + describe :running_or_pending do + subject { CommitStatus.running_or_pending.order(:id) } + + before do + @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' + @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' + @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled' + end + + it 'return statuses that are running or pending' do + is_expected.to eq([@commit1, @commit2]) + end + end +end diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb new file mode 100644 index 00000000000..f442fa5fbe5 --- /dev/null +++ b/spec/models/generic_commit_status_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe GenericCommitStatus do + let(:commit) { FactoryGirl.create :ci_commit } + let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit } + + describe :context do + subject { generic_commit_status.context } + before { generic_commit_status.context = 'my_context' } + + it { is_expected.to eq(generic_commit_status.name) } + end + + describe :tags do + subject { generic_commit_status.tags } + + it { is_expected.to eq([:external]) } + end + + describe :set_default_values do + before do + generic_commit_status.context = nil + generic_commit_status.stage = nil + generic_commit_status.save + end + + describe :context do + subject { generic_commit_status.context } + + it { is_expected.to_not be_nil } + end + + describe :stage do + subject { generic_commit_status.stage } + + it { is_expected.to_not be_nil } + end + end +end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb new file mode 100644 index 00000000000..d1dc97e1846 --- /dev/null +++ b/spec/requests/api/commit_status_spec.rb @@ -0,0 +1,135 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) } + let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + let(:commit) { project.repository.commit } + let!(:ci_commit) { project.ensure_ci_commit(commit.id) } + let(:commit_status) { create(:commit_status, commit: ci_commit) } + + describe "GET /projects/:id/repository/commits/:sha/statuses" do + context "reporter user" do + let(:statuses_id) { json_response.map { |status| status['id'] } } + + before do + @status1 = create(:commit_status, commit: ci_commit, status: 'running') + @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending') + @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running') + @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success') + @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success') + @status6 = create(:commit_status, commit: ci_commit, status: 'success') + end + + it "should return latest commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return all commit statuses" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id) + end + + it "should return latest commit statuses for specific ref" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status5.id) + end + + it "should return latest commit statuses for specific name" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user) + expect(response.status).to eq(200) + + expect(json_response).to be_an Array + expect(statuses_id).to contain_exactly(@status3.id, @status4.id) + end + end + + context "guest user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2) + expect(response.status).to eq(403) + end + end + + context "unauthorized user" do + it "should not return project commits" do + get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses") + expect(response.status).to eq(401) + end + end + end + + describe 'POST /projects/:id/repository/commits/:sha/status' do + let(:post_url) { "/projects/#{project.id}/repository/commits/#{commit.id}/status" } + + context 'reporter user' do + context 'should create commit status' do + it 'with only required parameters' do + post api(post_url, user), state: 'success' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('default') + expect(json_response['ref']).to be_nil + expect(json_response['target_url']).to be_nil + expect(json_response['description']).to be_nil + end + + it 'with all optional parameters' do + post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test' + expect(response.status).to eq(201) + expect(json_response['sha']).to eq(commit.id) + expect(json_response['status']).to eq('success') + expect(json_response['name']).to eq('coverage') + expect(json_response['ref']).to eq('develop') + expect(json_response['target_url']).to eq('url') + expect(json_response['description']).to eq('test') + end + end + + context 'should not create commit status' do + it 'with invalid state' do + post api(post_url, user), state: 'invalid' + expect(response.status).to eq(400) + end + + it 'without state' do + post api(post_url, user) + expect(response.status).to eq(400) + end + + it 'invalid commit' do + post api("/projects/#{project.id}/repository/commits/invalid_sha/status", user), state: 'running' + expect(response.status).to eq(404) + end + end + end + + context 'guest user' do + it 'should not create commit status' do + post api(post_url, user2) + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not create commit status' do + post api(post_url) + expect(response.status).to eq(401) + end + end + end +end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index a1c248c636e..49acc3368f4 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -47,6 +47,19 @@ describe API::API, api: true do get api("/projects/#{project.id}/repository/commits/invalid_sha", user) expect(response.status).to eq(404) end + + it "should return not_found for CI status" do + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq('not_found') + end + + it "should return status for CI" do + ci_commit = project.ensure_ci_commit(project.repository.commit.sha) + get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) + expect(response.status).to eq(200) + expect(json_response['status']).to eq(ci_commit.status) + end end context "unauthorized user" do From d805c5dbb3c2fde0a3b97f86a2dd3abc35e72bd9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Oct 2015 19:08:37 +0300 Subject: [PATCH 076/119] Add spellcheck=false to certain input fields --- app/views/admin/users/index.html.haml | 2 +- app/views/ci/admin/runners/index.html.haml | 2 +- app/views/ci/admin/runners/show.html.haml | 2 +- app/views/dashboard/projects/_projects.html.haml | 2 +- app/views/explore/groups/index.html.haml | 2 +- app/views/explore/projects/_filter.html.haml | 2 +- app/views/groups/_projects.html.haml | 2 +- app/views/groups/group_members/index.html.haml | 2 +- app/views/layouts/_search.html.haml | 2 +- app/views/projects/project_members/index.html.haml | 2 +- app/views/search/_form.html.haml | 2 +- app/views/shared/issuable/_search_form.html.haml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index e3698ac1c46..71e15d555d3 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -32,7 +32,7 @@ %hr = form_tag admin_users_path, method: :get, class: 'form-inline' do .form-group - = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control' + = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false = hidden_field_tag "filter", params[:filter] = button_tag class: 'btn btn-primary' do %i.fa.fa-search diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index b9d6703ff41..01ce81b4476 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -27,7 +27,7 @@ .pull-left = form_tag ci_admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do .form-group - = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token' + = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token', spellcheck: false = submit_tag 'Search', class: 'btn' .pull-right.light diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 5bb442cbf92..92787b2e6ac 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -76,7 +76,7 @@ %td = form_tag ci_admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do .form-group - = search_field_tag :search, params[:search], class: 'form-control' + = search_field_tag :search, params[:search], class: 'form-control', spellcheck: false = submit_tag 'Search', class: 'btn' %td diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index e09e032a7f1..d0194a17b01 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -1,7 +1,7 @@ .projects-list-holder .projects-search-form .input-group - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false - if current_user.can_create_project? %span.input-group-btn = link_to new_project_path, class: 'btn btn-green' do diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 83d4d321c83..fcb07b04083 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -11,7 +11,7 @@ = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f| = hidden_field_tag :sort, @sort .form-group - = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search" + = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false .form-group = button_tag 'Search', class: "btn btn-default" diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 5a3d689d1e5..2761272aa8a 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -1,7 +1,7 @@ .pull-left = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f| .form-group - = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search" + = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false .form-group = button_tag 'Search', class: "btn btn-success" diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 2b27a88794d..76da3276e47 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -1,7 +1,7 @@ .panel.panel-default.projects-list-holder .panel-heading.clearfix .input-group - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control' + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false - if can? current_user, :create_projects, @group %span.input-group-btn = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 3a6d07ebddf..fee4b0052b5 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -12,7 +12,7 @@ .clearfix.js-toggle-container = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' } + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false } = button_tag 'Search', class: 'btn' - if current_user && current_user.can?(:admin_group_member, @group) diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index e2d2dec7ab8..ceb64ce3157 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -1,6 +1,6 @@ .search = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f| - = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control" + = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control", spellcheck: false = hidden_field_tag :group_id, @group.try(:id) - if @project && @project.persisted? = hidden_field_tag :project_id, @project.id diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 9a0a824b811..82809bec5b8 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -5,7 +5,7 @@ .clearfix.js-toggle-container = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do .form-group - = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' } + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false } = button_tag 'Search', class: 'btn' - if can?(current_user, :admin_project_member, @project) diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml index 3938c545cad..17b0981f073 100644 --- a/app/views/search/_form.html.haml +++ b/app/views/search/_form.html.haml @@ -6,7 +6,7 @@ .search-holder.clearfix .input-group - = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true + = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true, spellcheck: false %span.input-group-btn = button_tag 'Search', class: "btn btn-primary" - unless params[:snippets].eql? 'true' diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml index 58c3de64b77..3a5ad00aa91 100644 --- a/app/views/shared/issuable/_search_form.html.haml +++ b/app/views/shared/issuable/_search_form.html.haml @@ -1,6 +1,6 @@ = form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do .append-right-10.hidden-xs.hidden-sm - = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input' } + = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input', spellcheck: false } = hidden_field_tag :state, params['state'] = hidden_field_tag :scope, params['scope'] = hidden_field_tag :assignee_id, params['assignee_id'] From 887494761ee41e8e647753e597fab534ed738058 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 12:15:35 +0200 Subject: [PATCH 077/119] Fix commit status POST URL --- lib/api/commit_statuses.rb | 2 +- spec/requests/api/commit_status_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index cfe8739b175..41c05334296 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -41,7 +41,7 @@ module API # description (optional) - A short description of the status # name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default" # Examples: - # POST /projects/:id/repository/commits/:sha/status + # POST /projects/:id/statuses/:sha post ':id/statuses/:sha' do required_attributes! [:state] attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name] diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index d1dc97e1846..b9e6dfc15a7 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -72,8 +72,8 @@ describe API::API, api: true do end end - describe 'POST /projects/:id/repository/commits/:sha/status' do - let(:post_url) { "/projects/#{project.id}/repository/commits/#{commit.id}/status" } + describe 'POST /projects/:id/statuses/:sha' do + let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" } context 'reporter user' do context 'should create commit status' do @@ -112,7 +112,7 @@ describe API::API, api: true do end it 'invalid commit' do - post api("/projects/#{project.id}/repository/commits/invalid_sha/status", user), state: 'running' + post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running' expect(response.status).to eq(404) end end From 7ef156a24292f98d89bc424cc245f00548831863 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 12:15:48 +0200 Subject: [PATCH 078/119] Add author to statuses --- app/models/commit_status.rb | 2 ++ lib/api/commit_statuses.rb | 2 +- lib/api/entities.rb | 1 + spec/models/commit_status_spec.rb | 7 +++++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 4c6de18527b..de7b29a649d 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -9,6 +9,8 @@ class CommitStatus < ActiveRecord::Base validates_presence_of :name + alias_attribute :author, :user + scope :running, ->() { where(status: 'running') } scope :pending, ->() { where(status: 'pending') } scope :success, ->() { where(status: 'success') } diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 41c05334296..c478b3dc245 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -52,7 +52,7 @@ module API name = params[:name] || params[:context] status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref]) - status = GenericCommitStatus.new(commit: ci_commit) unless status + status = GenericCommitStatus.new(commit: ci_commit, user: current_user) unless status status.update(attrs) case params[:state].to_s diff --git a/lib/api/entities.rb b/lib/api/entities.rb index e1c5a459751..bfb242bb6fd 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -232,6 +232,7 @@ module API class CommitStatus < Grape::Entity expose :id, :sha, :ref, :status, :name, :target_url, :description, :created_at, :started_at, :finished_at + expose :author, using: Entities::UserBasic end class Event < Grape::Entity diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index c1af50afb7c..cbefa7798de 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -18,6 +18,13 @@ describe CommitStatus do it { is_expected.to respond_to :running? } it { is_expected.to respond_to :pending? } + describe :author do + subject { commit_status.author } + before { commit_status.author = User.new } + + it { is_expected.to eq(commit_status.user) } + end + describe :started? do subject { commit_status.started? } From 07f60552728db7276ad24dafd1ff601ae49442d2 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Oct 2015 13:20:38 +0300 Subject: [PATCH 079/119] Invalidate stored service password if the endpoint URL is changed --- app/models/project_services/bamboo_service.rb | 7 +++ .../project_services/teamcity_service.rb | 7 +++ app/models/service.rb | 9 +++ .../project_services/bamboo_service_spec.rb | 56 +++++++++++++++++++ .../project_services/teamcity_service_spec.rb | 56 +++++++++++++++++++ spec/models/service_spec.rb | 23 ++++++++ 6 files changed, 158 insertions(+) create mode 100644 spec/models/project_services/bamboo_service_spec.rb create mode 100644 spec/models/project_services/teamcity_service_spec.rb diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index d8aedbd2ab4..5f5255ab487 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -40,12 +40,19 @@ class BambooService < CiService attr_accessor :response after_save :compose_service_hook, if: :activated? + before_update :reset_password def compose_service_hook hook = service_hook || build_service_hook hook.save end + def reset_password + if prop_updated?(:bamboo_url) + self.password = nil + end + end + def title 'Atlassian Bamboo CI' end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 3c002a1634b..fb11cad352e 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -37,12 +37,19 @@ class TeamcityService < CiService attr_accessor :response after_save :compose_service_hook, if: :activated? + before_update :reset_password def compose_service_hook hook = service_hook || build_service_hook hook.save end + def reset_password + if prop_updated?(:teamcity_url) + self.password = nil + end + end + def title 'JetBrains TeamCity CI' end diff --git a/app/models/service.rb b/app/models/service.rb index 60fcc9d2857..7e845d565b1 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -117,6 +117,15 @@ class Service < ActiveRecord::Base end end + # ActiveRecord does not provide a mechanism to track changes in serialized keys. + # This is why we need to perform extra query to do it mannually. + def prop_updated?(prop_name) + relation_name = self.type.underscore + previous_value = project.send(relation_name).send(prop_name) + return false if previous_value.nil? + previous_value != send(prop_name) + end + def async_execute(data) return unless supported_events.include?(data[:object_kind]) diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb new file mode 100644 index 00000000000..f8a3493f52d --- /dev/null +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -0,0 +1,56 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe BambooService, models: true do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + @bamboo_service = BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @bamboo_service.bamboo_url = 'http://gitlab1.com' + @bamboo_service.save + expect(@bamboo_service.password).to be_nil + end + + it "does not reset password if username changed" do + @bamboo_service.username = "some_name" + @bamboo_service.save + expect(@bamboo_service.password).to eq("password") + end + end +end diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb new file mode 100644 index 00000000000..3dbd2346bcc --- /dev/null +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -0,0 +1,56 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe TeamcityService, models: true do + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Execute" do + let(:user) { create(:user) } + let(:project) { create(:project) } + + before do + @teamcity_service = TeamcityService.create( + project: create(:project), + properties: { + teamcity_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "reset password if url changed" do + @teamcity_service.teamcity_url = 'http://gitlab1.com' + @teamcity_service.save + expect(@teamcity_service.password).to be_nil + end + + it "does not reset password if username changed" do + @teamcity_service.username = "some_name" + @teamcity_service.save + expect(@teamcity_service.password).to eq("password") + end + end +end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index a213ffe6c4b..da87ea5b84f 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -103,4 +103,27 @@ describe Service do end end end + + describe "#prop_updated?" do + let(:service) do + BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "returns false" do + service.username = "key_changed" + expect(service.prop_updated?(:bamboo_url)).to be_falsy + end + + it "returns true" do + service.bamboo_url = "http://other.com" + expect(service.prop_updated?(:bamboo_url)).to be_truthy + end + end end From 0aefeeb882b40d740b80f15ac6610a88a340d30b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 12:16:00 +0200 Subject: [PATCH 080/119] Add Commit Status documentation --- app/models/ci/commit.rb | 2 +- app/models/commit_status.rb | 2 +- doc/api/commits.md | 82 +++++++++++++++++++++++++++++++++++++ lib/api/commit_statuses.rb | 20 ++++----- 4 files changed, 94 insertions(+), 12 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 042a68681bb..623ff619c49 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -136,7 +136,7 @@ module Ci latest_statuses = statuses.latest.to_a latest_statuses.reject! { |status| status.try(&:allow_failure?) } - latest_statuses.select! { |status| status.ref == nil || status.ref == ref } if ref + latest_statuses.select! { |status| status.ref.nil? || status.ref == ref } if ref if latest_statuses.none? return 'skipped' diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index de7b29a649d..a4896a76316 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base belongs_to :user validates :commit, presence: true - validates :status, inclusion: {in: %w(pending running failed success canceled)} + validates :status, inclusion: { in: %w(pending running failed success canceled) } validates_presence_of :name diff --git a/doc/api/commits.md b/doc/api/commits.md index b22d040bf0d..78144dd42ef 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -157,3 +157,85 @@ Parameters: "line_type": "new" } ``` + +## Get the status of a commit + +Get the statuses of a commit in a project. + +``` +GET /projects/:id/repository/commits/:sha/statuses +``` + +Parameters: + +- `id` (required) - The ID of a project +- `sha` (required) - The commit SHA +- `ref` (optional) - Filter by ref name, it can be branch or tag +- `stage` (optional) - Filter by stage +- `name` (optional) - Filer by status name, eg. jenkins +- `all` (optional) - The flag to return all statuses, not only latest ones + +```json +[ + { + "id": 13, + "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27", + "ref": "test", + "status": "success", + "name": "ci/jenkins", + "target_url": "http://jenkins/project/url", + "description": "Jenkins success", + "created_at": "2015-10-12T09:47:16.250Z", + "started_at": "2015-10-12T09:47:16.250Z"", + "finished_at": "2015-10-12T09:47:16.262Z", + "author": { + "id": 1, + "username": "admin", + "email": "admin@local.host", + "name": "Administrator", + "blocked": false, + "created_at": "2012-04-29T08:46:00Z" + } + } +] +``` + +## Post the status to commit + +Adds or updates a status of a commit. +Optionally you can post comments on a specific line of a commit. Therefor both `path`, `line_new` and `line_old` are required. + +``` +POST /projects/:id/statuses/:sha +``` + +- `id` (required) - The ID of a project +- `sha` (required) - The commit SHA +- `state` (required) - The state of the status. Can be: pending, running, success, failed, canceled +- `ref` (optional) - The ref (branch or tag) to which the status refers +- `name` or `context` (optional) - The label to differentiate this status from the status of other systems. Default: "default" +- `target_url` (optional) - The target URL to associate with this status +- `description` (optional) - The short description of the status + +```json +{ + "id": 13, + "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27", + "ref": "test", + "status": "success", + "name": "ci/jenkins", + "target_url": "http://jenkins/project/url", + "description": "Jenkins success", + "created_at": "2015-10-12T09:47:16.250Z", + "started_at": "2015-10-12T09:47:16.250Z"", + "finished_at": "2015-10-12T09:47:16.262Z", + "author": { + "id": 1, + "username": "admin", + "email": "admin@local.host", + "name": "Administrator", + "blocked": false, + "created_at": "2012-04-29T08:46:00Z" + } +} +``` diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index c478b3dc245..ca750320e40 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -56,16 +56,16 @@ module API status.update(attrs) case params[:state].to_s - when 'running' - status.run - when 'success' - status.success - when 'failed' - status.drop - when 'canceled' - status.cancel - else - status.status = params[:state].to_s + when 'running' + status.run + when 'success' + status.success + when 'failed' + status.drop + when 'canceled' + status.cancel + else + status.status = params[:state].to_s end if status.save From c2c8f8acc41747280356e157e749c1cafbd807e3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 12 Oct 2015 14:36:06 +0200 Subject: [PATCH 081/119] Remove warning about Reply by email and HA --- doc/incoming_email/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md index 076c8c049a8..aafa2345fab 100644 --- a/doc/incoming_email/README.md +++ b/doc/incoming_email/README.md @@ -2,10 +2,6 @@ GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails. -**Warning**: Do not enable Reply by email if you have **multiple GitLab application servers**. -Due to an issue with the way incoming emails are read from the mail server, every incoming reply-by-email email will result in as many comments being created as you have application servers. -[A fix is being worked on.](https://github.com/tpitale/mail_room/issues/46) - ## Get a mailbox Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises. From d6a523d78569020a00cf707f34a1824996261037 Mon Sep 17 00:00:00 2001 From: teuneboon Date: Mon, 12 Oct 2015 14:37:07 +0200 Subject: [PATCH 082/119] Fix typo(missing quote) when deleting groups --- app/controllers/groups_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 524218290c6..735de309fd6 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -88,7 +88,7 @@ class GroupsController < Groups::ApplicationController def destroy DestroyGroupService.new(@group, current_user).execute - redirect_to root_path, alert: "Group '#{@group.name} was deleted." + redirect_to root_path, alert: "Group '#{@group.name}' was deleted." end protected From 5479797f0a3a648824fd98d17360bbf886957bf1 Mon Sep 17 00:00:00 2001 From: teuneboon Date: Mon, 12 Oct 2015 14:37:56 +0200 Subject: [PATCH 083/119] Make group deleted text consistent with group created and updated texts --- app/controllers/groups_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 735de309fd6..40fb15a5b36 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -88,7 +88,7 @@ class GroupsController < Groups::ApplicationController def destroy DestroyGroupService.new(@group, current_user).execute - redirect_to root_path, alert: "Group '#{@group.name}' was deleted." + redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted." end protected From cba6d62323eba2286f78e8aea12a5ecd26903728 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 12 Oct 2015 14:49:23 +0200 Subject: [PATCH 084/119] Move CI styles to pages dir Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/{ci => pages}/builds.scss | 0 .../stylesheets/{ci/projects.scss => pages/ci_projects.scss} | 0 app/assets/stylesheets/{ci => pages}/lint.scss | 0 app/assets/stylesheets/{ci => pages}/runners.scss | 0 app/assets/stylesheets/{ci => pages}/status.scss | 0 app/assets/stylesheets/{ci => pages}/xterm.scss | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename app/assets/stylesheets/{ci => pages}/builds.scss (100%) rename app/assets/stylesheets/{ci/projects.scss => pages/ci_projects.scss} (100%) rename app/assets/stylesheets/{ci => pages}/lint.scss (100%) rename app/assets/stylesheets/{ci => pages}/runners.scss (100%) rename app/assets/stylesheets/{ci => pages}/status.scss (100%) rename app/assets/stylesheets/{ci => pages}/xterm.scss (100%) diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/pages/builds.scss similarity index 100% rename from app/assets/stylesheets/ci/builds.scss rename to app/assets/stylesheets/pages/builds.scss diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/pages/ci_projects.scss similarity index 100% rename from app/assets/stylesheets/ci/projects.scss rename to app/assets/stylesheets/pages/ci_projects.scss diff --git a/app/assets/stylesheets/ci/lint.scss b/app/assets/stylesheets/pages/lint.scss similarity index 100% rename from app/assets/stylesheets/ci/lint.scss rename to app/assets/stylesheets/pages/lint.scss diff --git a/app/assets/stylesheets/ci/runners.scss b/app/assets/stylesheets/pages/runners.scss similarity index 100% rename from app/assets/stylesheets/ci/runners.scss rename to app/assets/stylesheets/pages/runners.scss diff --git a/app/assets/stylesheets/ci/status.scss b/app/assets/stylesheets/pages/status.scss similarity index 100% rename from app/assets/stylesheets/ci/status.scss rename to app/assets/stylesheets/pages/status.scss diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/pages/xterm.scss similarity index 100% rename from app/assets/stylesheets/ci/xterm.scss rename to app/assets/stylesheets/pages/xterm.scss From bdd477a0f800328a527f2fad92d1303441689341 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 12 Oct 2015 15:00:04 +0200 Subject: [PATCH 085/119] Re-organize GitLab css into 2 directories: framework and page specific css Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/application.scss | 30 ++--------------- app/assets/stylesheets/framework.scss | 32 +++++++++++++++++++ .../{generic => framework}/avatar.scss | 0 .../{generic => framework}/blocks.scss | 0 .../{generic => framework}/buttons.scss | 0 .../{generic => framework}/calendar.scss | 0 .../{generic => framework}/callout.scss | 0 .../{generic => framework}/common.scss | 0 .../{generic => framework}/files.scss | 0 .../{generic => framework}/filters.scss | 0 .../{generic => framework}/flash.scss | 0 .../{base => framework}/fonts.scss | 0 .../{generic => framework}/forms.scss | 0 .../{generic => framework}/gfm.scss | 0 .../{themes => framework}/gitlab-theme.scss | 0 .../{base => framework}/gl_bootstrap.scss | 0 .../{base => framework}/gl_variables.scss | 0 .../{generic => framework}/header.scss | 0 .../{generic => framework}/highlight.scss | 0 .../{generic => framework}/issue_box.scss | 0 .../{generic => framework}/jquery.scss | 0 .../{base => framework}/layout.scss | 0 .../{generic => framework}/lists.scss | 0 .../{generic => framework}/markdown_area.scss | 0 .../{base => framework}/mixins.scss | 0 .../{generic => framework}/mobile.scss | 0 .../{generic => framework}/pagination.scss | 0 .../{generic => framework}/selects.scss | 0 .../{generic => framework}/sidebar.scss | 0 .../{generic => framework}/tables.scss | 0 .../{generic => framework}/timeline.scss | 0 .../{generic => framework}/typography.scss | 0 .../{base => framework}/variables.scss | 0 .../{generic => framework}/zen.scss | 0 34 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 app/assets/stylesheets/framework.scss rename app/assets/stylesheets/{generic => framework}/avatar.scss (100%) rename app/assets/stylesheets/{generic => framework}/blocks.scss (100%) rename app/assets/stylesheets/{generic => framework}/buttons.scss (100%) rename app/assets/stylesheets/{generic => framework}/calendar.scss (100%) rename app/assets/stylesheets/{generic => framework}/callout.scss (100%) rename app/assets/stylesheets/{generic => framework}/common.scss (100%) rename app/assets/stylesheets/{generic => framework}/files.scss (100%) rename app/assets/stylesheets/{generic => framework}/filters.scss (100%) rename app/assets/stylesheets/{generic => framework}/flash.scss (100%) rename app/assets/stylesheets/{base => framework}/fonts.scss (100%) rename app/assets/stylesheets/{generic => framework}/forms.scss (100%) rename app/assets/stylesheets/{generic => framework}/gfm.scss (100%) rename app/assets/stylesheets/{themes => framework}/gitlab-theme.scss (100%) rename app/assets/stylesheets/{base => framework}/gl_bootstrap.scss (100%) rename app/assets/stylesheets/{base => framework}/gl_variables.scss (100%) rename app/assets/stylesheets/{generic => framework}/header.scss (100%) rename app/assets/stylesheets/{generic => framework}/highlight.scss (100%) rename app/assets/stylesheets/{generic => framework}/issue_box.scss (100%) rename app/assets/stylesheets/{generic => framework}/jquery.scss (100%) rename app/assets/stylesheets/{base => framework}/layout.scss (100%) rename app/assets/stylesheets/{generic => framework}/lists.scss (100%) rename app/assets/stylesheets/{generic => framework}/markdown_area.scss (100%) rename app/assets/stylesheets/{base => framework}/mixins.scss (100%) rename app/assets/stylesheets/{generic => framework}/mobile.scss (100%) rename app/assets/stylesheets/{generic => framework}/pagination.scss (100%) rename app/assets/stylesheets/{generic => framework}/selects.scss (100%) rename app/assets/stylesheets/{generic => framework}/sidebar.scss (100%) rename app/assets/stylesheets/{generic => framework}/tables.scss (100%) rename app/assets/stylesheets/{generic => framework}/timeline.scss (100%) rename app/assets/stylesheets/{generic => framework}/typography.scss (100%) rename app/assets/stylesheets/{base => framework}/variables.scss (100%) rename app/assets/stylesheets/{generic => framework}/zen.scss (100%) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index d9ede637944..233e01cc06b 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -11,18 +11,10 @@ *= require cal-heatmap */ - -@import "base/fonts"; -@import "base/variables"; -@import "base/mixins"; -@import "base/layout"; - - /** - * Customized Twitter bootstrap + * GitLab UI framework */ -@import 'base/gl_variables'; -@import 'base/gl_bootstrap'; +@import "framework"; /** * NProgress load bar css @@ -32,24 +24,12 @@ /** * Font icons - * */ @import "font-awesome"; -/** - * UI themes: - */ -@import "themes/**/*"; - -/** - * Generic css (forms, nav etc): - */ -@import "generic/**/*"; - /** * Page specific styles (issues, projects etc): */ - @import "pages/**/*"; /** @@ -61,9 +41,3 @@ * Styles for JS behaviors. */ @import "behaviors.scss"; - -/** - * CI specific styles: - */ -@import "ci/**/*"; - diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss new file mode 100644 index 00000000000..c5e23c1c328 --- /dev/null +++ b/app/assets/stylesheets/framework.scss @@ -0,0 +1,32 @@ +@import "framework/fonts"; +@import "framework/variables"; +@import "framework/mixins"; +@import "framework/layout"; +@import 'framework/gl_variables'; +@import 'framework/gl_bootstrap'; +@import "framework/avatar.scss"; +@import "framework/blocks.scss"; +@import "framework/buttons.scss"; +@import "framework/calendar.scss"; +@import "framework/callout.scss"; +@import "framework/common.scss"; +@import "framework/files.scss"; +@import "framework/filters.scss"; +@import "framework/flash.scss"; +@import "framework/forms.scss"; +@import "framework/gfm.scss"; +@import "framework/gitlab-theme.scss"; +@import "framework/header.scss"; +@import "framework/highlight.scss"; +@import "framework/issue_box.scss"; +@import "framework/jquery.scss"; +@import "framework/lists.scss"; +@import "framework/markdown_area.scss"; +@import "framework/mobile.scss"; +@import "framework/pagination.scss"; +@import "framework/selects.scss"; +@import "framework/sidebar.scss"; +@import "framework/tables.scss"; +@import "framework/timeline.scss"; +@import "framework/typography.scss"; +@import "framework/zen.scss"; diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/framework/avatar.scss similarity index 100% rename from app/assets/stylesheets/generic/avatar.scss rename to app/assets/stylesheets/framework/avatar.scss diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/framework/blocks.scss similarity index 100% rename from app/assets/stylesheets/generic/blocks.scss rename to app/assets/stylesheets/framework/blocks.scss diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/framework/buttons.scss similarity index 100% rename from app/assets/stylesheets/generic/buttons.scss rename to app/assets/stylesheets/framework/buttons.scss diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/framework/calendar.scss similarity index 100% rename from app/assets/stylesheets/generic/calendar.scss rename to app/assets/stylesheets/framework/calendar.scss diff --git a/app/assets/stylesheets/generic/callout.scss b/app/assets/stylesheets/framework/callout.scss similarity index 100% rename from app/assets/stylesheets/generic/callout.scss rename to app/assets/stylesheets/framework/callout.scss diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/framework/common.scss similarity index 100% rename from app/assets/stylesheets/generic/common.scss rename to app/assets/stylesheets/framework/common.scss diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/framework/files.scss similarity index 100% rename from app/assets/stylesheets/generic/files.scss rename to app/assets/stylesheets/framework/files.scss diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/framework/filters.scss similarity index 100% rename from app/assets/stylesheets/generic/filters.scss rename to app/assets/stylesheets/framework/filters.scss diff --git a/app/assets/stylesheets/generic/flash.scss b/app/assets/stylesheets/framework/flash.scss similarity index 100% rename from app/assets/stylesheets/generic/flash.scss rename to app/assets/stylesheets/framework/flash.scss diff --git a/app/assets/stylesheets/base/fonts.scss b/app/assets/stylesheets/framework/fonts.scss similarity index 100% rename from app/assets/stylesheets/base/fonts.scss rename to app/assets/stylesheets/framework/fonts.scss diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/framework/forms.scss similarity index 100% rename from app/assets/stylesheets/generic/forms.scss rename to app/assets/stylesheets/framework/forms.scss diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/framework/gfm.scss similarity index 100% rename from app/assets/stylesheets/generic/gfm.scss rename to app/assets/stylesheets/framework/gfm.scss diff --git a/app/assets/stylesheets/themes/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss similarity index 100% rename from app/assets/stylesheets/themes/gitlab-theme.scss rename to app/assets/stylesheets/framework/gitlab-theme.scss diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/framework/gl_bootstrap.scss similarity index 100% rename from app/assets/stylesheets/base/gl_bootstrap.scss rename to app/assets/stylesheets/framework/gl_bootstrap.scss diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/framework/gl_variables.scss similarity index 100% rename from app/assets/stylesheets/base/gl_variables.scss rename to app/assets/stylesheets/framework/gl_variables.scss diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/framework/header.scss similarity index 100% rename from app/assets/stylesheets/generic/header.scss rename to app/assets/stylesheets/framework/header.scss diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/framework/highlight.scss similarity index 100% rename from app/assets/stylesheets/generic/highlight.scss rename to app/assets/stylesheets/framework/highlight.scss diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss similarity index 100% rename from app/assets/stylesheets/generic/issue_box.scss rename to app/assets/stylesheets/framework/issue_box.scss diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/framework/jquery.scss similarity index 100% rename from app/assets/stylesheets/generic/jquery.scss rename to app/assets/stylesheets/framework/jquery.scss diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/framework/layout.scss similarity index 100% rename from app/assets/stylesheets/base/layout.scss rename to app/assets/stylesheets/framework/layout.scss diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/framework/lists.scss similarity index 100% rename from app/assets/stylesheets/generic/lists.scss rename to app/assets/stylesheets/framework/lists.scss diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss similarity index 100% rename from app/assets/stylesheets/generic/markdown_area.scss rename to app/assets/stylesheets/framework/markdown_area.scss diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/framework/mixins.scss similarity index 100% rename from app/assets/stylesheets/base/mixins.scss rename to app/assets/stylesheets/framework/mixins.scss diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/framework/mobile.scss similarity index 100% rename from app/assets/stylesheets/generic/mobile.scss rename to app/assets/stylesheets/framework/mobile.scss diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/framework/pagination.scss similarity index 100% rename from app/assets/stylesheets/generic/pagination.scss rename to app/assets/stylesheets/framework/pagination.scss diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/framework/selects.scss similarity index 100% rename from app/assets/stylesheets/generic/selects.scss rename to app/assets/stylesheets/framework/selects.scss diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss similarity index 100% rename from app/assets/stylesheets/generic/sidebar.scss rename to app/assets/stylesheets/framework/sidebar.scss diff --git a/app/assets/stylesheets/generic/tables.scss b/app/assets/stylesheets/framework/tables.scss similarity index 100% rename from app/assets/stylesheets/generic/tables.scss rename to app/assets/stylesheets/framework/tables.scss diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/framework/timeline.scss similarity index 100% rename from app/assets/stylesheets/generic/timeline.scss rename to app/assets/stylesheets/framework/timeline.scss diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/framework/typography.scss similarity index 100% rename from app/assets/stylesheets/generic/typography.scss rename to app/assets/stylesheets/framework/typography.scss diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/framework/variables.scss similarity index 100% rename from app/assets/stylesheets/base/variables.scss rename to app/assets/stylesheets/framework/variables.scss diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/framework/zen.scss similarity index 100% rename from app/assets/stylesheets/generic/zen.scss rename to app/assets/stylesheets/framework/zen.scss From c42d35b7214348e5c31014b3d3d54e59f6030ddc Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 12 Oct 2015 15:08:33 +0200 Subject: [PATCH 086/119] Add css welcome notice Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/application.scss | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 233e01cc06b..65f775ca3f3 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -11,33 +11,41 @@ *= require cal-heatmap */ -/** +/* + * Welcome to GitLab css! + * If you need to add or modify UI component that is commont for many pages + * like table or typography then make changes in framework/ directory. + * If you need to add uniq style that should affect only one page - use pages/ + * directory. + */ + +/* * GitLab UI framework */ @import "framework"; -/** +/* * NProgress load bar css */ @import 'nprogress'; @import 'nprogress-bootstrap'; -/** +/* * Font icons */ @import "font-awesome"; -/** +/* * Page specific styles (issues, projects etc): */ @import "pages/**/*"; -/** +/* * Code highlight */ @import "highlight/**/*"; -/** +/* * Styles for JS behaviors. */ @import "behaviors.scss"; From 024e34e94d973842cf02d9177e9ec52bd587ceee Mon Sep 17 00:00:00 2001 From: Alex Lossent Date: Mon, 12 Oct 2015 15:24:00 +0200 Subject: [PATCH 087/119] Hide passwords to non-admin users in the services API In order to be consistent with !1490 doing it for the web interface --- CHANGELOG | 1 + lib/api/entities.rb | 12 +++++++++ lib/api/services.rb | 2 +- spec/requests/api/services_spec.rb | 33 ++++++++++++++++++++++++- spec/support/services_shared_context.rb | 8 +++++- 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a3d796bea66..10ea52a12f4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.1.0 (unreleased) - Fix position of hamburger in header for smaller screens (Han Loong Liauw) - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji) - Persist filters when sorting on admin user page (Jerry Lukins) + - Hide passwords from services API (Alex Lossent) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9620d36ac41..7a1e702c755 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -255,6 +255,18 @@ module API expose :notification_level end + class ProjectService < Grape::Entity + expose :id, :title, :created_at, :updated_at, :active + expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events + # Expose serialized properties + expose :properties do |service, options| + field_names = service.fields. + select { |field| options[:include_passwords] || field[:type] != 'password' }. + map { |field| field[:name] } + service.properties.slice(*field_names) + end + end + class ProjectWithAccess < Project expose :permissions do expose :project_access, using: Entities::ProjectAccess do |project, options| diff --git a/lib/api/services.rb b/lib/api/services.rb index 6727e80ac1e..203f04a6259 100644 --- a/lib/api/services.rb +++ b/lib/api/services.rb @@ -57,7 +57,7 @@ module API # GET /project/:id/services/gitlab-ci # get ':id/services/:service_slug' do - present project_service + present project_service, with: Entities::ProjectService, include_passwords: current_user.is_admin? end end end diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 9aa60826f21..c0226605a23 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -3,6 +3,8 @@ require "spec_helper" describe API::API, api: true do include ApiHelpers let(:user) { create(:user) } + let(:admin) { create(:admin) } + let(:user2) { create(:user) } let(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } Service.available_services_names.each do |service| @@ -51,11 +53,40 @@ describe API::API, api: true do describe "GET /projects/:id/services/#{service.dasherize}" do include_context service - it "should get #{service} settings" do + # inject some properties into the service + before do + project.build_missing_services + service_object = project.send(service_method) + service_object.properties = service_attrs + service_object.save + end + + it 'should return authentication error when unauthenticated' do + get api("/projects/#{project.id}/services/#{dashed_service}") + expect(response.status).to eq(401) + end + + it "should return all properties of service #{service} when authenticated as admin" do + get api("/projects/#{project.id}/services/#{dashed_service}", admin) + + expect(response.status).to eq(200) + expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list.map) + end + + it "should return properties of service #{service} other than passwords when authenticated as project owner" do get api("/projects/#{project.id}/services/#{dashed_service}", user) expect(response.status).to eq(200) + expect(json_response['properties'].keys.map(&:to_sym)).to match_array(service_attrs_list_without_passwords) end + + it "should return error when authenticated but not a project owner" do + project.team << [user2, :developer] + get api("/projects/#{project.id}/services/#{dashed_service}", user2) + + expect(response.status).to eq(403) + end + end end end diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb index 4d007ae55ee..d1c999cad4d 100644 --- a/spec/support/services_shared_context.rb +++ b/spec/support/services_shared_context.rb @@ -3,7 +3,13 @@ Service.available_services_names.each do |service| let(:dashed_service) { service.dasherize } let(:service_method) { "#{service}_service".to_sym } let(:service_klass) { "#{service}_service".classify.constantize } - let(:service_attrs_list) { service_klass.new.fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } + let(:service_fields) { service_klass.new.fields } + let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } } + let(:service_attrs_list_without_passwords) do + service_fields. + select { |field| field[:type] != 'password' }. + map { |field| field[:name].to_sym} + end let(:service_attrs) do service_attrs_list.inject({}) do |hash, k| if k =~ /^(token*|.*_token|.*_key)/ From 69c04498ef0a49186a667fd44383b9b43690a91e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 15:45:46 +0200 Subject: [PATCH 088/119] Small bug fixes --- app/models/ci/build.rb | 16 +++++++++---- app/models/commit_status.rb | 23 +++++++++++++------ .../commit_statuses/_commit_status.html.haml | 16 ++++++------- doc/api/commits.md | 1 - lib/api/commit_statuses.rb | 5 ++-- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 41ce522b2ff..cb6a1015210 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -212,15 +212,23 @@ module Ci "#{dir_to_trace}/#{id}.log" end - def description - name - end - def target_url Gitlab::Application.routes.url_helpers. namespace_project_build_url(gl_project.namespace, gl_project, self) end + def cancel_url + if active? + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + end + end + + def retry_url + if commands.present? + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + end + end + private def yaml_variables diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index a4896a76316..b6234c896e3 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -11,14 +11,14 @@ class CommitStatus < ActiveRecord::Base alias_attribute :author, :user - scope :running, ->() { where(status: 'running') } - scope :pending, ->() { where(status: 'pending') } - scope :success, ->() { where(status: 'success') } - scope :failed, ->() { where(status: 'failed') } - scope :running_or_pending, ->() { where(status:[:running, :pending]) } - scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) } + scope :running, -> { where(status: 'running') } + scope :pending, -> { where(status: 'pending') } + scope :success, -> { where(status: 'success') } + scope :failed, -> { where(status: 'failed') } + scope :running_or_pending, -> { where(status:[:running, :pending]) } + scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) } scope :for_ref, ->(ref) { where(ref: [ref, nil]) } - scope :running_or_pending, ->() { where(status: [:running, :pending]) } + scope :running_or_pending, -> { where(status: [:running, :pending]) } state_machine :status, initial: :pending do event :run do @@ -55,6 +55,7 @@ class CommitStatus < ActiveRecord::Base delegate :sha, :short_sha, :gl_project, to: :commit, prefix: false + # TODO: this should be removed with all references def before_sha Gitlab::Git::BLANK_SHA end @@ -78,4 +79,12 @@ class CommitStatus < ActiveRecord::Base Time.now - started_at end end + + def cancel_url + nil + end + + def retry_url + nil + end end diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index f79929c70bf..14b814bb0d6 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -9,7 +9,7 @@ - else %strong Build ##{commit_status.id} - - if defined?(ref) + - if defined?(ref) && ref %td = commit_status.ref @@ -17,7 +17,7 @@ = commit_status.stage %td - = commit_status.description + = commit_status.name .pull-right - if commit_status.tags.any? - commit_status.tags.each do |tag| @@ -36,17 +36,17 @@ - if commit_status.finished_at %span #{time_ago_in_words commit_status.finished_at} ago - - if defined?(coverage) + - if defined?(coverage) && coverage %td.coverage - if commit_status.try(:coverage) #{commit_status.coverage}% %td - - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project) + - if defined?(controls) && controls && current_user && can?(current_user, :manage_builds, gl_project) .pull-right - - if commit_status.active? - = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, commit_status, return_to: request.original_url), title: 'Cancel commit_status' do + - if commit_status.cancel_url + = link_to commit_status.cancel_url, title: 'Cancel' do %i.fa.fa-remove.cred - - elsif commit_status.commands.present? - = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, commit_status, return_to: request.original_url), method: :post, title: 'Retry commit_status' do + - elsif commit_status.retry_url + = link_to commit_status.retry_url, method: :post, title: 'Retry' do %i.fa.fa-repeat diff --git a/doc/api/commits.md b/doc/api/commits.md index 78144dd42ef..9f72adc6ed9 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -203,7 +203,6 @@ Parameters: ## Post the status to commit Adds or updates a status of a commit. -Optionally you can post comments on a specific line of a commit. Therefor both `path`, `line_new` and `line_old` are required. ``` POST /projects/:id/statuses/:sha diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index ca750320e40..2a005d6a9f7 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -5,7 +5,6 @@ module API class CommitStatus < Grape::API resource :projects do before { authenticate! } - before { authorize! :read_commit_statuses, user_project } # Get a commit's statuses # @@ -19,13 +18,14 @@ module API # Examples: # GET /projects/:id/repository/commits/:sha/statuses get ':id/repository/commits/:sha/statuses' do + authorize! :read_commit_statuses, user_project sha = params[:sha] ci_commit = user_project.ci_commit(sha) not_found! 'Commit' unless ci_commit statuses = ci_commit.statuses statuses = statuses.latest unless parse_boolean(params[:all]) statuses = statuses.where(ref: params[:ref]) if params[:ref].present? - statuses = statuses.where(name: params[:stage]) if params[:stage].present? + statuses = statuses.where(stage: params[:stage]) if params[:stage].present? statuses = statuses.where(name: params[:name]) if params[:name].present? present paginate(statuses), with: Entities::CommitStatus end @@ -43,6 +43,7 @@ module API # Examples: # POST /projects/:id/statuses/:sha post ':id/statuses/:sha' do + authorize! :create_commit_statuses, user_project required_attributes! [:state] attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name] commit = @project.commit(params[:sha]) From 789fe7b4899fb97c48ad363c5ecb1969d78d1536 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:32:58 +0200 Subject: [PATCH 089/119] Update rendering --- app/models/ci/commit.rb | 64 +++++++++--------- app/models/commit_status.rb | 5 +- app/views/projects/builds/show.html.haml | 6 +- app/views/projects/commit/ci.html.haml | 65 +++++++------------ .../commit_statuses/_commit_status.html.haml | 5 +- lib/api/commit_statuses.rb | 2 +- 6 files changed, 62 insertions(+), 85 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 623ff619c49..201b6f62b86 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -106,50 +106,47 @@ module Ci end def refs - statuses.pluck(:ref).compact.uniq + statuses.order(:ref).pluck(:ref).uniq end - def statuses_for_ref(ref = nil) - if ref - statuses.for_ref(ref) - else - statuses - end + def latest_statuses + @latest_statuses ||= statuses.latest.to_a end - def builds_without_retry(ref = nil) - if ref - builds.for_ref(ref).latest - else - builds.latest - end + def builds_without_retry + @builds_without_retry ||= builds.latest.to_a + end + + def builds_without_retry_for_ref(ref) + builds_without_retry.select { |build| build.ref == ref } end def retried @retried ||= (statuses.order(id: :desc) - statuses.latest) end - def status(ref = nil) + def status if yaml_errors.present? return 'failed' end - latest_statuses = statuses.latest.to_a - latest_statuses.reject! { |status| status.try(&:allow_failure?) } - latest_statuses.select! { |status| status.ref.nil? || status.ref == ref } if ref + @status ||= begin + latest = latest_statuses + latest.reject! { |status| status.try(&:allow_failure?) } - if latest_statuses.none? - return 'skipped' - elsif latest_statuses.all?(&:success?) - 'success' - elsif latest_statuses.all?(&:pending?) - 'pending' - elsif latest_statuses.any?(&:running?) || latest_statuses.any?(&:pending?) - 'running' - elsif latest_statuses.all?(&:canceled?) - 'canceled' - else - 'failed' + if latest.none? + 'skipped' + elsif latest.all?(&:success?) + 'success' + elsif latest.all?(&:pending?) + 'pending' + elsif latest.any?(&:running?) || latest.any?(&:pending?) + 'running' + elsif latest.all?(&:canceled?) + 'canceled' + else + 'failed' + end end end @@ -173,8 +170,9 @@ module Ci status == 'canceled' end - def duration(ref = nil) - statuses_for_ref(ref).latest.select(&:duration).sum(&:duration).to_i + def duration + duration_array = latest_statuses.map(&:duration).compact + duration_array.reduce(:+).to_i end def finished_at @@ -190,8 +188,8 @@ module Ci end end - def matrix?(ref) - builds_without_retry(ref).pluck(:id).size > 1 + def matrix_for_ref?(ref) + builds_without_retry_for_ref(ref).size > 1 end def config_processor diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index b6234c896e3..b4d91b1b0c3 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -16,8 +16,9 @@ class CommitStatus < ActiveRecord::Base scope :success, -> { where(status: 'success') } scope :failed, -> { where(status: 'failed') } scope :running_or_pending, -> { where(status:[:running, :pending]) } - scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) } - scope :for_ref, ->(ref) { where(ref: [ref, nil]) } + scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) } + scope :ordered, -> { order(:ref, :stage_idx, :name) } + scope :for_ref, ->(ref) { where(ref: ref) } scope :running_or_pending, -> { where(status: [:running, :pending]) } state_machine :status, initial: :pending do diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 66e668f3771..b561078e8c7 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -7,9 +7,9 @@ %code #{@build.ref} #up-build-trace - - if @commit.matrix?(@build.ref) + - if @commit.matrix_for_ref?(@build.ref) %ul.center-top-menu.build-top-menu - - @commit.builds_without_retry(@build.ref).each do |build| + - @commit.builds_without_retry_for_ref(@build.ref).each do |build| %li{class: ('active' if build == @build) } = link_to namespace_project_build_path(@project.namespace, @project, build) do = ci_icon_for_status(build.status) @@ -20,7 +20,7 @@ = build.id - - unless @commit.builds_without_retry(@build.ref).include?(@build) + - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build) %li.active %a Build ##{@build.id} diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index 7f106631cac..585f012fe04 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -20,49 +20,28 @@ .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit -- if @ci_commit.refs.blank? - .gray-content-block.second-block - Latest builds - - if @ci_commit.duration > 0 - %small.pull-right - %i.fa.fa-time - #{time_interval_in_words @ci_commit.duration} +.gray-content-block.second-block + Latest + - if @ci_commit.duration > 0 + %small.pull-right + %i.fa.fa-time + #{time_interval_in_words @ci_commit.duration} - %table.table.builds - %thead - %tr - %th Status - %th Build ID - %th Stage - %th Name - %th Duration - %th Finished at - - if @ci_project && @ci_project.coverage_enabled? - %th Coverage - %th - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.latest, coverage: @ci_project.try(:coverage_enabled?), controls: true - -- @ci_commit.refs.sort.each do |ref| - .gray-content-block.second-block - Builds for #{ref} - - if @ci_commit.duration(ref) > 0 - %small.pull-right - %i.fa.fa-time - #{time_interval_in_words @ci_commit.duration(ref)} - - %table.table.builds - %thead - %tr - %th Status - %th Build ID - %th Stage - %th Name - %th Duration - %th Finished at - - if @ci_project && @ci_project.coverage_enabled? - %th Coverage - %th - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest, coverage: @ci_project.try(:coverage_enabled?), controls: true +%table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Ref + %th Stage + %th Name + %th Duration + %th Finished at + - if @ci_project && @ci_project.coverage_enabled? + %th Coverage + %th + - @ci_commit.refs.each do |ref| + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, coverage: @ci_project.try(:coverage_enabled?), controls: true - if @ci_commit.retried.any? .gray-content-block.second-block @@ -81,4 +60,4 @@ - if @ci_project && @ci_project.coverage_enabled? %th Coverage %th - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?), ref: true + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?) diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index 14b814bb0d6..e3a17faf0bd 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -9,9 +9,8 @@ - else %strong Build ##{commit_status.id} - - if defined?(ref) && ref - %td - = commit_status.ref + %td + = commit_status.ref %td = commit_status.stage diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 2a005d6a9f7..50ca89079e0 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -53,7 +53,7 @@ module API name = params[:name] || params[:context] status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref]) - status = GenericCommitStatus.new(commit: ci_commit, user: current_user) unless status + status ||= GenericCommitStatus.new(commit: ci_commit, user: current_user) status.update(attrs) case params[:state].to_s From 2e9c1608e56a20b93608ddcd569c6a45d3a229eb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:35:58 +0200 Subject: [PATCH 090/119] Fix commit skipping --- app/services/ci/create_commit_service.rb | 10 +++++----- spec/models/ci/project_services/mail_service_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb index 0ae35387579..79b0291fabc 100644 --- a/app/services/ci/create_commit_service.rb +++ b/app/services/ci/create_commit_service.rb @@ -17,11 +17,11 @@ module Ci tag = origin_ref.start_with?('refs/tags/') commit = project.gl_project.ensure_ci_commit(sha) - return false if commit.skip_ci? - - commit.update_committed! - commit.create_builds(ref, tag, user) - + unless commit.skip_ci? + commit.update_committed! + commit.create_builds(ref, tag, user) + end + commit end end diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index 2ce5c2b4707..d9b3d34ff15 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -35,7 +35,7 @@ describe Ci::MailService do let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) } let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( @@ -167,7 +167,7 @@ describe Ci::MailService do end let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) } + let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) } before do allow(mail).to receive_messages( From 5cd504ed331dd23d17709285237945fb867978a8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:39:08 +0200 Subject: [PATCH 091/119] Rename builds_without_retry to latest_builds --- app/models/ci/commit.rb | 14 +++++++------- app/models/project_services/ci/hip_chat_service.rb | 2 +- app/models/project_services/ci/mail_service.rb | 2 +- app/models/project_services/ci/slack_message.rb | 2 +- app/models/project_services/ci/slack_service.rb | 2 +- app/services/ci/create_commit_service.rb | 2 +- app/views/projects/builds/show.html.haml | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 201b6f62b86..296890c5e7c 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -48,7 +48,7 @@ module Ci end def retry - builds_without_retry.each do |build| + latest_builds.each do |build| Ci::Build.retry(build) end end @@ -113,12 +113,12 @@ module Ci @latest_statuses ||= statuses.latest.to_a end - def builds_without_retry - @builds_without_retry ||= builds.latest.to_a + def latest_builds + @latest_builds ||= builds.latest.to_a end - def builds_without_retry_for_ref(ref) - builds_without_retry.select { |build| build.ref == ref } + def latest_builds_for_ref(ref) + latest_builds.select { |build| build.ref == ref } end def retried @@ -181,7 +181,7 @@ module Ci def coverage if project.coverage_enabled? - coverage_array = builds_without_retry.map(&:coverage).compact + coverage_array = latest_builds.map(&:coverage).compact if coverage_array.size >= 1 '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) end @@ -189,7 +189,7 @@ module Ci end def matrix_for_ref?(ref) - builds_without_retry_for_ref(ref).size > 1 + latest_builds_for_ref(ref).size > 1 end def config_processor diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb index 0e6e97394bc..f17993d9f3b 100644 --- a/app/models/project_services/ci/hip_chat_service.rb +++ b/app/models/project_services/ci/hip_chat_service.rb @@ -49,7 +49,7 @@ module Ci commit = build.commit return unless commit - return unless commit.builds_without_retry.include? build + return unless commit.latest_builds.include? build case commit.status.to_sym when :failed diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index 11a2743f969..fd193301001 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -48,7 +48,7 @@ module Ci # it doesn't make sense to send emails for retried builds commit = build.commit return unless commit - return unless commit.builds_without_retry.include?(build) + return unless commit.latest_builds.include?(build) case build.status.to_sym when :failed diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb index 5ac8907ecd0..dc050a3fc59 100644 --- a/app/models/project_services/ci/slack_message.rb +++ b/app/models/project_services/ci/slack_message.rb @@ -23,7 +23,7 @@ module Ci def attachments fields = [] - commit.builds_without_retry.each do |build| + commit.latest_builds.each do |build| next if build.allow_failure? next unless build.failed? fields << { diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb index 76db573dc17..ee8e4988826 100644 --- a/app/models/project_services/ci/slack_service.rb +++ b/app/models/project_services/ci/slack_service.rb @@ -48,7 +48,7 @@ module Ci commit = build.commit return unless commit - return unless commit.builds_without_retry.include?(build) + return unless commit.latest_builds.include?(build) case commit.status.to_sym when :failed diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb index 79b0291fabc..479a2d6defc 100644 --- a/app/services/ci/create_commit_service.rb +++ b/app/services/ci/create_commit_service.rb @@ -21,7 +21,7 @@ module Ci commit.update_committed! commit.create_builds(ref, tag, user) end - + commit end end diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index b561078e8c7..9c3ae622b72 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -9,7 +9,7 @@ #up-build-trace - if @commit.matrix_for_ref?(@build.ref) %ul.center-top-menu.build-top-menu - - @commit.builds_without_retry_for_ref(@build.ref).each do |build| + - @commit.latest_builds_for_ref(@build.ref).each do |build| %li{class: ('active' if build == @build) } = link_to namespace_project_build_path(@project.namespace, @project, build) do = ci_icon_for_status(build.status) @@ -20,7 +20,7 @@ = build.id - - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build) + - unless @commit.latest_builds_for_ref(@build.ref).include?(@build) %li.active %a Build ##{@build.id} From c61dc1315077ad4e175a3c76991872c0e7b1d2ab Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 16:41:36 +0200 Subject: [PATCH 092/119] Fix some changes --- lib/api/entities.rb | 2 +- spec/models/commit_status_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index bfb242bb6fd..519072d0157 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -65,7 +65,7 @@ module API expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at expose :creator_id expose :namespace - expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda { |project, options| project.forked? } + expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } expose :avatar_url expose :star_count, :forks_count end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index cbefa7798de..c96a606fdaa 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -142,7 +142,7 @@ describe CommitStatus do end it 'return statuses with equal and nil ref set' do - is_expected.to eq([@commit1, @commit3]) + is_expected.to eq([@commit1]) end end From 1af4fcfa123370ab6dcf38a424c54029d7734032 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 12 Oct 2015 19:00:21 +0000 Subject: [PATCH 093/119] Fix typos in application.scss --- app/assets/stylesheets/application.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 65f775ca3f3..7b060ce4853 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -13,9 +13,9 @@ /* * Welcome to GitLab css! - * If you need to add or modify UI component that is commont for many pages - * like table or typography then make changes in framework/ directory. - * If you need to add uniq style that should affect only one page - use pages/ + * If you need to add or modify UI component that is common for many pages + * like a table or typography then make changes in the framework/ directory. + * If you need to add unique style that should affect only one page - use pages/ * directory. */ @@ -48,4 +48,4 @@ /* * Styles for JS behaviors. */ -@import "behaviors.scss"; +@import "behaviors.scss"; \ No newline at end of file From daca1c6511c3a09d5f0c33a8c4d29487e668afc2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 21:35:52 +0200 Subject: [PATCH 094/119] Fix broken tests --- app/models/ci/commit.rb | 4 ++-- lib/api/commit_statuses.rb | 2 +- spec/models/ci/commit_spec.rb | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 296890c5e7c..f6fc4645947 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -82,7 +82,7 @@ module Ci end def stage - running_or_pending = statuses.latest.running_or_pending + running_or_pending = statuses.latest.running_or_pending.ordered running_or_pending.first.try(:stage) end @@ -189,7 +189,7 @@ module Ci end def matrix_for_ref?(ref) - latest_builds_for_ref(ref).size > 1 + builds_without_retry_for_ref(ref).size > 1 end def config_processor diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 50ca89079e0..2c0596c9dfb 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -43,7 +43,7 @@ module API # Examples: # POST /projects/:id/statuses/:sha post ':id/statuses/:sha' do - authorize! :create_commit_statuses, user_project + authorize! :create_commit_status, user_project required_attributes! [:state] attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name] commit = @project.commit(params[:sha]) diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 371add4ee59..330971174fb 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -125,7 +125,7 @@ describe Ci::Commit do end it 'returns all refs' do - is_expected.to contain_exactly('master', 'develop') + is_expected.to contain_exactly('master', 'develop', nil) end end @@ -225,9 +225,10 @@ describe Ci::Commit do it 'rebuilds commit' do expect(commit.status).to eq('skipped') expect(create_builds(trigger_request)).to be_truthy - commit.builds.reload - expect(commit.builds.size).to eq(2) - expect(commit.status).to eq('pending') + + # since everything in Ci::Commit is cached we need to fetch a new object + new_commit = Ci::Commit.find_by_id(commit.id) + expect(new_commit.status).to eq('pending') end end end From 766da5fa7bdded8a333a758d7b172533ade5a934 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 22:34:59 +0200 Subject: [PATCH 095/119] Fix broken matrix_for_ref? Signed-off-by: Kamil Trzcinski --- app/models/ci/commit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index f6fc4645947..68864edfbbf 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -189,7 +189,7 @@ module Ci end def matrix_for_ref?(ref) - builds_without_retry_for_ref(ref).size > 1 + latest_builds_for_ref(ref).size > 1 end def config_processor From 4bf69b0bd9a27e675c98eee998f24c0122030731 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 23:59:21 +0200 Subject: [PATCH 096/119] Fix feature tests --- app/views/projects/commit/ci.html.haml | 2 +- features/steps/project/commits/commits.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index 585f012fe04..4a1ef378a30 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -21,7 +21,7 @@ \.gitlab-ci.yml not found in this commit .gray-content-block.second-block - Latest + Latest builds - if @ci_commit.duration > 0 %small.pull-right %i.fa.fa-time diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index ae5f90004e6..a3cb83880e3 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -118,6 +118,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'I see builds list' do expect(page).to have_content "build: pending" - expect(page).to have_content "Builds for master" + expect(page).to have_content "Latest builds" end end From 42fb52adf4460682c35d70930bcb2379ccb26171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A7=D0=B8=D0=BD=D0=B3=D0=B8=D0=B7=20=D0=90=D1=83=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=D1=81=D0=BE=D0=B2?= Date: Tue, 13 Oct 2015 04:41:51 +0600 Subject: [PATCH 097/119] Use css style --- app/views/dashboard/projects/_projects.html.haml | 3 ++- app/views/groups/_projects.html.haml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index 30372e98430..e641b82f819 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -5,6 +5,7 @@ - if current_user.can_create_project? %span.input-group-btn = link_to new_project_path, class: 'btn btn-green' do - + New Project + %i.fa.fa-plus + New Project = render 'shared/projects/list', projects: @projects, ci: true diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 9c73f7a0ef7..53c11a471b0 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -5,6 +5,7 @@ - if can? current_user, :create_projects, @group %span.input-group-btn = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do - + New Project + %i.fa.fa-plus + New Project = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false From 7816488a8967d4c87e84a35027499c1a5cb5406e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 12 Oct 2015 23:02:27 -0700 Subject: [PATCH 098/119] Add "New Page" button to Wiki Pages tab Closes #2998 --- CHANGELOG | 1 + app/views/projects/wikis/pages.html.haml | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a3d796bea66..d86f28bdc9a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.1.0 (unreleased) - Fix position of hamburger in header for smaller screens (Han Loong Liauw) - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji) - Persist filters when sorting on admin user page (Jerry Lukins) + - Add "New Page" button to Wiki Pages tab (Stan Hu) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 03e6a522b25..d179a1abec1 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -3,6 +3,7 @@ = render 'nav' .gray-content-block + = render 'main_links' %h3.page-title All Pages %ul.content-list From cb9b53958a4b49f4c1ab796d430674a64f1ccb9f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 13 Oct 2015 11:12:39 +0300 Subject: [PATCH 099/119] update changelog --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a3d796bea66..8f0bbb12d7b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,8 @@ v 8.1.0 (unreleased) - Fix position of hamburger in header for smaller screens (Han Loong Liauw) - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji) - Persist filters when sorting on admin user page (Jerry Lukins) + - Add spellcheck=false to certain input fields + - Invalidate stored service password if the endpoint URL is changed v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) From 0e34154d214c0e470b7783b5086a70b4fe4cb08f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Oct 2015 19:35:09 +0300 Subject: [PATCH 100/119] Project names are not fully shown if group name is too big, even on group page view --- CHANGELOG | 1 + app/views/groups/_projects.html.haml | 2 +- app/views/shared/projects/_list.html.haml | 3 ++- app/views/shared/projects/_project.html.haml | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8f0bbb12d7b..063b85d0eb7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,7 @@ v 8.1.0 (unreleased) - Persist filters when sorting on admin user page (Jerry Lukins) - Add spellcheck=false to certain input fields - Invalidate stored service password if the endpoint URL is changed + - Project names are not fully shown if group name is too big, even on group page view v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index 76da3276e47..133f3e2d5a8 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -7,4 +7,4 @@ = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do New project - = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false + = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 16e1d8421de..357cfd6a370 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -2,11 +2,12 @@ - avatar = true unless local_assigns[:avatar] == false - stars = true unless local_assigns[:stars] == false - ci = false unless local_assigns[:ci] == true +- skip_namespace = false unless local_assigns[:skip_namespace] == true %ul.projects-list - projects.each_with_index do |project, i| - css_class = (i >= projects_limit) ? 'hide' : nil - = render "shared/projects/project", project: project, + = render "shared/projects/project", project: project, skip_namespace: skip_namespace, avatar: avatar, stars: stars, css_class: css_class, ci: ci - if projects.size > projects_limit diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index e67e5a8a638..aee839b44e7 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -1,6 +1,7 @@ - avatar = true unless local_assigns[:avatar] == false - stars = true unless local_assigns[:stars] == false - ci = false unless local_assigns[:ci] == true +- skip_namespace = false unless local_assigns[:skip_namespace] == true - css_class = '' unless local_assigns[:css_class] - css_class += " no-description" unless project.description.present? %li.project-row{ class: css_class } @@ -11,7 +12,7 @@ = project_icon(project, alt: '', class: 'avatar project-avatar s46') %span.project-full-name %span.namespace-name - - if project.namespace + - if project.namespace && !skip_namespace = project.namespace.human_name \/ %span.project-name.filter-title From e4a9447451bfccbea6943bb5546b03c81321eb77 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 10:55:06 +0200 Subject: [PATCH 101/119] Rename bootstrap css file and refactor typography css Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework.scss | 5 +- app/assets/stylesheets/framework/mixins.scss | 141 ----------------- app/assets/stylesheets/framework/tables.scss | 16 ++ .../{gl_bootstrap.scss => tw_bootstrap.scss} | 20 --- ...ables.scss => tw_bootstrap_variables.scss} | 0 .../stylesheets/framework/typography.scss | 145 +++++++++++++++++- 6 files changed, 162 insertions(+), 165 deletions(-) rename app/assets/stylesheets/framework/{gl_bootstrap.scss => tw_bootstrap.scss} (93%) rename app/assets/stylesheets/framework/{gl_variables.scss => tw_bootstrap_variables.scss} (100%) diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index c5e23c1c328..1ec9d2fd84f 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -2,8 +2,9 @@ @import "framework/variables"; @import "framework/mixins"; @import "framework/layout"; -@import 'framework/gl_variables'; -@import 'framework/gl_bootstrap'; +@import 'framework/tw_bootstrap_variables'; +@import 'framework/tw_bootstrap'; + @import "framework/avatar.scss"; @import "framework/blocks.scss"; @import "framework/buttons.scss"; diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index c74a6d39824..089e6958eeb 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -54,147 +54,6 @@ @include box-shadow(0 0 0 3px #f1f1f1); } -@mixin md-typography { - color: $md-text-color; - - a { - color: $md-link-color; - } - - img { - max-width: 100%; - } - - *:first-child { - margin-top: 0; - } - - code { - font-family: $monospace_font; - white-space: pre; - word-wrap: normal; - padding: 1px 2px; - } - - kbd { - display: inline-block; - padding: 3px 5px; - font-size: 11px; - line-height: 10px; - color: #555; - vertical-align: middle; - background-color: #FCFCFC; - border-width: 1px; - border-style: solid; - border-color: #CCC #CCC #BBB; - border-image: none; - border-radius: 3px; - box-shadow: 0px -1px 0px #BBB inset; - } - - h1 { - font-size: 1.3em; - font-weight: 600; - margin: 24px 0 12px 0; - padding: 0 0 10px 0; - border-bottom: 1px solid #e7e9ed; - color: #313236; - } - - h2 { - font-size: 1.2em; - font-weight: 600; - margin: 24px 0 12px 0; - color: #313236; - } - - h3 { - margin: 24px 0 12px 0; - font-size: 1.25em; - } - - h4 { - margin: 24px 0 12px 0; - font-size: 1.1em; - } - - h5 { - margin: 24px 0 12px 0; - font-size: 1em; - } - - h6 { - margin: 24px 0 12px 0; - font-size: 0.90em; - } - - blockquote { - padding: 8px 21px; - margin: 12px 0 12px; - border-left: 3px solid #e7e9ed; - } - - blockquote p { - color: #7f8fa4 !important; - font-size: 15px; - line-height: 1.5; - } - - p { - color:#5c5d5e; - margin:6px 0 0 0; - } - - table { - @extend .table; - @extend .table-bordered; - margin: 12px 0 12px 0; - color: #5c5d5e; - th { - background: #f8fafc; - } - } - - pre { - margin: 12px 0 12px 0 !important; - background-color: #f8fafc !important; - font-size: 13px !important; - color: #5b6169 !important; - line-height: 1.6em !important; - @include border-radius(2px); - } - - p > code { - font-weight: inherit; - } - - - ul { - color: #5c5d5e; - } - - li { - line-height: 1.6em; - } - - a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { - &:before { - margin-right: 4px; - - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - content: "\f0c6"; - } - - &:hover:before { - text-decoration: none; - } - } -} - - @mixin str-truncated($max_width: 82%) { display: inline-block; overflow: hidden; diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index a66e45577de..76163b3a05e 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -1,5 +1,21 @@ table { &.table { + .dropdown-menu a { + text-decoration: none; + } + + .success, + .warning, + .danger, + .info { + color: #fff; + + a:not(.btn) { + text-decoration: underline; + color: #fff; + } + } + tr { td, th { padding: 8px 10px; diff --git a/app/assets/stylesheets/framework/gl_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss similarity index 93% rename from app/assets/stylesheets/framework/gl_bootstrap.scss rename to app/assets/stylesheets/framework/tw_bootstrap.scss index eb8d23d6453..722b44d2d10 100644 --- a/app/assets/stylesheets/framework/gl_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -251,23 +251,3 @@ .text-info:hover { color: $brand-info; } - -// Tables ===================================================================== - -table.table { - .dropdown-menu a { - text-decoration: none; - } - - .success, - .warning, - .danger, - .info { - color: #fff; - - a:not(.btn) { - text-decoration: underline; - color: #fff; - } - } -} diff --git a/app/assets/stylesheets/framework/gl_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss similarity index 100% rename from app/assets/stylesheets/framework/gl_variables.scss rename to app/assets/stylesheets/framework/tw_bootstrap_variables.scss diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 6a3cb49baae..bf36f96cc97 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -1,3 +1,144 @@ +@mixin md-typography { + color: $md-text-color; + + a { + color: $md-link-color; + } + + img { + max-width: 100%; + } + + *:first-child { + margin-top: 0; + } + + code { + font-family: $monospace_font; + white-space: pre; + word-wrap: normal; + padding: 1px 2px; + } + + kbd { + display: inline-block; + padding: 3px 5px; + font-size: 11px; + line-height: 10px; + color: #555; + vertical-align: middle; + background-color: #FCFCFC; + border-width: 1px; + border-style: solid; + border-color: #CCC #CCC #BBB; + border-image: none; + border-radius: 3px; + box-shadow: 0px -1px 0px #BBB inset; + } + + h1 { + font-size: 1.3em; + font-weight: 600; + margin: 24px 0 12px 0; + padding: 0 0 10px 0; + border-bottom: 1px solid #e7e9ed; + color: #313236; + } + + h2 { + font-size: 1.2em; + font-weight: 600; + margin: 24px 0 12px 0; + color: #313236; + } + + h3 { + margin: 24px 0 12px 0; + font-size: 1.25em; + } + + h4 { + margin: 24px 0 12px 0; + font-size: 1.1em; + } + + h5 { + margin: 24px 0 12px 0; + font-size: 1em; + } + + h6 { + margin: 24px 0 12px 0; + font-size: 0.90em; + } + + blockquote { + padding: 8px 21px; + margin: 12px 0 12px; + border-left: 3px solid #e7e9ed; + } + + blockquote p { + color: #7f8fa4 !important; + font-size: 15px; + line-height: 1.5; + } + + p { + color:#5c5d5e; + margin:6px 0 0 0; + } + + table { + @extend .table; + @extend .table-bordered; + margin: 12px 0 12px 0; + color: #5c5d5e; + th { + background: #f8fafc; + } + } + + pre { + margin: 12px 0 12px 0 !important; + background-color: #f8fafc !important; + font-size: 13px !important; + color: #5b6169 !important; + line-height: 1.6em !important; + @include border-radius(2px); + } + + p > code { + font-weight: inherit; + } + + + ul { + color: #5c5d5e; + } + + li { + line-height: 1.6em; + } + + a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] { + &:before { + margin-right: 4px; + + font: normal normal normal 14px/1 FontAwesome; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + content: "\f0c6"; + } + + &:hover:before { + text-decoration: none; + } + } +} + + /** * Headers * @@ -6,7 +147,7 @@ body { text-rendering:optimizeLegibility; -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px; } - + .page-title { margin-top: 0px; line-height: 1.3; @@ -127,4 +268,4 @@ textarea.js-gfm-input { .strikethrough { text-decoration: line-through; -} \ No newline at end of file +} From 029edcc391d492c30f0598e70dba3433189806c7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 11:02:44 +0200 Subject: [PATCH 102/119] Fix padding for controls in list Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 3bfed8de772..fdfbb886926 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -117,7 +117,7 @@ ul.content-list { } .controls { - padding-top: 10px; + padding-top: 5px; float: right; } } From f1e3894973abd96a318b5f3f56bfce110c620c39 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 11:23:10 +0200 Subject: [PATCH 103/119] Remove unused twbs components Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/tw_bootstrap.scss | 2 -- app/assets/stylesheets/pages/note_form.scss | 1 - 2 files changed, 3 deletions(-) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 722b44d2d10..99d028d1228 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -32,8 +32,6 @@ @import "bootstrap/pager"; @import "bootstrap/labels"; @import "bootstrap/badges"; -@import "bootstrap/jumbotron"; -@import "bootstrap/thumbnails"; @import "bootstrap/alerts"; @import "bootstrap/progress-bars"; @import "bootstrap/list-group"; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index dcd1aed7196..4392f08942b 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -65,7 +65,6 @@ .note-image-attach { @extend .col-md-4; - @extend .thumbnail; margin-left: 45px; float: none; } From ab604c73ef986d03d5c5c04075663d87ef390479 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 11:33:46 +0200 Subject: [PATCH 104/119] Temporary return sm and xs button sizes Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/buttons.scss | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 11acbe3adfa..e5f0c0ad9ef 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -6,7 +6,7 @@ font-size: 13px; font-weight: 600; line-height: 18px; - padding: 11px 16px; + padding: 11px $gl-padding; letter-spacing: .4px; &:focus, @@ -71,6 +71,14 @@ @include btn-default; @include btn-white; + &.btn-sm { + padding: 5px 10px; + } + + &.btn-xs { + padding: 1px 5px; + } + &.btn-success, &.btn-new, &.btn-create, From e7cc554cc181cbb850f89af26e64a9ab56116f28 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 13 Oct 2015 11:50:37 +0200 Subject: [PATCH 105/119] Fix retry and cancel URLs --- app/models/ci/build.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index cb6a1015210..f8c731a7bf7 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -219,13 +219,15 @@ module Ci def cancel_url if active? - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + Gitlab::Application.routes.url_helpers. + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) end end def retry_url if commands.present? - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + Gitlab::Application.routes.url_helpers. + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) end end From 4ad61519f9f9cdc82b1ee6bd1ed92905692c7e7f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 13:02:27 +0200 Subject: [PATCH 106/119] Fix control buttons in lists Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index fdfbb886926..c5764c36597 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -117,8 +117,12 @@ ul.content-list { } .controls { - padding-top: 5px; + padding-top: 4px; float: right; + + .btn { + padding: 10px 14px; + } } } } From 219451241a92ff9e81dcb77914e6d659ea2f7376 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 13:03:27 +0200 Subject: [PATCH 107/119] Fix UI issue on project page with no ssh key message Signed-off-by: Dmitriy Zaporozhets --- app/views/layouts/_page.html.haml | 3 ++- app/views/projects/show.html.haml | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 2468687b56d..1a883e20e89 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -6,7 +6,7 @@ = brand_header_logo .gitlab-text-container %h3 GitLab - + - if defined?(sidebar) && sidebar = render "layouts/nav/#{sidebar}" - elsif current_user @@ -23,6 +23,7 @@ = current_user.username .content-wrapper = render "layouts/flash" + = yield :flash_message %div{ class: container_class } .content .clearfix diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index efa119edd5a..e95d987d74c 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -2,9 +2,10 @@ - if current_user = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity") -- if current_user && can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - = render 'shared/no_password' += content_for :flash_message do + - if current_user && can?(current_user, :download_code, @project) + = render 'shared/no_ssh' + = render 'shared/no_password' - if prefer_readme? = render 'projects/last_push' From e75655950d27eff75dba0aadd2f853f84034bb3a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 13:37:15 +0200 Subject: [PATCH 108/119] Several fixes for UI on mobile Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/mobile.scss | 14 +++++++++++--- app/assets/stylesheets/pages/projects.scss | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index 36ae126f865..cea47fba192 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -23,7 +23,7 @@ margin-right: 0; } - .issues-filters, + .issues-details-filters, .dash-projects-filters, .check-all-holder { display: none; @@ -83,6 +83,7 @@ .center-top-menu { height: 45px; + margin-bottom: 30px; li a { font-size: 14px; @@ -90,9 +91,11 @@ } } - .projects-search-form { - margin: 0 -5px !important; + .activity-filter-block { + display: none; + } + .projects-search-form { .btn { display: none; } @@ -100,6 +103,11 @@ } @media (max-width: $screen-sm-max) { + .page-with-sidebar .content-wrapper { + padding: 0; + padding-top: 1px; + } + .issues-filters { .milestone-filter, .labels-filter { display: none; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 0031ab5151b..9d42c0f28e7 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -63,6 +63,7 @@ } p { + padding: 0 $gl-padding; color: #5c5d5e; } } From 2f68fb9cc3622eb90c955b6936e91ea766f262b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 14:38:38 +0200 Subject: [PATCH 109/119] Small css cleanup Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/common.scss | 4 ---- app/assets/stylesheets/pages/projects.scss | 5 ----- app/views/dashboard/_activities.html.haml | 7 +++---- app/views/groups/show.html.haml | 8 +++----- app/views/projects/_activity.html.haml | 7 +++---- app/views/projects/buttons/_notifications.html.haml | 2 +- app/views/projects/new.html.haml | 4 ++-- 7 files changed, 12 insertions(+), 25 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 03919f15f1f..e1a1793be9c 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -398,7 +398,3 @@ table { .space-right { margin-right: 10px; } - -.in-line { - display: inline-block; -} diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 9d42c0f28e7..f7a22849003 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -511,8 +511,3 @@ pre.light-well { margin-top: -1px; } } - -.inline-form { - display: inline-block; -} - diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml index 19d919f9b6a..f98fd9f06ba 100644 --- a/app/views/dashboard/_activities.html.haml +++ b/app/views/dashboard/_activities.html.haml @@ -3,10 +3,9 @@ .gray-content-block - if current_user - %ul.nav.nav-pills.event_filter.pull-right - %li.pull-right - = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do - %i.fa.fa-rss + .pull-right + = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do + %i.fa.fa-rss = render 'shared/event_filter' .content_list diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index a9ba9d2ba10..dc8e81323a6 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -25,11 +25,9 @@ .hidden-xs - if current_user = render "events/event_last_push", event: @last_push - - %ul.nav.nav-pills.event_filter.pull-right - %li - = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do - %i.fa.fa-rss + .pull-right + = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do + %i.fa.fa-rss = render 'shared/event_filter' %hr diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 1261f6254d7..c2683bc6219 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -1,10 +1,9 @@ = render 'projects/last_push' .gray-content-block.activity-filter-block - if current_user - %ul.nav.nav-pills.event_filter.pull-right - %li - = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do - %i.fa.fa-rss + .pull-right + = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'btn rss-btn' do + %i.fa.fa-rss = render 'shared/event_filter' .content_list{:"data-href" => activity_project_path(@project)} diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 4b69a6d7a6f..3bc2daeec4e 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,6 +1,6 @@ - return unless @membership -= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do += form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do = hidden_field_tag :notification_type, 'project' = hidden_field_tag :notification_id, @membership.id = hidden_field_tag :notification_level diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 1b093c8f514..daab2326bc7 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -111,10 +111,10 @@ - if current_user.can_create_group? .pull-right - .light.in-line + .light.inline .space-right Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-xs" do + = link_to new_group_path, class: "btn" do Create a group .save-project-loader.hide From a0a488ed13e2a07e0dd42e8795c50ea762917f20 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 13 Oct 2015 16:41:48 +0200 Subject: [PATCH 110/119] Apply new design to files page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + .../javascripts/line_highlighter.js.coffee | 6 +- app/assets/stylesheets/framework/tables.scss | 2 +- .../framework/tw_bootstrap_variables.scss | 2 + .../stylesheets/framework/variables.scss | 1 + app/assets/stylesheets/pages/tree.scss | 34 +++--- app/views/projects/blob/_blob.html.haml | 2 +- .../repositories/_download_archive.html.haml | 4 +- app/views/projects/tree/_readme.html.haml | 13 ++- app/views/projects/tree/_tree.html.haml | 106 +++++++++--------- .../fixtures/line_highlighter.html.haml | 2 +- .../line_highlighter_spec.js.coffee | 2 +- 12 files changed, 87 insertions(+), 88 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8f0bbb12d7b..8b3df158a04 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,7 @@ v 8.1.0 (unreleased) - Persist filters when sorting on admin user page (Jerry Lukins) - Add spellcheck=false to certain input fields - Invalidate stored service password if the endpoint URL is changed + - Apply new design for Files page v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/assets/javascripts/line_highlighter.js.coffee b/app/assets/javascripts/line_highlighter.js.coffee index e604e6025c2..2254a3f91ae 100644 --- a/app/assets/javascripts/line_highlighter.js.coffee +++ b/app/assets/javascripts/line_highlighter.js.coffee @@ -6,7 +6,7 @@ # # ### Example Markup # -#
+#
#
#
# 1 @@ -53,7 +53,7 @@ class @LineHighlighter $.scrollTo("#L#{range[0]}", offset: -150) bindEvents: -> - $('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler + $('#blob-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler # While it may seem odd to bind to the mousedown event and then throw away # the click event, there is a method to our madness. @@ -62,7 +62,7 @@ class @LineHighlighter # active state even when the event is cancelled, resulting in an ugly border # around the link and/or a persisted underline text decoration. - $('#tree-content-holder').on 'click', 'a[data-line-number]', (event) -> + $('#blob-content-holder').on 'click', 'a[data-line-number]', (event) -> event.preventDefault() clickHandler: (event) => diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 76163b3a05e..789b34020c1 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -28,7 +28,7 @@ table { border-bottom: 1px solid $border-color !important; } td { - border-color: #F1F1F1 !important; + border-color: $table-border-color !important; border-bottom: 1px solid; } } diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss index 18632da4f2a..63868a34e2a 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss @@ -156,3 +156,5 @@ $nav-link-padding: 13px $gl-padding; $pre-bg: #f8fafc !default; $pre-color: $gl-gray !default; $pre-border-color: #e7e9ed; + +$table-bg-accent: $background-color; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index eb9a2966389..91954683c3e 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -16,6 +16,7 @@ $avatar_radius: 50%; $code_font_size: 13px; $code_line_height: 1.5; $border-color: #dce0e6; +$table-border-color: #eef0f2; $background-color: #F7F8FA; $header-height: 58px; $fixed-layout-width: 1200px; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 271cc547e2b..dadd86e88cc 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,7 +1,7 @@ .tree-holder { - .tree-content-holder { - float: left; - width: 100%; + .tree-table-holder { + margin-left: -$gl-padding; + margin-right: -$gl-padding; } .tree_progress { @@ -13,10 +13,15 @@ } .tree-table { - @extend .table; - @include border-radius(0); + margin-bottom: 0; tr { + > td, > th { + padding: 10px $gl-padding; + line-height: 32px; + border-color: $table-border-color !important; + } + &:hover { td { background: $hover; @@ -27,9 +32,9 @@ } &.selected { td { - background: $background-color; - border-top: 1px solid #EEE; - border-bottom: 1px solid #EEE; + background: $gray-dark; + border-top: 1px solid $border-gray-dark; + border-bottom: 1px solid $border-gray-dark; } } } @@ -85,19 +90,6 @@ margin-right: 15px; } -.readme-holder { - margin: 0 auto; - - .readme-file-title { - font-size: 14px; - font-weight: bold; - margin-bottom: 20px; - color: #777; - border-bottom: 1px solid #DDD; - padding: 10px 0; - } -} - .blob-commit-info { list-style: none; margin: 0; diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index b4c7d8b9b71..a1ae1397584 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -19,7 +19,7 @@ - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) = render blob_commit, project: @project -%div#tree-content-holder.tree-content-holder +%div#blob-content-holder.blob-content-holder %article.file-holder .file-title = blob_icon blob.mode, blob.name diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index b9486a9b492..07c24950ee2 100644 --- a/app/views/projects/repositories/_download_archive.html.haml +++ b/app/views/projects/repositories/_download_archive.html.haml @@ -3,10 +3,10 @@ - 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 + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-success col-xs-10', rel: 'nofollow' do %i.fa.fa-download %span Download zip - %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } %span.caret %span.sr-only Select Archive Format diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml index f082d711865..7e9af19c8ba 100644 --- a/app/views/projects/tree/_readme.html.haml +++ b/app/views/projects/tree/_readme.html.haml @@ -1,7 +1,8 @@ -%article.readme-holder#README - = link_to '#README' do - %h4.readme-file-title - %i.fa.fa-file - = readme.name - .wiki +%article.file-holder.readme-holder#README + .file-title + = link_to '#README' do + %strong + %i.fa.fa-file + = readme.name + .file-content.wiki = render_readme(readme) diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml index 457f8a4a585..7ff48e32e60 100644 --- a/app/views/projects/tree/_tree.html.haml +++ b/app/views/projects/tree/_tree.html.haml @@ -1,59 +1,61 @@ -%ul.breadcrumb.repo-breadcrumb - %li - = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do - = @project.path - - tree_breadcrumbs(tree, 6) do |title, path| +.gray-content-block + %ul.breadcrumb.repo-breadcrumb %li - - if path - = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) - - else - = link_to title, '#' - - if allowed_tree_edit? - %li - %span.dropdown - %a.dropdown-toggle.btn.btn-xs.add-to-tree{href: '#', "data-toggle" => "dropdown"} - = icon('plus') - %ul.dropdown-menu - %li - = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do - = icon('pencil fw') - Create file - %li - = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do - = icon('file fw') - Upload file - %li.divider - %li - = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do - = icon('folder fw') - New directory + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = @project.path + - tree_breadcrumbs(tree, 6) do |title, path| + %li + - if path + = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path) + - else + = link_to title, '#' + - if allowed_tree_edit? + %li + %span.dropdown + %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} + = icon('plus') + %ul.dropdown-menu + %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do + = icon('pencil fw') + Create file + %li + = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do + = icon('file fw') + Upload file + %li.divider + %li + = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do + = icon('folder fw') + New directory -%div#tree-content-holder.tree-content-holder.prepend-top-20 - %table#tree-slider{class: "table_#{@hex_path} tree-table" } - %thead - %tr - %th Name - %th Last Update - %th.hidden-xs - .pull-left Last Commit - .last-commit.hidden-sm.pull-left -   - %i.fa.fa-angle-right -   - %small.light - = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit) - – - = truncate(@commit.title, length: 50) - = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' +%div#tree-content-holder.tree-content-holder + .tree-table-holder + %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" } + %thead + %tr + %th Name + %th Last Update + %th.hidden-xs + .pull-left Last Commit + .last-commit.hidden-sm.pull-left +   + %i.fa.fa-angle-right +   + %small.light + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit) + – + = truncate(@commit.title, length: 50) + = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' - - if @path.present? - %tr.tree-item - %td.tree-item-file-name - = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10' - %td - %td.hidden-xs + - if @path.present? + %tr.tree-item + %td.tree-item-file-name + = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10' + %td + %td.hidden-xs - = render_tree(tree) + = render_tree(tree) - if tree.readme = render "projects/tree/readme", readme: tree.readme diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml index da1ebcdb23c..514877340e4 100644 --- a/spec/javascripts/fixtures/line_highlighter.html.haml +++ b/spec/javascripts/fixtures/line_highlighter.html.haml @@ -1,4 +1,4 @@ -#tree-content-holder +#blob-content-holder .file-content .line-numbers - 1.upto(25) do |i| diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee index 57453c716a5..a073f21e7bc 100644 --- a/spec/javascripts/line_highlighter_spec.js.coffee +++ b/spec/javascripts/line_highlighter_spec.js.coffee @@ -39,7 +39,7 @@ describe 'LineHighlighter', -> expect(spy).toHaveBeenPrevented() it 'handles garbage input from the hash', -> - func = -> new LineHighlighter('#tree-content-holder') + func = -> new LineHighlighter('#blob-content-holder') expect(func).not.toThrow() describe '#clickHandler', -> From a092d27025cbaf4abe8a1198a25c5becfc07c9b1 Mon Sep 17 00:00:00 2001 From: sue445 Date: Wed, 14 Oct 2015 00:30:48 +0900 Subject: [PATCH 111/119] [ci skip] Fix wrong comment According to `attributes_for_keys` and API doc, `POST /projects/:id/merge_requests` is received `target_project_id` (NOT `target_project`) --- lib/api/merge_requests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 63ea2f05438..f3a59fadf24 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -99,7 +99,7 @@ module API # id (required) - The ID of a project - this will be the source of the merge request # source_branch (required) - The source branch # target_branch (required) - The target branch - # target_project - The target project of the merge request defaults to the :id of the project + # target_project_id - The target project of the merge request defaults to the :id of the project # assignee_id - Assignee user ID # title (required) - Title of MR # description - Description of MR From 2297a7ba1f5d004c877a7cb82510d7d635f90ec0 Mon Sep 17 00:00:00 2001 From: sue445 Date: Wed, 14 Oct 2015 00:40:37 +0900 Subject: [PATCH 112/119] [ci skip] Add missing parameters in API doc --- doc/api/merge_requests.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index bb551fc67f7..ffa7f2cdf14 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -188,6 +188,7 @@ Parameters: - `title` (required) - Title of MR - `description` (optional) - Description of MR - `target_project_id` (optional) - The target project (numeric id) +- `labels` (optional) - Labels for MR as a comma-separated list ```json { @@ -239,6 +240,7 @@ Parameters: - `title` - Title of MR - `description` - Description of MR - `state_event` - New state (close|reopen|merge) +- `labels` (optional) - Labels for MR as a comma-separated list ```json { From 135ec3242d803fc46087b59d10016e55207f6743 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 13 Oct 2015 18:27:25 +0200 Subject: [PATCH 113/119] Reduce font size of commit references to not stand out as much --- app/assets/stylesheets/generic/gfm.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/generic/gfm.scss index bd9200ace23..5ae0520fd7b 100644 --- a/app/assets/stylesheets/generic/gfm.scss +++ b/app/assets/stylesheets/generic/gfm.scss @@ -22,4 +22,5 @@ .gfm-commit, .gfm-commit_range { font-family: $monospace_font; + font-size: 90%; } From 8346dde0520ed625446ecc5d5a35b53e0b60dbb0 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 9 Oct 2015 20:07:29 +0300 Subject: [PATCH 114/119] Only render 404 page from /public --- CHANGELOG | 1 + app/controllers/application_controller.rb | 6 +----- app/controllers/import/bitbucket_controller.rb | 2 +- app/controllers/import/fogbugz_controller.rb | 2 +- app/controllers/import/github_controller.rb | 2 +- app/controllers/import/gitlab_controller.rb | 2 +- app/controllers/import/gitorious_controller.rb | 2 +- app/controllers/import/google_code_controller.rb | 2 +- app/controllers/projects/avatars_controller.rb | 2 +- app/controllers/projects/blob_controller.rb | 6 +++--- app/controllers/projects/raw_controller.rb | 2 +- app/controllers/projects/tree_controller.rb | 6 +++--- app/controllers/projects/uploads_controller.rb | 2 +- app/controllers/uploads_controller.rb | 6 +++--- lib/extracts_path.rb | 2 +- 15 files changed, 21 insertions(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8f0bbb12d7b..18fc76e444b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,7 @@ v 8.1.0 (unreleased) - Persist filters when sorting on admin user page (Jerry Lukins) - Add spellcheck=false to certain input fields - Invalidate stored service password if the endpoint URL is changed + - Only render 404 page from /public v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 527c9da0faa..2b2ea3dff16 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base rescue_from ActiveRecord::RecordNotFound do |exception| log_exception(exception) - render "errors/not_found", layout: "errors", status: 404 + render_404 end protected @@ -149,10 +149,6 @@ class ApplicationController < ActionController::Base render "errors/access_denied", layout: "errors", status: 404 end - def not_found! - render "errors/not_found", layout: "errors", status: 404 - end - def git_not_found! render "errors/git_not_found", layout: "errors", status: 404 end diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index f84f85a7df8..25e58724860 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController end def verify_bitbucket_import_enabled - not_found! unless bitbucket_import_enabled? + render_404 unless bitbucket_import_enabled? end def bitbucket_auth diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index 849646cd665..18300390851 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -99,6 +99,6 @@ class Import::FogbugzController < Import::BaseController end def verify_fogbugz_import_enabled - not_found! unless fogbugz_import_enabled? + render_404 unless fogbugz_import_enabled? end end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index f21fbd9ecca..aae77d384c6 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -47,7 +47,7 @@ class Import::GithubController < Import::BaseController end def verify_github_import_enabled - not_found! unless github_import_enabled? + render_404 unless github_import_enabled? end def github_auth diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb index 27af19f5f61..23a396e8084 100644 --- a/app/controllers/import/gitlab_controller.rb +++ b/app/controllers/import/gitlab_controller.rb @@ -44,7 +44,7 @@ class Import::GitlabController < Import::BaseController end def verify_gitlab_import_enabled - not_found! unless gitlab_import_enabled? + render_404 unless gitlab_import_enabled? end def gitlab_auth diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb index f24cdb3709a..eecbe380c9e 100644 --- a/app/controllers/import/gitorious_controller.rb +++ b/app/controllers/import/gitorious_controller.rb @@ -42,7 +42,7 @@ class Import::GitoriousController < Import::BaseController end def verify_gitorious_import_enabled - not_found! unless gitorious_import_enabled? + render_404 unless gitorious_import_enabled? end end diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb index 82fadeb7e83..41472a6fe6c 100644 --- a/app/controllers/import/google_code_controller.rb +++ b/app/controllers/import/google_code_controller.rb @@ -106,7 +106,7 @@ class Import::GoogleCodeController < Import::BaseController end def verify_google_code_import_enabled - not_found! unless google_code_import_enabled? + render_404 unless google_code_import_enabled? end def user_map diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 9c3763d5934..548f1b9ebfe 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -12,7 +12,7 @@ class Projects::AvatarsController < Projects::ApplicationController filename: @blob.name ) else - not_found! + render_404 end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index ae9b1384463..8cc2f21d887 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -113,14 +113,14 @@ class Projects::BlobController < Projects::ApplicationController end end - return not_found! + return render_404 end end def commit @commit = @repository.commit(@ref) - return not_found! unless @commit + return render_404 unless @commit end def assign_blob_vars @@ -128,7 +128,7 @@ class Projects::BlobController < Projects::ApplicationController @ref, @path = extract_ref(@id) rescue InvalidPathError - not_found! + render_404 end def after_edit_path diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 5f6fbce795e..d5ee6ac8663 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -20,7 +20,7 @@ class Projects::RawController < Projects::ApplicationController disposition: 'inline' ) else - not_found! + render_404 end end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 7eaff1d61ee..bdcb1a3e297 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -10,7 +10,7 @@ class Projects::TreeController < Projects::ApplicationController before_action :authorize_push_code!, only: [:create_dir] def show - return not_found! unless @repository.commit(@ref) + return render_404 unless @repository.commit(@ref) if tree.entries.empty? if @repository.blob_at(@commit.id, @path) @@ -19,7 +19,7 @@ class Projects::TreeController < Projects::ApplicationController File.join(@ref, @path)) ) and return elsif @path.present? - return not_found! + return render_404 end end @@ -31,7 +31,7 @@ class Projects::TreeController < Projects::ApplicationController end def create_dir - return not_found! unless @commit_params.values.all? + return render_404 unless @commit_params.values.all? begin result = Files::CreateDirService.new(@project, current_user, @commit_params).execute diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 71ecc20dd95..e1fe7ea2114 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -20,7 +20,7 @@ class Projects::UploadsController < Projects::ApplicationController end def show - return not_found! if uploader.nil? || !uploader.file.exists? + return render_404 if uploader.nil? || !uploader.file.exists? disposition = uploader.image? ? 'inline' : 'attachment' send_file uploader.file.path, disposition: disposition diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 28536e359e5..868b05929d7 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -10,7 +10,7 @@ class UploadsController < ApplicationController end unless uploader.file && uploader.file.exists? - return not_found! + return render_404 end disposition = uploader.image? ? 'inline' : 'attachment' @@ -21,7 +21,7 @@ class UploadsController < ApplicationController def find_model unless upload_model && upload_mount - return not_found! + return render_404 end @model = upload_model.find(params[:id]) @@ -44,7 +44,7 @@ class UploadsController < ApplicationController return if authorized if current_user - not_found! + render_404 else authenticate_user! end diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb index 322aed5e27c..51e46da82cc 100644 --- a/lib/extracts_path.rb +++ b/lib/extracts_path.rb @@ -110,7 +110,7 @@ module ExtractsPath @project, @ref, @path) rescue RuntimeError, NoMethodError, InvalidPathError - not_found! + render_404 end def tree From 33c9d6e45c0800fd5a312af2c286826764fd7589 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 13 Oct 2015 16:51:13 +0200 Subject: [PATCH 115/119] Fix retry and cancel for build --- app/models/ci/build.rb | 4 ++-- app/views/projects/commit/ci.html.haml | 22 +++++++++++-------- .../commit_statuses/_commit_status.html.haml | 6 ++--- spec/features/commits_spec.rb | 10 ++++++++- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index f8c731a7bf7..cfafbf6786e 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -220,14 +220,14 @@ module Ci def cancel_url if active? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + cancel_namespace_project_build_path(gl_project.namespace, gl_project, self) end end def retry_url if commands.present? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url) + retry_namespace_project_build_path(gl_project.namespace, gl_project, self) end end diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml index 4a1ef378a30..ca71a91af15 100644 --- a/app/views/projects/commit/ci.html.haml +++ b/app/views/projects/commit/ci.html.haml @@ -3,11 +3,6 @@ = render "commit_box" = render "ci_menu" -- if @ci_project && current_user && can?(current_user, :manage_builds, @project) - .pull-right - - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-sm btn-danger' - - if @ci_commit.yaml_errors.present? .bs-callout.bs-callout-danger @@ -22,11 +17,18 @@ .gray-content-block.second-block Latest builds - - if @ci_commit.duration > 0 - %small.pull-right + + .pull-right + - if @ci_commit.duration > 0 %i.fa.fa-time #{time_interval_in_words @ci_commit.duration} +   + + - if @ci_project && current_user && can?(current_user, :manage_builds, @project) + - if @ci_commit.builds.running_or_pending.any? + = link_to "Cancel all", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger' + %table.table.builds %thead %tr @@ -41,7 +43,8 @@ %th Coverage %th - @ci_commit.refs.each do |ref| - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, coverage: @ci_project.try(:coverage_enabled?), controls: true + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, + locals: { coverage: @ci_project.try(:coverage_enabled?), allow_retry: true } - if @ci_commit.retried.any? .gray-content-block.second-block @@ -60,4 +63,5 @@ - if @ci_project && @ci_project.coverage_enabled? %th Coverage %th - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?) + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, + locals: { coverage: @ci_project.try(:coverage_enabled?) } diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index e3a17faf0bd..7314f8e79d3 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -41,11 +41,11 @@ #{commit_status.coverage}% %td - - if defined?(controls) && controls && current_user && can?(current_user, :manage_builds, gl_project) - .pull-right + .pull-right + - if current_user && can?(current_user, :manage_builds, commit_status.gl_project) - if commit_status.cancel_url = link_to commit_status.cancel_url, title: 'Cancel' do %i.fa.fa-remove.cred - - elsif commit_status.retry_url + - elsif defined?(allow_retry) && allow_retry && commit_status.retry_url = link_to commit_status.retry_url, method: :post, title: 'Retry' do %i.fa.fa-repeat diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index cbb6360069b..1adc2cdf70a 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -29,8 +29,16 @@ describe "Commits" do it { expect(page).to have_content @commit.git_author_name } end - describe "Cancel commit" do + describe "Cancel all builds" do it "cancels commit" do + visit ci_status_path(@commit) + click_on "Cancel all" + expect(page).to have_content "canceled" + end + end + + describe "Cancel build" do + it "cancels build" do visit ci_status_path(@commit) click_on "Cancel" expect(page).to have_content "canceled" From b46397548056e4e8ef00efe4f641c61ba1dd5230 Mon Sep 17 00:00:00 2001 From: Alex Lossent Date: Tue, 13 Oct 2015 11:29:57 +0200 Subject: [PATCH 116/119] Improve invalidation of stored service password if the endpoint URL is changed It now allows to specify a password at the same time as the new URL, and works on the service template admin pages. --- app/controllers/admin/services_controller.rb | 8 +++- .../projects/services_controller.rb | 8 +++- app/models/project_services/bamboo_service.rb | 2 +- .../project_services/teamcity_service.rb | 2 +- app/models/service.rb | 32 ++++++++++++--- spec/models/service_spec.rb | 39 +++++++++++++++++-- 6 files changed, 78 insertions(+), 13 deletions(-) diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index a62170662e1..46133588332 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -39,7 +39,13 @@ class Admin::ServicesController < Admin::ApplicationController end def application_services_params - params.permit(:id, + application_services_params = params.permit(:id, service: Projects::ServicesController::ALLOWED_PARAMS) + if application_services_params[:service].is_a?(Hash) + Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param| + application_services_params[:service].delete(param) if application_services_params[:service][param].blank? + end + end + application_services_params end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 3047ee8a1ff..129068ef019 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -9,6 +9,10 @@ class Projects::ServicesController < Projects::ApplicationController :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification] + + # Parameters to ignore if no value is specified + FILTER_BLANK_PARAMS = [:password] + # Authorize before_action :authorize_admin_project! before_action :service, only: [:edit, :update, :test] @@ -59,7 +63,9 @@ class Projects::ServicesController < Projects::ApplicationController def service_params service_params = params.require(:service).permit(ALLOWED_PARAMS) - service_params.delete("password") if service_params["password"].blank? + FILTER_BLANK_PARAMS.each do |param| + service_params.delete(param) if service_params[param].blank? + end service_params end end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 5f5255ab487..4a18c772e50 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -48,7 +48,7 @@ class BambooService < CiService end def reset_password - if prop_updated?(:bamboo_url) + if prop_modified?(:bamboo_url) && !prop_updated?(:password) self.password = nil end end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index fb11cad352e..a548a557dfe 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -45,7 +45,7 @@ class TeamcityService < CiService end def reset_password - if prop_updated?(:teamcity_url) + if prop_modified?(:teamcity_url) && !prop_updated?(:password) self.password = nil end end diff --git a/app/models/service.rb b/app/models/service.rb index 7e845d565b1..946ea1a096b 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -33,6 +33,8 @@ class Service < ActiveRecord::Base after_initialize :initialize_properties + after_commit :reset_updated_properties + belongs_to :project has_one :service_hook @@ -103,6 +105,7 @@ class Service < ActiveRecord::Base # Provide convenient accessor methods # for each serialized property. + # Also keep track of updated properties. def self.prop_accessor(*args) args.each do |arg| class_eval %{ @@ -111,19 +114,36 @@ class Service < ActiveRecord::Base end def #{arg}=(value) + updated_properties['#{arg}'] = #{arg} unless updated_properties.include?('#{arg}') self.properties['#{arg}'] = value end } end end - # ActiveRecord does not provide a mechanism to track changes in serialized keys. - # This is why we need to perform extra query to do it mannually. + # Returns a hash of the properties that have been assigned a new value since last save, + # indicating their original values (attr => original value). + # ActiveRecord does not provide a mechanism to track changes in serialized keys, + # so we need a specific implementation for service properties. + # This allows to track changes to properties set with the accessor methods, + # but not direct manipulation of properties hash. + def updated_properties + @updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new + end + def prop_updated?(prop_name) - relation_name = self.type.underscore - previous_value = project.send(relation_name).send(prop_name) - return false if previous_value.nil? - previous_value != send(prop_name) + # Check if a new value was set. + # The new value may or may not be the same as the old value + updated_properties.include?(prop_name) + end + + def prop_modified?(prop_name) + # Check if new value was set and it is different from the old value + prop_updated?(prop_name) && updated_properties[prop_name] != send(prop_name) + end + + def reset_updated_properties + @updated_properties = nil end def async_execute(data) diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index da87ea5b84f..d42b96294ba 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -116,14 +116,47 @@ describe Service do ) end - it "returns false" do + it "returns false when the property has not been assigned a new value" do service.username = "key_changed" expect(service.prop_updated?(:bamboo_url)).to be_falsy end - it "returns true" do - service.bamboo_url = "http://other.com" + it "returns true when the property has been assigned a different value" do + service.bamboo_url = "http://example.com" + expect(service.prop_updated?(:bamboo_url)).to be_truthy + end + + it "returns true when the property has been re-assigned the same value" do + service.bamboo_url = 'http://gitlab.com' expect(service.prop_updated?(:bamboo_url)).to be_truthy end end + + describe "#prop_modified?" do + let(:service) do + BambooService.create( + project: create(:project), + properties: { + bamboo_url: 'http://gitlab.com', + username: 'mic', + password: "password" + } + ) + end + + it "returns false when the property has not been assigned a new value" do + service.username = "key_changed" + expect(service.prop_modified?(:bamboo_url)).to be_falsy + end + + it "returns true when the property has been assigned a different value" do + service.bamboo_url = "http://example.com" + expect(service.prop_modified?(:bamboo_url)).to be_truthy + end + + it "returns false when the property has been re-assigned the same value" do + service.bamboo_url = 'http://gitlab.com' + expect(service.prop_modified?(:bamboo_url)).to be_falsy + end + end end From 2d0fcb4de23ab368e2e030b3cf7f6b1705ef676f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 14 Oct 2015 17:26:24 +0200 Subject: [PATCH 117/119] Fix spinach tests introduced by 07101cfab61f28c6328efebea98f018ab8356cdd --- features/dashboard/new_project.feature | 10 +++++----- features/steps/dashboard/new_project.rb | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/features/dashboard/new_project.feature b/features/dashboard/new_project.feature index bbd82a85e3a..76392068357 100644 --- a/features/dashboard/new_project.feature +++ b/features/dashboard/new_project.feature @@ -7,24 +7,24 @@ Background: And I click "New project" link @javascript - Scenario: I should see New projects page - Then I see "New project" page + Scenario: I should see New Projects page + Then I see "New Project" page Then I see all possible import optios @javascript Scenario: I should see instructions on how to import from Git URL - Given I see "New project" page + Given I see "New Project" page When I click on "Any repo by URL" Then I see instructions on how to import from Git URL @javascript Scenario: I should see instructions on how to import from GitHub - Given I see "New project" page + Given I see "New Project" page When I click on "Import project from GitHub" Then I see instructions on how to import from GitHub @javascript Scenario: I should see Google Code import page - Given I see "New project" page + Given I see "New Project" page When I click on "Google Code" Then I redirected to Google Code import page diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 1e09162a5b5..44a4aa9844a 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -3,13 +3,13 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps include SharedPaths include SharedProject - step 'I click "New project" link' do + step 'I click "New Project" link' do page.within('.content') do - click_link "New project" + click_link "New Project" end end - step 'I see "New project" page' do + step 'I see "New Project" page' do expect(page).to have_content('Project path') end From 8f584d5f2c8dc036214a7fc71ce864fe23b4cb9e Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 6 Oct 2015 17:09:03 +0300 Subject: [PATCH 118/119] Fix: Images cannot show when projects' path was changed --- CHANGELOG | 1 + app/models/namespace.rb | 2 + app/models/project.rb | 2 + app/services/projects/transfer_service.rb | 4 + app/uploaders/file_uploader.rb | 2 +- features/steps/admin/projects.rb | 2 + lib/gitlab/markdown.rb | 2 + lib/gitlab/markdown/upload_link_filter.rb | 47 ++++++++++++ lib/gitlab/uploads_transfer.rb | 35 +++++++++ public/uploads/.gitkeep | 0 .../projects/uploads_controller_spec.rb | 4 +- .../gitlab/email/attachment_uploader_spec.rb | 1 - .../markdown/upload_link_filter_spec.rb | 75 +++++++++++++++++++ spec/lib/gitlab/uploads_transfer_spec.rb | 50 +++++++++++++ .../projects/download_service_spec.rb | 2 - .../projects/transfer_service_spec.rb | 2 + spec/services/projects/upload_service_spec.rb | 4 - 17 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 lib/gitlab/markdown/upload_link_filter.rb create mode 100644 lib/gitlab/uploads_transfer.rb delete mode 100644 public/uploads/.gitkeep create mode 100644 spec/lib/gitlab/markdown/upload_link_filter_spec.rb create mode 100644 spec/lib/gitlab/uploads_transfer_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 31feb115d1e..87baa6b6621 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -53,6 +53,7 @@ v 8.1.0 (unreleased) - Add "New Page" button to Wiki Pages tab (Stan Hu) - Only render 404 page from /public - Hide passwords from services API (Alex Lossent) + - Fix: Images cannot show when projects' path was changed v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) diff --git a/app/models/namespace.rb b/app/models/namespace.rb index bc8525df5a5..5782e649f8b 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -118,6 +118,8 @@ class Namespace < ActiveRecord::Base gitlab_shell.add_namespace(path_was) if gitlab_shell.mv_namespace(path_was, path) + Gitlab::UploadsTransfer.new.rename_namespace(path_was, path) + # If repositories moved successfully we need to # send update instructions to users. # However we cannot allow rollback since we moved namespace dir diff --git a/app/models/project.rb b/app/models/project.rb index 021920008ad..cd30467fae3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -656,6 +656,8 @@ class Project < ActiveRecord::Base # db changes in order to prevent out of sync between db and fs raise Exception.new('repository cannot be renamed') end + + Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) end def hook_attrs diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index c327c244f0d..64ea6dd42eb 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -27,6 +27,7 @@ module Projects def transfer(project, new_namespace) Project.transaction do old_path = project.path_with_namespace + old_namespace = project.namespace new_path = File.join(new_namespace.try(:path) || '', project.path) if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? @@ -51,6 +52,9 @@ module Projects # clear project cached events project.reset_events_cache + # Move uploads + Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) + true end end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index f9673abbfe8..e8211585834 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -26,7 +26,7 @@ class FileUploader < CarrierWave::Uploader::Base end def secure_url - File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename) + File.join("/uploads", @secret, file.filename) end def file_storage? diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb index 17233f89f38..5a1cc9aa151 100644 --- a/features/steps/admin/projects.rb +++ b/features/steps/admin/projects.rb @@ -41,6 +41,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps end step 'I transfer project to group \'Web\'' do + allow_any_instance_of(Projects::TransferService). + to receive(:move_uploads_to_new_namespace).and_return(true) find(:xpath, "//input[@id='new_namespace_id']").set group.id click_button 'Transfer' end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index ae5f2544691..32a368c2e2b 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -48,6 +48,7 @@ module Gitlab autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter' autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' + autoload :UploadLinkFilter, 'gitlab/markdown/upload_link_filter' # Public: Parse the provided text with GitLab-Flavored Markdown # @@ -140,6 +141,7 @@ module Gitlab Gitlab::Markdown::SyntaxHighlightFilter, Gitlab::Markdown::SanitizationFilter, + Gitlab::Markdown::UploadLinkFilter, Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, diff --git a/lib/gitlab/markdown/upload_link_filter.rb b/lib/gitlab/markdown/upload_link_filter.rb new file mode 100644 index 00000000000..fbada73ab86 --- /dev/null +++ b/lib/gitlab/markdown/upload_link_filter.rb @@ -0,0 +1,47 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' +require 'uri' + +module Gitlab + module Markdown + # HTML filter that "fixes" relative upload links to files. + # Context options: + # :project (required) - Current project + # + class UploadLinkFilter < HTML::Pipeline::Filter + def call + doc.search('a').each do |el| + process_link_attr el.attribute('href') + end + + doc.search('img').each do |el| + process_link_attr el.attribute('src') + end + + doc + end + + protected + + def process_link_attr(html_attr) + return if html_attr.blank? + + uri = html_attr.value + if uri.starts_with?("/uploads/") + html_attr.value = build_url(uri).to_s + end + end + + def build_url(uri) + File.join(Gitlab.config.gitlab.url, context[:project].path_with_namespace, uri) + end + + # Ensure that a :project key exists in context + # + # Note that while the key might exist, its value could be nil! + def validate + needs :project + end + end + end +end diff --git a/lib/gitlab/uploads_transfer.rb b/lib/gitlab/uploads_transfer.rb new file mode 100644 index 00000000000..be8fcc7b2d2 --- /dev/null +++ b/lib/gitlab/uploads_transfer.rb @@ -0,0 +1,35 @@ +module Gitlab + class UploadsTransfer + def move_project(project_path, namespace_path_was, namespace_path) + new_namespace_folder = File.join(root_dir, namespace_path) + FileUtils.mkdir_p(new_namespace_folder) unless Dir.exist?(new_namespace_folder) + from = File.join(root_dir, namespace_path_was, project_path) + to = File.join(root_dir, namespace_path, project_path) + move(from, to, "") + end + + def rename_project(path_was, path, namespace_path) + base_dir = File.join(root_dir, namespace_path) + move(path_was, path, base_dir) + end + + def rename_namespace(path_was, path) + move(path_was, path) + end + + private + + def move(path_was, path, base_dir = nil) + base_dir = root_dir unless base_dir + from = File.join(base_dir, path_was) + to = File.join(base_dir, path) + FileUtils.mv(from, to) + rescue Errno::ENOENT + false + end + + def root_dir + File.join(Rails.root, "public", "uploads") + end + end +end diff --git a/public/uploads/.gitkeep b/public/uploads/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb index f51abfedae5..93c4494c660 100644 --- a/spec/controllers/projects/uploads_controller_spec.rb +++ b/spec/controllers/projects/uploads_controller_spec.rb @@ -33,7 +33,7 @@ describe Projects::UploadsController do it 'returns a content with original filename, new link, and correct type.' do expect(response.body).to match '\"alt\":\"rails_sample\"' - expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match "\"url\":\"/uploads" expect(response.body).to match '\"is_image\":true' end end @@ -49,7 +49,7 @@ describe Projects::UploadsController do it 'returns a content with original filename, new link, and correct type.' do expect(response.body).to match '\"alt\":\"doc_sample.txt\"' - expect(response.body).to match "\"url\":\"http://localhost/#{project.path_with_namespace}/uploads" + expect(response.body).to match "\"url\":\"/uploads" expect(response.body).to match '\"is_image\":false' end end diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index e8208e15e29..8fb432367b6 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -13,7 +13,6 @@ describe Gitlab::Email::AttachmentUploader do expect(link).not_to be_nil expect(link[:is_image]).to be_truthy expect(link[:alt]).to eq("bricks") - expect(link[:url]).to include("/#{project.path_with_namespace}") expect(link[:url]).to include("bricks.png") end end diff --git a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb new file mode 100644 index 00000000000..9ae45a6f559 --- /dev/null +++ b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb @@ -0,0 +1,75 @@ +# encoding: UTF-8 + +require 'spec_helper' + +module Gitlab::Markdown + describe UploadLinkFilter do + def filter(doc, contexts = {}) + contexts.reverse_merge!({ + project: project + }) + + described_class.call(doc, contexts) + end + + def image(path) + %() + end + + def link(path) + %(#{path}) + end + + let(:project) { create(:project) } + + shared_examples :preserve_unchanged do + it 'does not modify any relative URL in anchor' do + doc = filter(link('README.md')) + expect(doc.at_css('a')['href']).to eq 'README.md' + end + + it 'does not modify any relative URL in image' do + doc = filter(image('files/images/logo-black.png')) + expect(doc.at_css('img')['src']).to eq 'files/images/logo-black.png' + end + end + + it 'does not raise an exception on invalid URIs' do + act = link("://foo") + expect { filter(act) }.not_to raise_error + end + + context 'with a valid repository' do + it 'rebuilds relative URL for a link' do + doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('a')['href']). + to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'rebuilds relative URL for an image' do + doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')) + expect(doc.at_css('a')['href']). + to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" + end + + it 'does not modify absolute URL' do + doc = filter(link('http://example.com')) + expect(doc.at_css('a')['href']).to eq 'http://example.com' + end + + it 'supports Unicode filenames' do + path = '/uploads/한글.png' + escaped = Addressable::URI.escape(path) + + # Stub these methods so the file doesn't actually need to be in the repo + allow_any_instance_of(described_class). + to receive(:file_exists?).and_return(true) + allow_any_instance_of(described_class). + to receive(:image?).with(path).and_return(true) + + doc = filter(image(escaped)) + expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png" + end + end + end +end diff --git a/spec/lib/gitlab/uploads_transfer_spec.rb b/spec/lib/gitlab/uploads_transfer_spec.rb new file mode 100644 index 00000000000..260364a513e --- /dev/null +++ b/spec/lib/gitlab/uploads_transfer_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Gitlab::UploadsTransfer do + before do + @root_dir = File.join(Rails.root, "public", "uploads") + @upload_transfer = Gitlab::UploadsTransfer.new + + @project_path_was = "test_project_was" + @project_path = "test_project" + @namespace_path_was = "test_namespace_was" + @namespace_path = "test_namespace" + end + + after do + FileUtils.rm_rf([ + File.join(@root_dir, @namespace_path), + File.join(@root_dir, @namespace_path_was) + ]) + end + + describe '#move_project' do + it "moves project upload to another namespace" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + @upload_transfer.move_project(@project_path, @namespace_path_was, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + describe '#rename_project' do + it "renames project" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path, @project_path_was)) + @upload_transfer.rename_project(@project_path_was, @project_path, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end + + describe '#rename_namespace' do + it "renames namespace" do + FileUtils.mkdir_p(File.join(@root_dir, @namespace_path_was, @project_path)) + @upload_transfer.rename_namespace(@namespace_path_was, @namespace_path) + + expected_path = File.join(@root_dir, @namespace_path, @project_path) + expect(Dir.exist?(expected_path)).to be_truthy + end + end +end diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index f12e09c58c3..ddee2e62dfc 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -37,7 +37,6 @@ describe Projects::DownloadService do it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file['is_image']).to be true } - it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file['url']).to match('rails_sample.jpg') } it { expect(@link_to_file['alt']).to eq('rails_sample') } end @@ -52,7 +51,6 @@ describe Projects::DownloadService do it { expect(@link_to_file).to have_key('url') } it { expect(@link_to_file).to have_key('is_image') } it { expect(@link_to_file['is_image']).to be false } - it { expect(@link_to_file['url']).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file['url']).to match('doc_sample.txt') } it { expect(@link_to_file['alt']).to eq('doc_sample.txt') } end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index bb7da33b12e..47755bfc990 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -7,6 +7,8 @@ describe Projects::TransferService do context 'namespace -> namespace' do before do + allow_any_instance_of(Gitlab::UploadsTransfer). + to receive(:move_project).and_return(true) group.add_owner(user) @result = transfer_project(project, user, group) end diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb index fa4ff6b01ad..1b1a80d1fe7 100644 --- a/spec/services/projects/upload_service_spec.rb +++ b/spec/services/projects/upload_service_spec.rb @@ -18,7 +18,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('banana_sample') } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('banana_sample.gif') } end @@ -34,7 +33,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_value('dk') } it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('dk.png') } end @@ -49,7 +47,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('rails_sample') } it { expect(@link_to_file[:is_image]).to equal(true) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('rails_sample.jpg') } end @@ -64,7 +61,6 @@ describe Projects::UploadService do it { expect(@link_to_file).to have_key(:is_image) } it { expect(@link_to_file).to have_value('doc_sample.txt') } it { expect(@link_to_file[:is_image]).to equal(false) } - it { expect(@link_to_file[:url]).to match("/#{@project.path_with_namespace}") } it { expect(@link_to_file[:url]).to match('doc_sample.txt') } end From b83a18a55cd03cfa6d0d631b8d4567ae29b5fe26 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 14 Oct 2015 19:21:27 +0300 Subject: [PATCH 119/119] Revert "Improve invalidation of stored service password if the endpoint URL is changed" This reverts commit b46397548056e4e8ef00efe4f641c61ba1dd5230. --- app/controllers/admin/services_controller.rb | 8 +--- .../projects/services_controller.rb | 8 +--- app/models/project_services/bamboo_service.rb | 2 +- .../project_services/teamcity_service.rb | 2 +- app/models/service.rb | 32 +++------------ spec/models/service_spec.rb | 39 ++----------------- 6 files changed, 13 insertions(+), 78 deletions(-) diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index 46133588332..a62170662e1 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -39,13 +39,7 @@ class Admin::ServicesController < Admin::ApplicationController end def application_services_params - application_services_params = params.permit(:id, + params.permit(:id, service: Projects::ServicesController::ALLOWED_PARAMS) - if application_services_params[:service].is_a?(Hash) - Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param| - application_services_params[:service].delete(param) if application_services_params[:service][param].blank? - end - end - application_services_params end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 129068ef019..3047ee8a1ff 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -9,10 +9,6 @@ class Projects::ServicesController < Projects::ApplicationController :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification] - - # Parameters to ignore if no value is specified - FILTER_BLANK_PARAMS = [:password] - # Authorize before_action :authorize_admin_project! before_action :service, only: [:edit, :update, :test] @@ -63,9 +59,7 @@ class Projects::ServicesController < Projects::ApplicationController def service_params service_params = params.require(:service).permit(ALLOWED_PARAMS) - FILTER_BLANK_PARAMS.each do |param| - service_params.delete(param) if service_params[param].blank? - end + service_params.delete("password") if service_params["password"].blank? service_params end end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 4a18c772e50..5f5255ab487 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -48,7 +48,7 @@ class BambooService < CiService end def reset_password - if prop_modified?(:bamboo_url) && !prop_updated?(:password) + if prop_updated?(:bamboo_url) self.password = nil end end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index a548a557dfe..fb11cad352e 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -45,7 +45,7 @@ class TeamcityService < CiService end def reset_password - if prop_modified?(:teamcity_url) && !prop_updated?(:password) + if prop_updated?(:teamcity_url) self.password = nil end end diff --git a/app/models/service.rb b/app/models/service.rb index 946ea1a096b..7e845d565b1 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -33,8 +33,6 @@ class Service < ActiveRecord::Base after_initialize :initialize_properties - after_commit :reset_updated_properties - belongs_to :project has_one :service_hook @@ -105,7 +103,6 @@ class Service < ActiveRecord::Base # Provide convenient accessor methods # for each serialized property. - # Also keep track of updated properties. def self.prop_accessor(*args) args.each do |arg| class_eval %{ @@ -114,36 +111,19 @@ class Service < ActiveRecord::Base end def #{arg}=(value) - updated_properties['#{arg}'] = #{arg} unless updated_properties.include?('#{arg}') self.properties['#{arg}'] = value end } end end - # Returns a hash of the properties that have been assigned a new value since last save, - # indicating their original values (attr => original value). - # ActiveRecord does not provide a mechanism to track changes in serialized keys, - # so we need a specific implementation for service properties. - # This allows to track changes to properties set with the accessor methods, - # but not direct manipulation of properties hash. - def updated_properties - @updated_properties ||= ActiveSupport::HashWithIndifferentAccess.new - end - + # ActiveRecord does not provide a mechanism to track changes in serialized keys. + # This is why we need to perform extra query to do it mannually. def prop_updated?(prop_name) - # Check if a new value was set. - # The new value may or may not be the same as the old value - updated_properties.include?(prop_name) - end - - def prop_modified?(prop_name) - # Check if new value was set and it is different from the old value - prop_updated?(prop_name) && updated_properties[prop_name] != send(prop_name) - end - - def reset_updated_properties - @updated_properties = nil + relation_name = self.type.underscore + previous_value = project.send(relation_name).send(prop_name) + return false if previous_value.nil? + previous_value != send(prop_name) end def async_execute(data) diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index d42b96294ba..da87ea5b84f 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -116,47 +116,14 @@ describe Service do ) end - it "returns false when the property has not been assigned a new value" do + it "returns false" do service.username = "key_changed" expect(service.prop_updated?(:bamboo_url)).to be_falsy end - it "returns true when the property has been assigned a different value" do - service.bamboo_url = "http://example.com" + it "returns true" do + service.bamboo_url = "http://other.com" expect(service.prop_updated?(:bamboo_url)).to be_truthy end - - it "returns true when the property has been re-assigned the same value" do - service.bamboo_url = 'http://gitlab.com' - expect(service.prop_updated?(:bamboo_url)).to be_truthy - end - end - - describe "#prop_modified?" do - let(:service) do - BambooService.create( - project: create(:project), - properties: { - bamboo_url: 'http://gitlab.com', - username: 'mic', - password: "password" - } - ) - end - - it "returns false when the property has not been assigned a new value" do - service.username = "key_changed" - expect(service.prop_modified?(:bamboo_url)).to be_falsy - end - - it "returns true when the property has been assigned a different value" do - service.bamboo_url = "http://example.com" - expect(service.prop_modified?(:bamboo_url)).to be_truthy - end - - it "returns false when the property has been re-assigned the same value" do - service.bamboo_url = 'http://gitlab.com' - expect(service.prop_modified?(:bamboo_url)).to be_falsy - end end end