From 7a8a892efdf59925a95cdf6504f7c74c31b87eeb Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 17:12:41 +0200 Subject: Add "rake gitlab:list_repos" task --- doc/raketasks/list_repos.md | 30 ++++++++++++++++++++++++++++++ lib/tasks/gitlab/list_repos.rake | 16 ++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 doc/raketasks/list_repos.md create mode 100644 lib/tasks/gitlab/list_repos.rake diff --git a/doc/raketasks/list_repos.md b/doc/raketasks/list_repos.md new file mode 100644 index 00000000000..476428eb4f5 --- /dev/null +++ b/doc/raketasks/list_repos.md @@ -0,0 +1,30 @@ +# Listing repository directories + +You can print a list of all Git repositories on disk managed by +GitLab with the following command: + +``` +# Omnibus +sudo gitlab-rake gitlab:list_repos + +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production +``` + +If you only want to list projects with recent activity you can pass +a date with the 'SINCE' environment variable. The time you specify +is parsed by the Rails [TimeZone#parse +function](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-i-parse). + +``` +# Omnibus +sudo gitlab-rake gitlab:list_repos SINCE='Sep 1 2015' + +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:list_repos RAILS_ENV=production SINCE='Sep 1 2015' +``` + +Note that the projects listed are NOT sorted by activity; they use +the default ordering of the GitLab Rails application. diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake new file mode 100644 index 00000000000..1377e1ea910 --- /dev/null +++ b/lib/tasks/gitlab/list_repos.rake @@ -0,0 +1,16 @@ +namespace :gitlab do + task list_repos: :environment do + scope = Project + if ENV['SINCE'] + date = Time.parse(ENV['SINCE']) + warn "Listing repositories with activity since #{date}" + project_ids = Project.where(['last_activity_at > ?', date]).pluck(:id) + scope = scope.where(id: project_ids) + end + scope.find_each do |project| + base = File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace) + puts base + '.git' + puts base + '.wiki.git' + end + end +end -- cgit v1.2.1 From 5bcd0efe3e0b1fef06147d87f843adac717d7c42 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 18:31:54 +0200 Subject: Add parallel-rsync-repos script and start docs --- bin/parallel-rsync-repos | 26 ++++++++++ doc/operations/rsyncing_repositories.md | 87 +++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 bin/parallel-rsync-repos create mode 100644 doc/operations/rsyncing_repositories.md diff --git a/bin/parallel-rsync-repos b/bin/parallel-rsync-repos new file mode 100644 index 00000000000..b2429f743b5 --- /dev/null +++ b/bin/parallel-rsync-repos @@ -0,0 +1,26 @@ +#!/bin/sh +# this script should run as the 'git' user, not root, because of mkdir +# +# Example invocation: +# find /var/opt/gitlab/git-data/repositories -maxdepth 2 | \ +# parallel-rsync-repos /var/opt/gitlab/git-data/repositories /mnt/gitlab/repositories + +SRC=$1 +DEST=$2 + +if [ -z "$JOBS" ] ; then + JOBS=10 +fi + +if [ -z "$SRC" ] || [ -z "$DEST" ] ; then + echo "Usage: $0 SRC DEST" + exit 1 +fi + +if ! cd $SRC ; then + echo "cd $SRC failed" + exit 1 +fi + +sed "s|$SRC|./|" |\ + parallel -j$JOBS --progress "mkdir -p $DEST/{} && rsync --delete -a {}/. $DEST/{}/" diff --git a/doc/operations/rsyncing_repositories.md b/doc/operations/rsyncing_repositories.md new file mode 100644 index 00000000000..231e09f0462 --- /dev/null +++ b/doc/operations/rsyncing_repositories.md @@ -0,0 +1,87 @@ +# Moving repositories managed by GitLab + +Sometimes you need to move all repositories managed by GitLab to +another filesystem or another server. In this document we will look +at some of the ways you can copy all your repositories from +`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`. + +We will look at three scenarios: the target directory is empty, the +target directory contains an outdated copy of the repositories, and +how to deal with thousands of repositories. + +**Each of the approaches we list can/will overwrite data in the +target directory `/mnt/gitlab/repositories`. Do not mix up the +source and the target.** + +## Target directory is empty: use a tar pipe + +If the target directory `/mnt/gitlab/repositories` is empty the +simplest thing to do is to use a tar pipe. + +``` +# As the git user +tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ + tar -C /mnt/gitlab/repositories -xf - +``` + +If you want to see progress, replace `-xf` with `-xvf`. + +### Tar pipe to another server + +You can also use a tar pipe to copy data to another server. If your +'git' user has SSH access to the newserver as 'git@newserver', you +can pipe the data through SSH. + +``` +# As the git user +tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ + ssh git@newserver tar -C /mnt/gitlab/repositories -xf - +``` + +If you want to compress the data before it goes over the network +(which will cost you CPU cycles) you can replace `ssh` with `ssh +-C`. + +## The target directory contains an outdated copy of the repositories: use rsync + +In this scenario it is better to use rsync. This utility is either +already installed on your system or easily installable via apt, yum +etc. + +``` +# As the 'git' user +rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ + /mnt/gitlab/repositories +``` + +The `/.` in the command above is very important, without it you can +easily get the wrong directory structure in the target directory. +If you want to see progress, replace `-a` with `-av`. + +### Single rsync to another server + +If the 'git' user on your source system has SSH access to the target +server you can send the repositories over the network with rsync. + +``` +# As the 'git' user +rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ + git@newserver:/mnt/gitlab/repositories +``` + +## Thousands of Git repositories: use one rsync per repository + +Every time you start an rsync job it has to inspect all files in +the source directory, all files in the target directory, and then +decide what files to copy or not. If the source or target directory +has many contents this startup phase of rsync can become a burden +for your GitLab server. In cases like this you can make rsync's +life easier by dividing its work in smaller pieces, and sync one +repository at a time. + +In addition to rsync we will use [GNU +Parallel](http://www.gnu.org/software/parallel/). This utility is +not included in GitLab so you need to install it yourself with apt +or yum. Also note that the GitLab scripts we used below were added +in GitLab 8.???. + -- cgit v1.2.1 From 9f3984b5e8fb261eb24be76ec548d83c43d58b96 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 18:32:51 +0200 Subject: Rename doc file --- doc/operations/moving_repositories.md | 87 +++++++++++++++++++++++++++++++++ doc/operations/rsyncing_repositories.md | 87 --------------------------------- 2 files changed, 87 insertions(+), 87 deletions(-) create mode 100644 doc/operations/moving_repositories.md delete mode 100644 doc/operations/rsyncing_repositories.md diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md new file mode 100644 index 00000000000..231e09f0462 --- /dev/null +++ b/doc/operations/moving_repositories.md @@ -0,0 +1,87 @@ +# Moving repositories managed by GitLab + +Sometimes you need to move all repositories managed by GitLab to +another filesystem or another server. In this document we will look +at some of the ways you can copy all your repositories from +`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`. + +We will look at three scenarios: the target directory is empty, the +target directory contains an outdated copy of the repositories, and +how to deal with thousands of repositories. + +**Each of the approaches we list can/will overwrite data in the +target directory `/mnt/gitlab/repositories`. Do not mix up the +source and the target.** + +## Target directory is empty: use a tar pipe + +If the target directory `/mnt/gitlab/repositories` is empty the +simplest thing to do is to use a tar pipe. + +``` +# As the git user +tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ + tar -C /mnt/gitlab/repositories -xf - +``` + +If you want to see progress, replace `-xf` with `-xvf`. + +### Tar pipe to another server + +You can also use a tar pipe to copy data to another server. If your +'git' user has SSH access to the newserver as 'git@newserver', you +can pipe the data through SSH. + +``` +# As the git user +tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ + ssh git@newserver tar -C /mnt/gitlab/repositories -xf - +``` + +If you want to compress the data before it goes over the network +(which will cost you CPU cycles) you can replace `ssh` with `ssh +-C`. + +## The target directory contains an outdated copy of the repositories: use rsync + +In this scenario it is better to use rsync. This utility is either +already installed on your system or easily installable via apt, yum +etc. + +``` +# As the 'git' user +rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ + /mnt/gitlab/repositories +``` + +The `/.` in the command above is very important, without it you can +easily get the wrong directory structure in the target directory. +If you want to see progress, replace `-a` with `-av`. + +### Single rsync to another server + +If the 'git' user on your source system has SSH access to the target +server you can send the repositories over the network with rsync. + +``` +# As the 'git' user +rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ + git@newserver:/mnt/gitlab/repositories +``` + +## Thousands of Git repositories: use one rsync per repository + +Every time you start an rsync job it has to inspect all files in +the source directory, all files in the target directory, and then +decide what files to copy or not. If the source or target directory +has many contents this startup phase of rsync can become a burden +for your GitLab server. In cases like this you can make rsync's +life easier by dividing its work in smaller pieces, and sync one +repository at a time. + +In addition to rsync we will use [GNU +Parallel](http://www.gnu.org/software/parallel/). This utility is +not included in GitLab so you need to install it yourself with apt +or yum. Also note that the GitLab scripts we used below were added +in GitLab 8.???. + diff --git a/doc/operations/rsyncing_repositories.md b/doc/operations/rsyncing_repositories.md deleted file mode 100644 index 231e09f0462..00000000000 --- a/doc/operations/rsyncing_repositories.md +++ /dev/null @@ -1,87 +0,0 @@ -# Moving repositories managed by GitLab - -Sometimes you need to move all repositories managed by GitLab to -another filesystem or another server. In this document we will look -at some of the ways you can copy all your repositories from -`/var/opt/gitlab/git-data/repositories` to `/mnt/gitlab/repositories`. - -We will look at three scenarios: the target directory is empty, the -target directory contains an outdated copy of the repositories, and -how to deal with thousands of repositories. - -**Each of the approaches we list can/will overwrite data in the -target directory `/mnt/gitlab/repositories`. Do not mix up the -source and the target.** - -## Target directory is empty: use a tar pipe - -If the target directory `/mnt/gitlab/repositories` is empty the -simplest thing to do is to use a tar pipe. - -``` -# As the git user -tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ - tar -C /mnt/gitlab/repositories -xf - -``` - -If you want to see progress, replace `-xf` with `-xvf`. - -### Tar pipe to another server - -You can also use a tar pipe to copy data to another server. If your -'git' user has SSH access to the newserver as 'git@newserver', you -can pipe the data through SSH. - -``` -# As the git user -tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ - ssh git@newserver tar -C /mnt/gitlab/repositories -xf - -``` - -If you want to compress the data before it goes over the network -(which will cost you CPU cycles) you can replace `ssh` with `ssh --C`. - -## The target directory contains an outdated copy of the repositories: use rsync - -In this scenario it is better to use rsync. This utility is either -already installed on your system or easily installable via apt, yum -etc. - -``` -# As the 'git' user -rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ - /mnt/gitlab/repositories -``` - -The `/.` in the command above is very important, without it you can -easily get the wrong directory structure in the target directory. -If you want to see progress, replace `-a` with `-av`. - -### Single rsync to another server - -If the 'git' user on your source system has SSH access to the target -server you can send the repositories over the network with rsync. - -``` -# As the 'git' user -rsync -a --delete /var/opt/gitlab/git-data/repositories/. \ - git@newserver:/mnt/gitlab/repositories -``` - -## Thousands of Git repositories: use one rsync per repository - -Every time you start an rsync job it has to inspect all files in -the source directory, all files in the target directory, and then -decide what files to copy or not. If the source or target directory -has many contents this startup phase of rsync can become a burden -for your GitLab server. In cases like this you can make rsync's -life easier by dividing its work in smaller pieces, and sync one -repository at a time. - -In addition to rsync we will use [GNU -Parallel](http://www.gnu.org/software/parallel/). This utility is -not included in GitLab so you need to install it yourself with apt -or yum. Also note that the GitLab scripts we used below were added -in GitLab 8.???. - -- cgit v1.2.1 From 4dd7c2f1e0174f8de6be9c57f7296e64e1534af5 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 25 Sep 2015 18:35:41 +0200 Subject: Remove unwanted linebreak --- doc/operations/moving_repositories.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md index 231e09f0462..88b90e91316 100644 --- a/doc/operations/moving_repositories.md +++ b/doc/operations/moving_repositories.md @@ -39,8 +39,7 @@ tar -C /var/opt/gitlab/git-data/repositories -cf - -- . |\ ``` If you want to compress the data before it goes over the network -(which will cost you CPU cycles) you can replace `ssh` with `ssh --C`. +(which will cost you CPU cycles) you can replace `ssh` with `ssh -C`. ## The target directory contains an outdated copy of the repositories: use rsync -- cgit v1.2.1 From 3ef71fa4fbea9a5ecc3c094df39cf4ea2f97dc90 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 20:53:16 +0200 Subject: Add support for HiDPI displays in gravatar service --- app/services/gravatar_service.rb | 4 ++-- spec/helpers/application_helper_spec.rb | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb index 4bee0c26a68..433ecc2df32 100644 --- a/app/services/gravatar_service.rb +++ b/app/services/gravatar_service.rb @@ -1,13 +1,13 @@ class GravatarService include Gitlab::CurrentSettings - def execute(email, size = nil) + def execute(email, size = nil, scale = 2) if current_application_settings.gravatar_enabled? && email.present? size = 40 if size.nil? || size <= 0 sprintf gravatar_url, hash: Digest::MD5.hexdigest(email.strip.downcase), - size: size, + size: size * scale, email: email.strip end end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 742420f550e..e6e9bc99a16 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -141,15 +141,19 @@ describe ApplicationHelper do stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}') expect(gravatar_icon(user_email, 20)). - to eq('http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118') + to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118') end it 'accepts a custom size argument' do - expect(helper.gravatar_icon(user_email, 64)).to include '?s=64' + expect(helper.gravatar_icon(user_email, 64)).to include '?s=128' end - it 'defaults size to 40 when given an invalid size' do - expect(helper.gravatar_icon(user_email, nil)).to include '?s=40' + it 'defaults size to 40@2x when given an invalid size' do + expect(helper.gravatar_icon(user_email, nil)).to include '?s=80' + end + + it 'accepts a scaling factor' do + expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120' end it 'ignores case and surrounding whitespace' do -- cgit v1.2.1 From f93177c6a3bee4c02f6a9a18278db13dd1c30c35 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 21:25:04 +0200 Subject: Add scale parameter to helper --- app/helpers/application_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 39ab83ccf12..162dae1ed32 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -78,8 +78,8 @@ module ApplicationHelper end end - def gravatar_icon(user_email = '', size = nil) - GravatarService.new.execute(user_email, size) || + def gravatar_icon(user_email = '', size = nil, scale = 2) + GravatarService.new.execute(user_email, size, scale) || default_avatar end -- cgit v1.2.1 From 8d620e39c967739f99bd175449864524f05b915c Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sat, 26 Sep 2015 21:51:02 +0200 Subject: Add scale argument in user class --- app/helpers/application_helper.rb | 4 ++-- app/models/user.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 162dae1ed32..056ffd278f6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,13 +68,13 @@ module ApplicationHelper end end - def avatar_icon(user_email = '', size = nil) + def avatar_icon(user_email = '', size = nil, scale = 2) user = User.find_by(email: user_email) if user user.avatar_url(size) || default_avatar else - gravatar_icon(user_email, size) + gravatar_icon(user_email, size, scale) end end diff --git a/app/models/user.rb b/app/models/user.rb index 25371f9138a..55c095d7d57 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -633,11 +633,11 @@ class User < ActiveRecord::Base email.start_with?('temp-email-for-oauth') end - def avatar_url(size = nil) + def avatar_url(size = nil, scale = 2) if avatar.present? [gitlab_config.url, avatar.url].join else - GravatarService.new.execute(email, size) + GravatarService.new.execute(email, size, scale) end end -- cgit v1.2.1 From 16f8ca566b8637dc8092a6b630c23a82a905b437 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 1 Oct 2015 23:40:29 -0400 Subject: Add custom protocol whitelisting to SanitizationFilter Addresses internal https://dev.gitlab.org/gitlab/gitlabhq/issues/2613 --- lib/gitlab/markdown/sanitization_filter.rb | 19 ++++ .../gitlab/markdown/sanitization_filter_spec.rb | 101 +++++++++++++++++++-- 2 files changed, 110 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb index e368de7d848..ffb9dc33b64 100644 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ b/lib/gitlab/markdown/sanitization_filter.rb @@ -48,6 +48,12 @@ module Gitlab # Allow span elements whitelist[:elements].push('span') + # Allow any protocol in `a` elements... + whitelist[:protocols].delete('a') + + # ...but then remove links with the `javascript` protocol + whitelist[:transformers].push(remove_javascript_links) + # Remove `rel` attribute from `a` elements whitelist[:transformers].push(remove_rel) @@ -57,6 +63,19 @@ module Gitlab whitelist end + def remove_javascript_links + lambda do |env| + node = env[:node] + + return unless node.name == 'a' + return unless node.has_attribute?('href') + + if node['href'].start_with?('javascript', ':javascript') + node.remove_attribute('href') + end + end + end + def remove_rel lambda do |env| if env[:node_name] == 'a' diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb index e50c82d0b3c..27cd00e8054 100644 --- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb +++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb @@ -44,7 +44,7 @@ module Gitlab::Markdown instance = described_class.new('Foo') 3.times { instance.whitelist } - expect(instance.whitelist[:transformers].size).to eq 4 + expect(instance.whitelist[:transformers].size).to eq 5 end it 'allows syntax highlighting' do @@ -77,19 +77,100 @@ module Gitlab::Markdown end it 'removes `rel` attribute from `a` elements' do - doc = filter(%q{Link}) + act = %q{Link} + exp = %q{Link} - expect(doc.css('a').size).to eq 1 - expect(doc.at_css('a')['href']).to eq '#' - expect(doc.at_css('a')['rel']).to be_nil + expect(filter(act).to_html).to eq exp end - it 'removes script-like `href` attribute from `a` elements' do - html = %q{Hi} - doc = filter(html) + # Adapted from the Sanitize test suite: http://git.io/vczrM + protocols = { + 'protocol-based JS injection: simple, no spaces' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces before' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces after' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces before and after' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: preceding colon' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: UTF-8 encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long UTF-8 encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long UTF-8 encoding without semicolons' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: hex encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long hex encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: hex encoding without semicolons' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: null char' => { + input: "foo", + output: '' + }, + + 'protocol-based JS injection: spaces and entities' => { + input: 'foo', + output: 'foo' + }, + } + + protocols.each do |name, data| + it "handles #{name}" do + doc = filter(data[:input]) + + expect(doc.to_html).to eq data[:output] + end + end + + it 'allows non-standard anchor schemes' do + exp = %q{IRC} + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'allows relative links' do + exp = %q{foo/bar.md} + act = filter(exp) - expect(doc.css('a').size).to eq 1 - expect(doc.at_css('a')['href']).to be_nil + expect(act.to_html).to eq exp end end -- cgit v1.2.1 From 6479b821ebc04d9e2ec69be451768968c06ce6a5 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 5 Oct 2015 18:02:12 +0200 Subject: Add RSYNC variable to parallel-rsync-repos --- bin/parallel-rsync-repos | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) mode change 100644 => 100755 bin/parallel-rsync-repos diff --git a/bin/parallel-rsync-repos b/bin/parallel-rsync-repos old mode 100644 new mode 100755 index b2429f743b5..b777056c95f --- a/bin/parallel-rsync-repos +++ b/bin/parallel-rsync-repos @@ -4,6 +4,15 @@ # Example invocation: # find /var/opt/gitlab/git-data/repositories -maxdepth 2 | \ # parallel-rsync-repos /var/opt/gitlab/git-data/repositories /mnt/gitlab/repositories +# +# You can also rsync to a remote destination. +# +# parallel-rsync-repos /var/opt/gitlab/git-data/repositories user@host:/mnt/gitlab/repositories +# +# If you need to pass extra options to rsync, set the RSYNC variable +# +# env RSYNC='rsync --rsh="foo bar"' parallel-rsync-repos /src dest +# SRC=$1 DEST=$2 @@ -17,10 +26,14 @@ if [ -z "$SRC" ] || [ -z "$DEST" ] ; then exit 1 fi +if [ -z "$RSYNC" ] ; then + RSYNC=rsync +fi + if ! cd $SRC ; then echo "cd $SRC failed" exit 1 fi sed "s|$SRC|./|" |\ - parallel -j$JOBS --progress "mkdir -p $DEST/{} && rsync --delete -a {}/. $DEST/{}/" + parallel -j$JOBS --progress "mkdir -p $DEST/{} && $RSYNC --delete -a {}/. $DEST/{}/" -- cgit v1.2.1 From e0ef09d9a35bf001acbb89e4177d942f6db93e50 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 5 Oct 2015 18:02:32 +0200 Subject: Some more text in the doc --- doc/operations/moving_repositories.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md index 88b90e91316..d156f3ad777 100644 --- a/doc/operations/moving_repositories.md +++ b/doc/operations/moving_repositories.md @@ -16,7 +16,10 @@ source and the target.** ## Target directory is empty: use a tar pipe If the target directory `/mnt/gitlab/repositories` is empty the -simplest thing to do is to use a tar pipe. +simplest thing to do is to use a tar pipe. This method has low +overhead and tar is almost always already installed on your system. +However, it is not possible to resume an interrupted tar pipe: if +that happens then all data must be copied again. ``` # As the git user @@ -43,9 +46,11 @@ If you want to compress the data before it goes over the network ## The target directory contains an outdated copy of the repositories: use rsync -In this scenario it is better to use rsync. This utility is either -already installed on your system or easily installable via apt, yum -etc. +If the target directory already contains a partial / outdated copy +of the repositories it may be wasteful to copy all the data again +with tar. In this scenario it is better to use rsync. This utility +is either already installed on your system or easily installable +via apt, yum etc. ``` # As the 'git' user -- cgit v1.2.1 From ad37f58ebd97fee3c06a531e4067c8adb4c9ecc7 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 12:42:05 +0200 Subject: Add explanation about parallel-rsync-repos --- doc/operations/moving_repositories.md | 55 ++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md index d156f3ad777..a89602b367f 100644 --- a/doc/operations/moving_repositories.md +++ b/doc/operations/moving_repositories.md @@ -87,5 +87,58 @@ In addition to rsync we will use [GNU Parallel](http://www.gnu.org/software/parallel/). This utility is not included in GitLab so you need to install it yourself with apt or yum. Also note that the GitLab scripts we used below were added -in GitLab 8.???. +in GitLab 8.1. +** This process does not clean up repositories at the target location that no +longer exist at the source. ** If you start using your GitLab instance with +`/mnt/gitlab/repositories`, you need to run `gitlab-rake gitlab:cleanup:repos` +after switching to the new repository storage directory. + +### Parallel rsync for all repositories known to GitLab + +This will sync repositories with 10 rsync processes at a time. + +``` +# Omnibus +sudo gitlab-rake gitlab:list_repos |\ + sudo -u git \ + /usr/bin/env JOBS=10 \ + /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repoos \ + /var/opt/gitlab/git-data/repositories \ + /mnt/gitlab/repositories + +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:list_repos |\ + sudo -u git -H \ + /usr/bin/env JOBS=10 \ + bin/parallel-rsync-repos \ + /home/git/repositories \ + /mnt/gitlab/repositories +``` + +### Parallel rsync only for repositories with recent activity + +Suppose you have already done one sync that started after 2015-10-1 12:00 UTC. +Then you might only want to sync repositories that were changed via GitLab +_after_ that time. You can use the 'SINCE' variable to tell 'rake +gitlab:list_repos' to only print repositories with recent activity. + +``` +# Omnibus +sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\ + sudo -u git \ + /usr/bin/env JOBS=10 \ + /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repoos \ + /var/opt/gitlab/git-data/repositories \ + /mnt/gitlab/repositories + +# Source +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\ + sudo -u git -H \ + /usr/bin/env JOBS=10 \ + bin/parallel-rsync-repos \ + /home/git/repositories \ + /mnt/gitlab/repositories +``` -- cgit v1.2.1 From d3734fbd89c069d35856b440f12109af8a7ef9c9 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 14:43:19 +0200 Subject: Use tar for intermediate backup storage During the backup we create an intermediate copy of two directories: builds and uploads. Instead of creating many small files with 'cp -r', we now use tar (and fast gzip) to create single intermediate files. This saves on disk IO and disk space while creating a backup. --- lib/backup/builds.rb | 31 ++----------------------------- lib/backup/files.rb | 39 +++++++++++++++++++++++++++++++++++++++ lib/backup/manager.rb | 4 ++-- lib/backup/uploads.rb | 30 ++---------------------------- 4 files changed, 45 insertions(+), 59 deletions(-) create mode 100644 lib/backup/files.rb diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index 6f56f680bb9..d269f8e260c 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -1,34 +1,7 @@ module Backup - class Builds - attr_reader :app_builds_dir, :backup_builds_dir, :backup_dir - + class Builds < Files def initialize - @app_builds_dir = Settings.gitlab_ci.builds_path - @backup_dir = Gitlab.config.backup.path - @backup_builds_dir = File.join(Gitlab.config.backup.path, 'builds') - end - - # Copy builds from builds directory to backup/builds - def dump - FileUtils.rm_rf(backup_builds_dir) - # Ensure the parent dir of backup_builds_dir exists - FileUtils.mkdir_p(Gitlab.config.backup.path) - # Fail if somebody raced to create backup_builds_dir before us - FileUtils.mkdir(backup_builds_dir, mode: 0700) - FileUtils.cp_r(app_builds_dir, backup_dir) - end - - def restore - backup_existing_builds_dir - - FileUtils.cp_r(backup_builds_dir, app_builds_dir) - end - - def backup_existing_builds_dir - timestamped_builds_path = File.join(app_builds_dir, '..', "builds.#{Time.now.to_i}") - if File.exists?(app_builds_dir) - FileUtils.mv(app_builds_dir, File.expand_path(timestamped_builds_path)) - end + super(Settings.gitlab_ci.builds_path) end end end diff --git a/lib/backup/files.rb b/lib/backup/files.rb new file mode 100644 index 00000000000..d0a6e8f27be --- /dev/null +++ b/lib/backup/files.rb @@ -0,0 +1,39 @@ +require 'open3' + +module Backup + class Files + attr_reader :name, :app_files_dir, :backup_tarball, :backup_dir, :files_parent_dir + + def initialize(app_files_dir) + @app_files_dir = File.realpath(app_files_dir) + @name = File.basename(app_files_dir) + @files_parent_dir = File.realpath(File.join(@app_files_dir, '..')) + @backup_dir = Gitlab.config.backup.path + @backup_tarball = File.join(@backup_dir, name + '.tar.gz') + end + + # Copy files from public/files to backup/files + def dump + FileUtils.mkdir_p(Gitlab.config.backup.path) + run_pipeline!([%W(tar -C #{files_parent_dir} -cf - #{name}), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + end + + def restore + backup_existing_files_dir + + run_pipeline!([%W(gzip -cd), %W(tar -C #{files_parent_dir} -xf -)], in: backup_tarball) + end + + def backup_existing_files_dir + timestamped_files_path = File.join(files_parent_dir, "#{name}.#{Time.now.to_i}") + if File.exists?(app_files_dir) + FileUtils.mv(app_files_dir, File.expand_path(timestamped_files_path)) + end + end + + def run_pipeline!(cmd_list, options={}) + status_list = Open3.pipeline(*cmd_list, options) + abort 'Backup failed' unless status_list.compact.all?(&:success?) + end + end +end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 5c42f25f4a2..f011fd03de0 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,11 +150,11 @@ module Backup private def backup_contents - folders_to_backup + ["backup_information.yml"] + folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] end def folders_to_backup - folders = %w{repositories db uploads builds} + folders = %w{repositories db} if ENV["SKIP"] return folders.reject{ |folder| ENV["SKIP"].include?(folder) } diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index 1f9626644e6..7c0838cc8b7 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -1,34 +1,8 @@ module Backup - class Uploads - attr_reader :app_uploads_dir, :backup_uploads_dir, :backup_dir + class Uploads < Files def initialize - @app_uploads_dir = File.realpath(Rails.root.join('public', 'uploads')) - @backup_dir = Gitlab.config.backup.path - @backup_uploads_dir = File.join(Gitlab.config.backup.path, 'uploads') - end - - # Copy uploads from public/uploads to backup/uploads - def dump - FileUtils.rm_rf(backup_uploads_dir) - # Ensure the parent dir of backup_uploads_dir exists - FileUtils.mkdir_p(Gitlab.config.backup.path) - # Fail if somebody raced to create backup_uploads_dir before us - FileUtils.mkdir(backup_uploads_dir, mode: 0700) - FileUtils.cp_r(app_uploads_dir, backup_dir) - end - - def restore - backup_existing_uploads_dir - - FileUtils.cp_r(backup_uploads_dir, app_uploads_dir) - end - - def backup_existing_uploads_dir - timestamped_uploads_path = File.join(app_uploads_dir, '..', "uploads.#{Time.now.to_i}") - if File.exists?(app_uploads_dir) - FileUtils.mv(app_uploads_dir, File.expand_path(timestamped_uploads_path)) - end + super(Rails.root.join('public/uploads')) end end end -- cgit v1.2.1 From 90ddf140b9390647002771572d0375da0bb9dfa4 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 15:06:15 +0200 Subject: Reduce disk IO during SQL backup By using light gzip compression we can save a lot of disk IO during the backup. --- lib/backup/database.rb | 53 +++++++++++++++++++++++--------------------------- lib/backup/manager.rb | 4 ++-- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/lib/backup/database.rb b/lib/backup/database.rb index 959ac4b7868..4bdf6e1c628 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -2,26 +2,27 @@ require 'yaml' module Backup class Database - attr_reader :config, :db_dir + attr_reader :config, :db_file_name def initialize @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] - @db_dir = File.join(Gitlab.config.backup.path, 'db') + @db_file_name = File.join(Gitlab.config.backup.path, 'database.sql.gz') end def dump - FileUtils.rm_rf(@db_dir) - # Ensure the parent dir of @db_dir exists + FileUtils.rm_f(db_file_name) + compress_rd, compress_wr = IO.pipe + compress_pid = spawn(*%W(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600]) + compress_rd.close + FileUtils.mkdir_p(Gitlab.config.backup.path) - # Fail if somebody raced to create @db_dir before us - FileUtils.mkdir(@db_dir, mode: 0700) - success = case config["adapter"] + dump_pid = case config["adapter"] when /^mysql/ then $progress.print "Dumping MySQL database #{config['database']} ... " # Workaround warnings from MySQL 5.6 about passwords on cmd line ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] - system('mysqldump', *mysql_args, config['database'], out: db_file_name) + spawn('mysqldump', *mysql_args, config['database'], out: compress_wr) when "postgresql" then $progress.print "Dumping PostgreSQL database #{config['database']} ... " pg_env @@ -30,48 +31,42 @@ module Backup pgsql_args << "-n" pgsql_args << Gitlab.config.backup.pg_schema end - system('pg_dump', *pgsql_args, config['database'], out: db_file_name) + spawn('pg_dump', *pgsql_args, config['database'], out: compress_wr) end - report_success(success) - abort 'Backup failed' unless success + compress_wr.close + + success = [compress_pid, dump_pid].all? { |pid| Process.waitpid(pid); $?.success? } - $progress.print 'Compressing database ... ' - success = system('gzip', db_file_name) report_success(success) - abort 'Backup failed: compress error' unless success + abort 'Backup failed' unless success end def restore - $progress.print 'Decompressing database ... ' - success = system('gzip', '-d', db_file_name_gz) - report_success(success) - abort 'Restore failed: decompress error' unless success + decompress_rd, decompress_wr = IO.pipe + decompress_pid = spawn(*%W(gzip -cd), out: decompress_wr, in: db_file_name) + decompress_wr.close - success = case config["adapter"] + restore_pid = case config["adapter"] when /^mysql/ then $progress.print "Restoring MySQL database #{config['database']} ... " # Workaround warnings from MySQL 5.6 about passwords on cmd line ENV['MYSQL_PWD'] = config["password"].to_s if config["password"] - system('mysql', *mysql_args, config['database'], in: db_file_name) + spawn('mysql', *mysql_args, config['database'], in: decompress_rd) when "postgresql" then $progress.print "Restoring PostgreSQL database #{config['database']} ... " pg_env - system('psql', config['database'], '-f', db_file_name) + spawn('psql', config['database'], in: decompress_rd) end + decompress_rd.close + + success = [decompress_pid, restore_pid].all? { |pid| Process.waitpid(pid); $?.success? } + report_success(success) abort 'Restore failed' unless success end protected - def db_file_name - File.join(db_dir, 'database.sql') - end - - def db_file_name_gz - File.join(db_dir, 'database.sql.gz') - end - def mysql_args args = { 'host' => '--host', diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index f011fd03de0..53e79d4d1f7 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,11 +150,11 @@ module Backup private def backup_contents - folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] + folders_to_backup + ["database.sql.gz", "uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] end def folders_to_backup - folders = %w{repositories db} + folders = %w{repositories} if ENV["SKIP"] return folders.reject{ |folder| ENV["SKIP"].include?(folder) } -- cgit v1.2.1 From 7b71727c562b6f6337a180ae136be94bf0f6ed31 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 15:10:13 +0200 Subject: Remove old "files" tarball explicitly --- lib/backup/files.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/backup/files.rb b/lib/backup/files.rb index d0a6e8f27be..1b08e3324d7 100644 --- a/lib/backup/files.rb +++ b/lib/backup/files.rb @@ -15,6 +15,7 @@ module Backup # Copy files from public/files to backup/files def dump FileUtils.mkdir_p(Gitlab.config.backup.path) + FileUtils.rm_f(backup_tarball) run_pipeline!([%W(tar -C #{files_parent_dir} -cf - #{name}), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) end -- cgit v1.2.1 From e789644783fae55f1095ffcc38b32f810f549caa Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 15:21:15 +0200 Subject: Keep old path: db/database.sql.gz Documentation elsewhere refers to this internal path, let's keep it. --- lib/backup/database.rb | 3 ++- lib/backup/manager.rb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/backup/database.rb b/lib/backup/database.rb index 4bdf6e1c628..fe0434361e8 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -6,10 +6,11 @@ module Backup def initialize @config = YAML.load_file(File.join(Rails.root,'config','database.yml'))[Rails.env] - @db_file_name = File.join(Gitlab.config.backup.path, 'database.sql.gz') + @db_file_name = File.join(Gitlab.config.backup.path, 'db', 'database.sql.gz') end def dump + FileUtils.mkdir_p(File.dirname(db_file_name)) FileUtils.rm_f(db_file_name) compress_rd, compress_wr = IO.pipe compress_pid = spawn(*%W(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600]) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 53e79d4d1f7..f011fd03de0 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,11 +150,11 @@ module Backup private def backup_contents - folders_to_backup + ["database.sql.gz", "uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] + folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] end def folders_to_backup - folders = %w{repositories} + folders = %w{repositories db} if ENV["SKIP"] return folders.reject{ |folder| ENV["SKIP"].include?(folder) } -- cgit v1.2.1 From 7d58489fd908b2263f02e8919b1bd0b3fae1201d Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 15:22:08 +0200 Subject: Remove unused variable --- lib/backup/files.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/backup/files.rb b/lib/backup/files.rb index 1b08e3324d7..5a210a0e464 100644 --- a/lib/backup/files.rb +++ b/lib/backup/files.rb @@ -2,14 +2,13 @@ require 'open3' module Backup class Files - attr_reader :name, :app_files_dir, :backup_tarball, :backup_dir, :files_parent_dir + attr_reader :name, :app_files_dir, :backup_tarball, :files_parent_dir def initialize(app_files_dir) @app_files_dir = File.realpath(app_files_dir) @name = File.basename(app_files_dir) @files_parent_dir = File.realpath(File.join(@app_files_dir, '..')) - @backup_dir = Gitlab.config.backup.path - @backup_tarball = File.join(@backup_dir, name + '.tar.gz') + @backup_tarball = File.join(Gitlab.config.backup.path, name + '.tar.gz') end # Copy files from public/files to backup/files -- cgit v1.2.1 From 901f5445785a754227d8b77ca535947ab8cbbfca Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 15:38:21 +0200 Subject: Remove superfluous mkdir -p --- lib/backup/database.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/backup/database.rb b/lib/backup/database.rb index fe0434361e8..67b2a64bd10 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -16,8 +16,6 @@ module Backup compress_pid = spawn(*%W(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600]) compress_rd.close - FileUtils.mkdir_p(Gitlab.config.backup.path) - dump_pid = case config["adapter"] when /^mysql/ then $progress.print "Dumping MySQL database #{config['database']} ... " -- cgit v1.2.1 From 852526e07fdac8cc11bdd4dd47cab71b39ae192c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 16:10:14 +0200 Subject: Spec fixes for new backup contents --- spec/tasks/gitlab/backup_rake_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 3be7dd4e52b..9a881455ea1 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -112,14 +112,14 @@ describe 'gitlab:app namespace rake task' do it 'should set correct permissions on the tar contents' do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads repositories builds} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz} ) expect(exit_status).to eq(0) expect(tar_contents).to match('db/') - expect(tar_contents).to match('uploads/') + expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('repositories/') - expect(tar_contents).to match('builds/') - expect(tar_contents).not_to match(/^.{4,9}[rwx].* (db|uploads|repositories|builds)\/$/) + expect(tar_contents).to match('builds.tar.gz') + expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz)\/$/) end it 'should delete temp directories' do @@ -160,12 +160,12 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do tar_contents, _exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads repositories builds} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz} ) expect(tar_contents).to match('db/') - expect(tar_contents).to match('uploads/') - expect(tar_contents).to match('builds/') + expect(tar_contents).to match('uploads.tar.gz') + expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).not_to match('repositories/') end -- cgit v1.2.1 From b847baf8c405935415fcd4ad4a620d577f6526f8 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 6 Oct 2015 16:11:59 +0200 Subject: One more backup spec fix Stop the 'uploads' part from actually running. --- spec/tasks/gitlab/backup_rake_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 9a881455ea1..386ac9c8372 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -55,6 +55,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke) + expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke) expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end -- cgit v1.2.1 From 857cc388e1a93760e37145cd32514dc4026d0775 Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Mon, 12 Oct 2015 18:38:34 +1100 Subject: Adds modified date to snippets#show #1767 - Fixed a bug with .btn-sm padding. - Changed created_at to modified_at for snippets#index as it seems to be more relevant. --- CHANGELOG | 1 + app/assets/stylesheets/generic/buttons.scss | 10 ++++++++++ app/views/shared/snippets/_snippet.html.haml | 3 +-- app/views/snippets/show.html.haml | 4 ++-- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index a3d796bea66..1e41f6251bd 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) + - Added last modified date to snippets#show (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/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss index 11acbe3adfa..da69fc8f51c 100644 --- a/app/assets/stylesheets/generic/buttons.scss +++ b/app/assets/stylesheets/generic/buttons.scss @@ -22,6 +22,12 @@ padding: 11px 24px; } +@mixin btn-sm { + @include btn-default; + @include border-radius(2px); + padding: 5px 10px; +} + @mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) { background-color: $light; border-color: $border-light; @@ -71,6 +77,10 @@ @include btn-default; @include btn-white; + &.btn-sm { + @include btn-sm; + } + &.btn-success, &.btn-new, &.btn-create, diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 69a713ad9aa..6f4b0453ad3 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -17,5 +17,4 @@ = link_to user_snippets_path(snippet.author) do = image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: '' = snippet.author_name - authored #{time_ago_with_tooltip(snippet.created_at)} - + authored #{time_ago_with_tooltip(snippet.updated_at)} diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 97374e073dc..a12cfd0ff43 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -11,12 +11,12 @@ = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do Add new snippet -.append-bottom-10.prepend-top-10 +.append-bottom-10.prepend-top-10.clearfix .pull-right %span.light - created by = link_to user_snippets_path(@snippet.author) do = @snippet.author_name + authored #{time_ago_with_tooltip(@snippet.updated_at)} .back-link - if @snippet.author == current_user -- cgit v1.2.1 From 7f7d39858f87d5920137d63e5d6005a0989ca392 Mon Sep 17 00:00:00 2001 From: Iman Mohamadi Date: Wed, 14 Oct 2015 11:14:59 +0330 Subject: ugly outlines removed form sidebar --- app/assets/stylesheets/framework/sidebar.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c5ea3aca7ca..1c42ba2fd75 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -64,6 +64,7 @@ text-decoration: none; padding-left: 22px; font-weight: normal; + outline: none; &:hover { text-decoration: none; @@ -176,6 +177,7 @@ text-align: center; line-height: 40px; transition-duration: .3s; + outline: none; } .collapse-nav a:hover { @@ -238,6 +240,7 @@ width: 100%; padding: 10px 22px; overflow: hidden; + outline: none; img { width: 36px; -- cgit v1.2.1 From de026375fc4eea39ae4bccb2de3bbed93de8c86b Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Wed, 14 Oct 2015 19:41:12 +1100 Subject: Updated the style of the snippets header in #show It should now more closly match the styles used in issues and merge requests with some small tweaks to be more relevant to snippets --- app/assets/stylesheets/pages/snippets.scss | 55 ++++++++++++++++++++++++++++++ app/views/snippets/show.html.haml | 51 +++++++++++++-------------- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index a3d7aba054d..39034a1391f 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -30,3 +30,58 @@ } } } + +.snippet-details { + .page-title { + margin-top: -15px; + padding: 10px 0; + margin-bottom: 0; + color: #5c5d5e; + font-size: 16px; + + .author { + color: #5c5d5e; + } + + .snippet-id { + color: #5c5d5e; + } + .btn { + padding: 10px $gl-padding; + } + } + + .snippet-title { + margin: 0; + font-size: 23px; + color: #313236; + } + + @media (max-width: $screen-md-max) { + .new-snippet-link { + display: none; + } + } + + @media (max-width: $screen-sm-max) { + .creator, + .page-title .btn-close { + display: none; + } + } +} + +.snippet-box { + @include border-radius(2px); + + display: inline-block; + padding: 10px $gl-padding; + font-weight: normal; + margin-right: 10px; + font-size: $gl-font-size; + + &.snippet-box-locked { + background: $gl-gray; + color: #FFF; + } +} diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index a12cfd0ff43..f2519abea6f 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,30 +1,29 @@ - page_title @snippet.title, "Snippets" -%h4.page-title - = @snippet.title - - if @snippet.private? - %span.label.label-success - %i.fa.fa-lock - private +.snippet + .snippet-details + .page-title + - if @snippet.private? + .snippet-box.snippet-box-locked + %i.fa.fa-lock + Private + %span.creator + updated by #{link_to_member(@project, @snippet.author, size: 24)} + · + = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_updated_ago') - .pull-right - = link_to new_snippet_path, class: "btn btn-new btn-sm", title: "New Snippet" do - Add new snippet - -.append-bottom-10.prepend-top-10.clearfix - .pull-right - %span.light - = link_to user_snippets_path(@snippet.author) do - = @snippet.author_name - authored #{time_ago_with_tooltip(@snippet.updated_at)} - - .back-link - - if @snippet.author == current_user - = link_to dashboard_snippets_path do - ← your snippets - - else - = link_to explore_snippets_path do - ← explore snippets + .pull-right + = link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do + = icon('plus') + Add new snippet + = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' + - if can?(current_user, :update_personal_snippet, @snippet) + = link_to edit_snippet_path(@snippet), class: "btn btn-grouped issuabled-edit" do + = icon('pencil-square-o') + Edit + .gray-content-block.middle-block + %h2.snippet-title + = gfm escape_once(@snippet.title) .file-holder .file-title @@ -33,9 +32,5 @@ = @snippet.file_name .file-actions .btn-group - - if can?(current_user, :update_personal_snippet, @snippet) - = link_to "edit", edit_snippet_path(@snippet), class: "btn btn-sm", title: 'Edit Snippet' = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" - - if can?(current_user, :admin_personal_snippet, @snippet) - = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet' = render 'shared/snippets/blob' -- cgit v1.2.1 From a60853fda09b37c2990b2437baa7a5d13b54572d Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Wed, 14 Oct 2015 20:37:36 +1100 Subject: include created_at date in heading --- app/views/snippets/show.html.haml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index f2519abea6f..9d20b76cbf2 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -7,15 +7,21 @@ .snippet-box.snippet-box-locked %i.fa.fa-lock Private + %span.snippet-id Snippet ##{@snippet.id} %span.creator - updated by #{link_to_member(@project, @snippet.author, size: 24)} + · created by #{link_to_member(@project, @snippet.author, size: 24)} · - = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_updated_ago') + = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') + - if @snippet.updated_at != @snippet.created_at + %span + · + = icon('edit', title: 'edited') + = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') .pull-right = link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do = icon('plus') - Add new snippet + new snippet = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' - if can?(current_user, :update_personal_snippet, @snippet) = link_to edit_snippet_path(@snippet), class: "btn btn-grouped issuabled-edit" do -- cgit v1.2.1 From 1f92c22fec8493fa0efb8a53829a4c726e78934e Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Wed, 14 Oct 2015 23:12:08 +1100 Subject: New snippet design for projects Split out header into shared partial Used action partials to have unique actions for shared and personal snippets changed back to created date in list view Switched to using existing color classes --- app/assets/stylesheets/pages/snippets.scss | 16 ++++++------- app/views/projects/snippets/_actions.html.haml | 9 ++++++++ app/views/projects/snippets/show.html.haml | 28 +--------------------- app/views/shared/snippets/_header.html.haml | 25 ++++++++++++++++++++ app/views/shared/snippets/_snippet.html.haml | 2 +- app/views/snippets/_actions.html.haml | 8 +++++++ app/views/snippets/show.html.haml | 32 +------------------------- 7 files changed, 53 insertions(+), 67 deletions(-) create mode 100644 app/views/projects/snippets/_actions.html.haml create mode 100644 app/views/shared/snippets/_header.html.haml create mode 100644 app/views/snippets/_actions.html.haml diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 39034a1391f..af391481764 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -37,10 +37,14 @@ padding: 10px 0; margin-bottom: 0; color: #5c5d5e; - font-size: 16px; + font-size: 13px; + @include clearfix(); - .author { - color: #5c5d5e; + .creator { + color: $gl-gray; + a { + color: $gl-gray; + } } .snippet-id { @@ -79,9 +83,5 @@ font-weight: normal; margin-right: 10px; font-size: $gl-font-size; - - &.snippet-box-locked { - background: $gl-gray; - color: #FFF; - } + border: 1px solid; } diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml new file mode 100644 index 00000000000..4d5f7026373 --- /dev/null +++ b/app/views/projects/snippets/_actions.html.haml @@ -0,0 +1,9 @@ += link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do + = icon('plus') + new snippet +- if can?(current_user, :admin_project_snippet, @snippet) + = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' +- if can?(current_user, :update_project_snippet, @snippet) + = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do + = icon('pencil-square-o') + Edit diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index be7d4d486fa..4f236fcc7e2 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,27 +1,6 @@ - page_title @snippet.title, "Snippets" = render "header_title" - -%h3.page-title - = @snippet.title - - .pull-right - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do - Add new snippet - -%hr - -.append-bottom-20 - .pull-right - = "##{@snippet.id}" - %span.light - by - = link_to user_path(@snippet.author) do - = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" - = @snippet.author_name - - .back-link - = link_to namespace_project_snippets_path(@project.namespace, @project) do - ← project snippets += render 'shared/snippets/header' .file-holder .file-title @@ -30,11 +9,6 @@ = @snippet.file_name .file-actions .btn-group - - if can?(current_user, :update_project_snippet, @snippet) - = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", title: 'Edit Snippet' = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" - - if can?(current_user, :admin_project_snippet, @snippet) - = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-sm btn-remove", title: 'Delete Snippet' - = render 'shared/snippets/blob' %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml new file mode 100644 index 00000000000..95786ee3377 --- /dev/null +++ b/app/views/shared/snippets/_header.html.haml @@ -0,0 +1,25 @@ +.snippet + .snippet-details + .page-title + .snippet-box{class: visibility_level_color(@snippet.visibility_level)} + = visibility_level_icon(@snippet.visibility_level) + = visibility_level_label(@snippet.visibility_level) + %span.snippet-id Snippet ##{@snippet.id} + %span.creator + · created by #{link_to_member(@project, @snippet.author, size: 24)} + · + = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') + - if @snippet.updated_at != @snippet.created_at + %span + · + = icon('edit', title: 'edited') + = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') + + .pull-right + - if @snippet.project_id? + = render "projects/snippets/actions" + - else + = render "snippets/actions" + .gray-content-block.middle-block + %h2.snippet-title + = gfm escape_once(@snippet.title) diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml index 6f4b0453ad3..c6294caddc7 100644 --- a/app/views/shared/snippets/_snippet.html.haml +++ b/app/views/shared/snippets/_snippet.html.haml @@ -17,4 +17,4 @@ = link_to user_snippets_path(snippet.author) do = image_tag avatar_icon(snippet.author_email), class: "avatar s24", alt: '' = snippet.author_name - authored #{time_ago_with_tooltip(snippet.updated_at)} + authored #{time_ago_with_tooltip(snippet.created_at)} diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml new file mode 100644 index 00000000000..a4fdf89a4ee --- /dev/null +++ b/app/views/snippets/_actions.html.haml @@ -0,0 +1,8 @@ += link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do + = icon('plus') + new snippet += link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' +- if can?(current_user, :update_personal_snippet, @snippet) + = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do + = icon('pencil-square-o') + Edit diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 9d20b76cbf2..202a43bc57d 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,35 +1,5 @@ - page_title @snippet.title, "Snippets" - -.snippet - .snippet-details - .page-title - - if @snippet.private? - .snippet-box.snippet-box-locked - %i.fa.fa-lock - Private - %span.snippet-id Snippet ##{@snippet.id} - %span.creator - · created by #{link_to_member(@project, @snippet.author, size: 24)} - · - = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') - - if @snippet.updated_at != @snippet.created_at - %span - · - = icon('edit', title: 'edited') - = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') - - .pull-right - = link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do - = icon('plus') - new snippet - = link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' - - if can?(current_user, :update_personal_snippet, @snippet) - = link_to edit_snippet_path(@snippet), class: "btn btn-grouped issuabled-edit" do - = icon('pencil-square-o') - Edit - .gray-content-block.middle-block - %h2.snippet-title - = gfm escape_once(@snippet.title) += render 'shared/snippets/header' .file-holder .file-title -- cgit v1.2.1 From c5d97c7e37fbfc4ed478da24d64d54c770ed2097 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Sat, 19 Sep 2015 13:33:28 +0200 Subject: COPYING is now also accepted as licence file Fixes #2526 --- CHANGELOG | 2 ++ app/models/repository.rb | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 31feb115d1e..f078d08d86b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -90,6 +90,8 @@ v 8.0.1 v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) +v 8.0.0 (unreleased) + - Accept COPYING as licence file (Zeger-Jan van de Weg) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - Fix broken sort in merge request API (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index 8b51602bc23..cc46ab916c7 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -210,9 +210,9 @@ class Repository def license cache.fetch(:license) do - tree(:head).blobs.find do |file| - file.name =~ /\Alicense/i - end + tree(:head).blobs.find_all do |file| + file.name =~ /\A(copying|license)/i + end.last # Prefer `LICENSE` as filename over `COPYING` end end -- cgit v1.2.1 From 9ec32b1cacdd411cbdcc262f7b327c6bfc552735 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 21 Sep 2015 10:14:38 +0200 Subject: Prefer Licence over Copying --- app/models/repository.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index cc46ab916c7..dc7cd926745 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -210,9 +210,13 @@ class Repository def license cache.fetch(:license) do - tree(:head).blobs.find_all do |file| - file.name =~ /\A(copying|license)/i - end.last # Prefer `LICENSE` as filename over `COPYING` + licenses = tree(:head).blobs.find_all do |file| + file.name =~ /\A(copying|license)/i + end + + # If `licence`, `copying` and `copying.lesser` are found, return in the + # following order: licence, copying, copying.lesser + licenses.find { |l| l =~ /\Alicence/i } || licenses.sort.first end end -- cgit v1.2.1 From 7248566632d5de39e7b57594dcf6e021ae912282 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 23 Sep 2015 19:48:53 +0200 Subject: Add changelog line to right release --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f078d08d86b..3d0beb0b7ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -82,6 +82,7 @@ v 8.0.2 - Fix Reply by email for non-UTF-8 messages. - Add option to use StartTLS with Reply by email IMAP server. - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie) + - Accept COPYING as licence file (Zeger-Jan van de Weg) v 8.0.1 - Remove git refs used internally by GitLab from network graph (Stan Hu) @@ -90,8 +91,6 @@ v 8.0.1 v 8.0.0 - Fix Markdown links not showing up in dashboard activity feed (Stan Hu) - Remove milestones from merge requests when milestones are deleted (Stan Hu) -v 8.0.0 (unreleased) - - Accept COPYING as licence file (Zeger-Jan van de Weg) - Fix HTML link that was improperly escaped in new user e-mail (Stan Hu) - Fix broken sort in merge request API (Stan Hu) - Bump rouge to 1.10.1 to remove warning noise and fix other syntax highlighting bugs (Stan Hu) -- cgit v1.2.1 From 54e6c0045bb13c05cc5478cbdf47d3246bd9fe2b Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 22 Sep 2015 14:04:14 +0200 Subject: Added three rake tasks for repository maintainance Tasks added: gitlab:git:repack gitlab:git:gc gitlab:git:prune --- CHANGELOG | 1 + lib/tasks/gitlab/git.rake | 52 ++++++++++++++++++++++++++++++++++++++ lib/tasks/gitlab/task_helpers.rake | 4 +++ 3 files changed, 57 insertions(+) create mode 100644 lib/tasks/gitlab/git.rake diff --git a/CHANGELOG b/CHANGELOG index 31feb115d1e..2c98b092e1b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,6 +62,7 @@ v 8.0.4 - Fix "Assign All" button on Runner admin page - Fix search in Files - Add full project namespace to payload of system webhooks (Ricardo Band) + - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) v 8.0.3 - Fix URL shown in Slack notifications diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake new file mode 100644 index 00000000000..4fbf5a9393c --- /dev/null +++ b/lib/tasks/gitlab/git.rake @@ -0,0 +1,52 @@ +namespace :gitlab do + namespace :git do + + desc "GitLab | Git | Repack" + task repack: :environment do + failures = perform_git_cmd('git repack -a --quiet', 'Git repack') + if failures.empty? + puts "Done".green + else + output_failures(failures) + end + end + + desc "GitLab | Git | Run gits garbage collection on all repo's" + task gc: :environment do + failures = perform_git_cmd('git gc --auto --quiet', "Garbage Collection") + if failures.empty? + puts "Done".green + else + output_failures(failures) + end + end + + desc "GitLab | Git | Git prune all repo's" + task prune: :environment do + failures = perform_git_cmd('git prune', 'Git Prune') + if failures.empty? + puts "Done".green + else + output_failures(failures) + end + end + + def perform_git_cmd(cmd, message) + puts "Starting #{message} on all repositories" + + failures = [] + all_repos.each do |r| + puts "Performing #{message} at #{r}" + failures << r unless system(*%w(#{cmd}), chdir: r) + end + + failures + end + + def output_failures(failures) + puts "The following repositories reported errors:".red + failures.each { |f| puts "- #{f}" } + end + + end +end diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index c95b6540ebc..77c41bf61cb 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -128,4 +128,8 @@ namespace :gitlab do false end end + + def all_repos + Dir.glob(File.join(Gitlab.config.gitlab_shell.repos_path, '**/*\.git')) + end end -- cgit v1.2.1 From 8db063b579322238af43f6d04b5968d9c6ea935d Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 1 Oct 2015 13:34:41 +0200 Subject: Scalable way of requesting all repos --- lib/tasks/gitlab/git.rake | 19 +++++++++++-------- lib/tasks/gitlab/task_helpers.rake | 6 +++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake index 4fbf5a9393c..65ee430d550 100644 --- a/lib/tasks/gitlab/git.rake +++ b/lib/tasks/gitlab/git.rake @@ -3,7 +3,7 @@ namespace :gitlab do desc "GitLab | Git | Repack" task repack: :environment do - failures = perform_git_cmd('git repack -a --quiet', 'Git repack') + failures = perform_git_cmd(%W(git repack -a --quiet), "Repacking repo") if failures.empty? puts "Done".green else @@ -11,9 +11,9 @@ namespace :gitlab do end end - desc "GitLab | Git | Run gits garbage collection on all repo's" + desc "GitLab | Git | Run garbage collection on all repos" task gc: :environment do - failures = perform_git_cmd('git gc --auto --quiet', "Garbage Collection") + failures = perform_git_cmd(%W(git gc --auto --quiet), "Garbage Collecting") if failures.empty? puts "Done".green else @@ -21,9 +21,9 @@ namespace :gitlab do end end - desc "GitLab | Git | Git prune all repo's" + desc "GitLab | Git | Prune all repos" task prune: :environment do - failures = perform_git_cmd('git prune', 'Git Prune') + failures = perform_git_cmd(%W(git prune), "Git Prune") if failures.empty? puts "Done".green else @@ -35,9 +35,12 @@ namespace :gitlab do puts "Starting #{message} on all repositories" failures = [] - all_repos.each do |r| - puts "Performing #{message} at #{r}" - failures << r unless system(*%w(#{cmd}), chdir: r) + all_repos do |repo| + if system(*cmd, chdir: repo) + puts "Performed #{message} at #{repo}" + else + failures << repo + end end failures diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index 77c41bf61cb..e35fd47c5c4 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -130,6 +130,10 @@ namespace :gitlab do end def all_repos - Dir.glob(File.join(Gitlab.config.gitlab_shell.repos_path, '**/*\.git')) + IO.popen(%W(find #{Gitlab.config.gitlab_shell.repos_path} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find| + find.each_line do |path| + yield path.chomp + end + end end end -- cgit v1.2.1 From ed41333a6ecbfcc04781a47a3dc71064c92d837b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 14 Oct 2015 19:27:23 +0200 Subject: Use Gitlab::Markdown.render with :pipeline option rather than different methods --- app/helpers/gitlab_markdown_helper.rb | 37 +--- app/views/events/_commit.html.haml | 2 +- app/views/projects/commit/_commit_box.html.haml | 4 +- app/views/projects/commits/_commit.html.haml | 2 +- app/views/projects/issues/show.html.haml | 2 +- .../projects/merge_requests/show/_mr_box.html.haml | 2 +- .../projects/merge_requests/widget/_open.html.haml | 2 +- app/views/projects/milestones/show.html.haml | 2 +- app/views/projects/repositories/_feed.html.haml | 2 +- lib/gitlab/markdown.rb | 236 ++++++++++----------- lib/gitlab/markdown/markdown_filter.rb | 39 ++++ lib/gitlab/markdown/relative_link_filter.rb | 2 +- lib/gitlab/markdown/sanitization_filter.rb | 6 +- lib/gitlab/reference_extractor.rb | 20 +- 14 files changed, 181 insertions(+), 177 deletions(-) create mode 100644 lib/gitlab/markdown/markdown_filter.rb diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 65813482120..c6fff1b8ecf 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -20,7 +20,7 @@ module GitlabMarkdownHelper end user = current_user if defined?(current_user) - gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: user) + gfm_body = Gitlab::Markdown.render(escaped_body, project: @project, current_user: user, pipeline: :single_line) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) if fragment.children.size == 1 && fragment.children[0].name == 'a' @@ -48,37 +48,20 @@ module GitlabMarkdownHelper def markdown(text, context = {}) return "" unless text.present? - context.reverse_merge!( - path: @path, - pipeline: :default, - project: @project, - project_wiki: @project_wiki, - ref: @ref - ) - - user = current_user if defined?(current_user) - + context[:project] ||= @project + html = Gitlab::Markdown.render(text, context) - Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user) - end - # TODO (rspeicher): Remove all usages of this helper and just call `markdown` - # with a custom pipeline depending on the content being rendered - def gfm(text, options = {}) - return "" unless text.present? + context.merge!( + current_user: (current_user if defined?(current_user)), - options.reverse_merge!( - path: @path, - pipeline: :default, - project: @project, - project_wiki: @project_wiki, - ref: @ref + # RelativeLinkFilter + requested_path: @path, + project_wiki: @project_wiki, + ref: @ref ) - user = current_user if defined?(current_user) - - html = Gitlab::Markdown.gfm(text, options) - Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user) + Gitlab::Markdown.post_process(html, context) end def asciidoc(text) diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index ad63841ccf3..4ba8b84fd92 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -2,4 +2,4 @@ .commit-row-title = link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: '' · - = gfm event_commit_title(commit[:message]), project: project + = markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index fbf0a9ec0c3..f3be1da8428 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -50,10 +50,10 @@ .commit-box.gray-content-block.middle-block %h3.commit-title - = gfm escape_once(@commit.title) + = markdown escape_once(@commit.title), pipeline: :single_line - if @commit.description.present? %pre.commit-description - = preserve(gfm(escape_once(@commit.description))) + = preserve(markdown(escape_once(@commit.description), pipeline: :single_line)) :coffeescript $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}") diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index cddd5aa3a83..9e0b536bb4b 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -32,7 +32,7 @@ - if commit.description? .commit-row-description.js-toggle-content %pre - = preserve(gfm(escape_once(commit.description))) + = preserve(markdown(escape_once(commit.description), pipeline: :single_line)) .commit-row-info = commit_author_link(commit, avatar: true, size: 24) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 5cb814c9ea8..3233c6884cc 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -37,7 +37,7 @@ .gray-content-block.middle-block %h2.issue-title - = gfm escape_once(@issue.title) + = markdown escape_once(@issue.title), pipeline: :single_line %div - if @issue.description.present? .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index b4f62a75890..448230a377c 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,6 +1,6 @@ .gray-content-block.middle-block %h2.issue-title - = gfm escape_once(@merge_request.title) + = markdown escape_once(@merge_request.title), pipeline: :single_line %div - if @merge_request.description.present? diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 0aad9bb3e88..8629129c0b1 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -24,4 +24,4 @@ %i.fa.fa-check Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)} = succeed '.' do - != gfm(issues_sentence(@closes_issues)) + != markdown issues_sentence(@closes_issues), pipeline: :gfm diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 4eeb0621e52..302410765fc 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -31,7 +31,7 @@ %span All issues for this milestone are closed. You may close milestone now. %h3.issue-title - = gfm escape_once(@milestone.title) + = markdown escape_once(@milestone.title), pipeline: :single_line %div - if @milestone.description.present? .description diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index f3526ad0747..6ca919f7f80 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -12,7 +12,7 @@ = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do %code= commit.short_id = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' - = gfm escape_once(truncate(commit.title, length: 40)) + = markdown escape_once(truncate(commit.title, length: 40)), pipeline: :single_line %td %span.pull-right.cgray = time_ago_with_tooltip(commit.committed_date) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index d5b0060dd56..773ebddba2b 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -19,51 +19,15 @@ module Gitlab # context - Hash of context options passed to our HTML Pipeline # # Returns an HTML-safe String - def self.render(markdown, context = {}) - html = renderer.render(markdown) - html = gfm(html, context) + def self.render(text, context = {}) + pipeline = context[:pipeline] || :full - html.html_safe - end + html_pipeline = html_pipelines[pipeline] - # Convert a Markdown String into HTML without going through the HTML - # Pipeline. - # - # Note that because the pipeline is skipped, SanitizationFilter is as well. - # Do not output the result of this method to the user. - # - # markdown - Markdown String - # - # Returns a String - def self.render_without_gfm(markdown) - renderer.render(markdown) - end + transformers = get_context_transformers(pipeline) + context = transformers.reduce(context) { |context, transformer| transformer.call(context) } - # Perform post-processing on an HTML String - # - # This method is used to perform state-dependent changes to a String of - # HTML, such as removing references that the current user doesn't have - # permission to make (`RedactorFilter`). - # - # html - String to process - # options - Hash of options to customize output - # :pipeline - Symbol pipeline type - # :project - Project - # :user - User object - # - # Returns an HTML-safe String - def self.post_process(html, options) - context = { - project: options[:project], - current_user: options[:user] - } - doc = post_processor.to_document(html, context) - - if options[:pipeline] == :atom - doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) - else - doc.to_html - end.html_safe + html_pipeline.to_html(text, context) end # Provide autoload paths for filters to prevent a circular dependency error @@ -75,6 +39,7 @@ module Gitlab autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' + autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter' autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' @@ -85,98 +50,38 @@ module Gitlab autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' - # Public: Parse the provided HTML with GitLab-Flavored Markdown + # Perform post-processing on an HTML String + # + # This method is used to perform state-dependent changes to a String of + # HTML, such as removing references that the current user doesn't have + # permission to make (`RedactorFilter`). # - # html - HTML String - # options - A Hash of options used to customize output (default: {}) - # :no_header_anchors - Disable header anchors in TableOfContentsFilter - # :path - Current path String - # :pipeline - Symbol pipeline type - # :project - Current Project object - # :project_wiki - Current ProjectWiki object - # :ref - Current ref String + # html - String to process + # context - Hash of options to customize output + # :pipeline - Symbol pipeline type + # :project - Project + # :user - User object # # Returns an HTML-safe String - def self.gfm(html, options = {}) - return '' unless html.present? - - @pipeline ||= HTML::Pipeline.new(filters) - - context = { - # SanitizationFilter - pipeline: options[:pipeline], - - # EmojiFilter - asset_host: Gitlab::Application.config.asset_host, - asset_root: Gitlab.config.gitlab.base_url, - - # ReferenceFilter - only_path: only_path_pipeline?(options[:pipeline]), - project: options[:project], - - # RelativeLinkFilter - project_wiki: options[:project_wiki], - ref: options[:ref], - requested_path: options[:path], - - # TableOfContentsFilter - no_header_anchors: options[:no_header_anchors] - } + def self.post_process(html, context) + doc = html_pipelines[:post_process].to_document(html, context) - @pipeline.to_html(html, context).html_safe - end - - private - - # Check if a pipeline enables the `only_path` context option - # - # Returns Boolean - def self.only_path_pipeline?(pipeline) - case pipeline - when :atom, :email - false + if context[:xhtml] + doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) else - true - end - end - - def self.redcarpet_options - # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use - @redcarpet_options ||= { - fenced_code_blocks: true, - footnotes: true, - lax_spacing: true, - no_intra_emphasis: true, - space_after_headers: true, - strikethrough: true, - superscript: true, - tables: true - }.freeze - end - - def self.renderer - @markdown ||= begin - renderer = Redcarpet::Render::HTML.new - Redcarpet::Markdown.new(renderer, redcarpet_options) - end - end - - def self.post_processor - @post_processor ||= HTML::Pipeline.new([Gitlab::Markdown::RedactorFilter]) + doc.to_html + end.html_safe end - # Filters used in our pipeline - # - # SanitizationFilter should come first so that all generated reference HTML - # goes through untouched. - # - # See https://github.com/jch/html-pipeline#filters for more filters. - def self.filters - [ + private + FILTERS = { + plain_markdown: [ + Gitlab::Markdown::MarkdownFilter + ], + gfm: [ Gitlab::Markdown::SyntaxHighlightFilter, Gitlab::Markdown::SanitizationFilter, - Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, Gitlab::Markdown::AutolinkFilter, @@ -192,7 +97,90 @@ module Gitlab Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::TaskListFilter + ], + + full: [:plain_markdown, :gfm], + atom: :full, + email: :full, + description: :full, + single_line: :gfm, + + post_process: [ + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::RedactorFilter ] + } + + CONTEXT_TRANSFORMERS = { + gfm: { + only_path: true, + + # EmojiFilter + asset_host: Gitlab::Application.config.asset_host, + asset_root: Gitlab.config.gitlab.base_url + }, + full: :gfm, + + atom: [ + :full, + { + only_path: false, + xhtml: true + } + ], + email: [ + :full, + { + only_path: false + } + ], + description: [ + :full, + { + # SanitizationFilter + inline_sanitization: true + } + ], + single_line: :gfm, + + post_process: { + post_process: true + } + } + + def self.html_pipelines + @html_pipelines ||= Hash.new do |hash, pipeline| + filters = get_filters(pipeline) + HTML::Pipeline.new(filters) + end + end + + def self.get_filters(pipelines) + Array.wrap(pipelines).flat_map do |pipeline| + case pipeline + when Class + pipeline + when Symbol + get_filters(FILTERS[pipeline]) + when Array + get_filters(pipeline) + end + end.compact + end + + def self.get_context_transformers(pipelines) + Array.wrap(pipelines).flat_map do |pipeline| + case pipeline + when Hash + ->(context) { context.merge(pipeline) } + when Proc + pipeline + when Symbol + get_context_transformers(CONTEXT_TRANSFORMERS[pipeline]) + when Array + get_context_transformers(pipeline) + end + end.compact end end end diff --git a/lib/gitlab/markdown/markdown_filter.rb b/lib/gitlab/markdown/markdown_filter.rb new file mode 100644 index 00000000000..921e2a0794e --- /dev/null +++ b/lib/gitlab/markdown/markdown_filter.rb @@ -0,0 +1,39 @@ +module Gitlab + module Markdown + class MarkdownFilter < HTML::Pipeline::TextFilter + def initialize(text, context = nil, result = nil) + super text, context, result + @text = @text.gsub "\r", '' + end + + def call + html = self.class.renderer.render(@text) + html.rstrip! + html + end + + private + + def self.redcarpet_options + # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + @redcarpet_options ||= { + fenced_code_blocks: true, + footnotes: true, + lax_spacing: true, + no_intra_emphasis: true, + space_after_headers: true, + strikethrough: true, + superscript: true, + tables: true + }.freeze + end + + def self.renderer + @renderer ||= begin + renderer = Redcarpet::Render::HTML.new + Redcarpet::Markdown.new(renderer, redcarpet_options) + end + end + end + end +end diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index 6ee3d1ce039..3e9909d2f33 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -16,7 +16,7 @@ module Gitlab def call return doc unless linkable_files? - doc.search('a').each do |el| + doc.search('a:not(.gfm)').each do |el| process_link_attr el.attribute('href') end diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb index e368de7d848..550dfafca85 100644 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ b/lib/gitlab/markdown/sanitization_filter.rb @@ -11,7 +11,7 @@ module Gitlab def whitelist # Descriptions are more heavily sanitized, allowing only a few elements. # See http://git.io/vkuAN - if pipeline == :description + if context[:inline_sanitization] whitelist = LIMITED whitelist[:elements] -= %w(pre code img ol ul li) else @@ -25,10 +25,6 @@ module Gitlab private - def pipeline - context[:pipeline] || :default - end - def customized?(transformers) transformers.last.source_location[0] == __FILE__ end diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 66016ecc877..633c988d025 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -13,7 +13,8 @@ module Gitlab def analyze(text) references.clear - @text = Gitlab::Markdown.render_without_gfm(text) + + @document = Gitlab::Markdown.render(text, project: project) end %i(user label issue merge_request snippet commit commit_range).each do |type| @@ -44,20 +45,13 @@ module Gitlab filter = Gitlab::Markdown.const_get(klass) context = { - project: project, - current_user: current_user, - - # We don't actually care about the links generated - only_path: true, - ignore_blockquotes: true, - - # ReferenceGathererFilter + project: project, + current_user: current_user, load_lazy_references: false, reference_filter: filter } - pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) - result = pipeline.call(@text) + result = self.class.pipeline.call(@document, context) values = result[:references][filter_type].uniq @@ -67,5 +61,9 @@ module Gitlab values end + + def self.pipeline + @pipeline ||= HTML::Pipeline.new([Gitlab::Markdown::ReferenceGathererFilter]) + end end end -- cgit v1.2.1 From da9746e59a46005a0948d34bfd94ae715143c3f7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 14 Oct 2015 19:28:46 +0200 Subject: Enable caching of Gitlab::Markdown rendered result --- lib/gitlab/markdown.rb | 35 +++++++++++++++++++++++++---------- lib/gitlab/reference_extractor.rb | 4 ++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 773ebddba2b..21416f0fa02 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -20,14 +20,16 @@ module Gitlab # # Returns an HTML-safe String def self.render(text, context = {}) - pipeline = context[:pipeline] || :full - - html_pipeline = html_pipelines[pipeline] - - transformers = get_context_transformers(pipeline) - context = transformers.reduce(context) { |context, transformer| transformer.call(context) } + cache_key = context.delete(:cache_key) - html_pipeline.to_html(text, context) + if cache_key + cache_key = full_cache_key(cache_key, context[:pipeline]) + Rails.cache.fetch(cache_key) do + cacheless_render(text, context) + end + else + cacheless_render(text, context) + end end # Provide autoload paths for filters to prevent a circular dependency error @@ -130,9 +132,7 @@ module Gitlab ], email: [ :full, - { - only_path: false - } + { only_path: false } ], description: [ :full, @@ -155,6 +155,17 @@ module Gitlab end end + def self.cacheless_render(text, context = {}) + pipeline = context[:pipeline] || :full + + html_pipeline = html_pipelines[pipeline] + + transformers = get_context_transformers(pipeline) + context = transformers.reduce(context) { |context, transformer| transformer.call(context) } + + html_pipeline.to_html(text, context) + end + def self.get_filters(pipelines) Array.wrap(pipelines).flat_map do |pipeline| case pipeline @@ -182,5 +193,9 @@ module Gitlab end end.compact end + + def self.full_cache_key(cache_key, pipeline = :full) + ["markdown", *cache_key, pipeline] + end end end diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 633c988d025..2262e5a168a 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -11,10 +11,10 @@ module Gitlab @load_lazy_references = load_lazy_references end - def analyze(text) + def analyze(text, cache_key: nil) references.clear - @document = Gitlab::Markdown.render(text, project: project) + @document = Gitlab::Markdown.render(text, project: project, cache_key: cache_key) end %i(user label issue merge_request snippet commit commit_range).each do |type| -- cgit v1.2.1 From b093cb35d1614a48e980c5ccfca3a8d307d732f7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 14 Oct 2015 20:18:49 +0200 Subject: Use Gitlab::Markdown for Asciidoc and ReferenceExtractor pipelines --- app/helpers/gitlab_markdown_helper.rb | 15 ++- lib/gitlab/asciidoc.rb | 27 +--- lib/gitlab/markdown.rb | 226 +++++++++++++++++++++------------- lib/gitlab/reference_extractor.rb | 18 +-- spec/lib/gitlab/asciidoc_spec.rb | 6 +- 5 files changed, 166 insertions(+), 126 deletions(-) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index c6fff1b8ecf..2f933f4d695 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -65,13 +65,16 @@ module GitlabMarkdownHelper end def asciidoc(text) - Gitlab::Asciidoc.render(text, { - commit: @commit, - project: @project, - project_wiki: @project_wiki, + Gitlab::Asciidoc.render(text, + project: @project, + current_user: (current_user if defined?(current_user)), + + # RelativeLinkFilter + project_wiki: @project_wiki, requested_path: @path, - ref: @ref - }) + ref: @ref, + commit: @commit + ) end # Return the first line of +text+, up to +max_chars+, after parsing the line diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index bf33e5b1b1e..330d3342dd1 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -1,14 +1,10 @@ require 'asciidoctor' -require 'html/pipeline' module Gitlab # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters # the resulting HTML through HTML pipeline filters. module Asciidoc - # Provide autoload paths for filters to prevent a circular dependency error - autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' - DEFAULT_ADOC_ATTRS = [ 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', 'env-gitlab', 'source-highlighter=html-pipeline' @@ -24,13 +20,11 @@ module Gitlab # :requested_path # :ref # asciidoc_opts - a Hash of options to pass to the Asciidoctor converter - # html_opts - a Hash of options for HTML output: - # :xhtml - output XHTML instead of HTML # - def self.render(input, context, asciidoc_opts = {}, html_opts = {}) - asciidoc_opts = asciidoc_opts.reverse_merge( + def self.render(input, context, asciidoc_opts = {}) + asciidoc_opts.reverse_merge!( safe: :secure, - backend: html_opts[:xhtml] ? :xhtml5 : :html5, + backend: :html5, attributes: [] ) asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) @@ -38,23 +32,10 @@ module Gitlab html = ::Asciidoctor.convert(input, asciidoc_opts) if context[:project] - result = HTML::Pipeline.new(filters).call(html, context) - - save_opts = html_opts[:xhtml] ? - Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0 - - html = result[:output].to_html(save_with: save_opts) + html = Gitlab::Markdown.render(html, context.merge(pipeline: :asciidoc)) end html.html_safe end - - private - - def self.filters - [ - Gitlab::Markdown::RelativeLinkFilter - ] - end end end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 21416f0fa02..dc6751fa1c2 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -21,9 +21,9 @@ module Gitlab # Returns an HTML-safe String def self.render(text, context = {}) cache_key = context.delete(:cache_key) + cache_key = full_cache_key(cache_key, context[:pipeline]) if cache_key - cache_key = full_cache_key(cache_key, context[:pipeline]) Rails.cache.fetch(cache_key) do cacheless_render(text, context) end @@ -32,25 +32,21 @@ module Gitlab end end - # Provide autoload paths for filters to prevent a circular dependency error - autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter' - autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter' - autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter' - autoload :EmojiFilter, 'gitlab/markdown/emoji_filter' - autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter' - autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' - autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' - autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' - autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter' - autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' - autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' - autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' - autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter' - autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' - autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter' - autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter' - autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' - autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' + def self.render_result(text, context = {}) + pipeline = context[:pipeline] || :full + + html_pipeline = html_pipelines[pipeline] + + transformers = context_transformers[pipeline] + context = transformers.reduce(context) { |context, transformer| transformer.call(context) } + + html_pipeline.call(text, context) + end + + def self.cached?(cache_key, pipeline: :full) + cache_key = full_cache_key(cache_key, pipeline) + cache_key ? Rails.cache.exist?(cache_key) : false + end # Perform post-processing on an HTML String # @@ -66,21 +62,39 @@ module Gitlab # # Returns an HTML-safe String def self.post_process(html, context) - doc = html_pipelines[:post_process].to_document(html, context) + html_pipeline = html_pipelines[:post_process] if context[:xhtml] - doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) + html_pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) else - doc.to_html + html_pipeline.to_html(html, context) end.html_safe end private - FILTERS = { - plain_markdown: [ - Gitlab::Markdown::MarkdownFilter - ], - gfm: [ + + # Provide autoload paths for filters to prevent a circular dependency error + autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter' + autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter' + autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter' + autoload :EmojiFilter, 'gitlab/markdown/emoji_filter' + autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter' + autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' + autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' + autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' + autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter' + autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' + autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' + autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' + autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter' + autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' + autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter' + autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter' + autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' + autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' + + def self.gfm_filters + @gfm_filters ||= [ Gitlab::Markdown::SyntaxHighlightFilter, Gitlab::Markdown::SanitizationFilter, @@ -99,71 +113,99 @@ module Gitlab Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::TaskListFilter - ], + ] + end - full: [:plain_markdown, :gfm], - atom: :full, - email: :full, - description: :full, - single_line: :gfm, + def self.all_filters + @all_filters ||= { + plain_markdown: [ + Gitlab::Markdown::MarkdownFilter + ], + gfm: gfm_filters, + + full: [:plain_markdown, :gfm], + atom: :full, + email: :full, + description: :full, + single_line: :gfm, + + asciidoc: [ + Gitlab::Markdown::RelativeLinkFilter + ], + + post_process: [ + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::RedactorFilter + ], + + reference_extraction: [ + Gitlab::Markdown::ReferenceGathererFilter + ] + } + end - post_process: [ - Gitlab::Markdown::RelativeLinkFilter, - Gitlab::Markdown::RedactorFilter - ] - } - - CONTEXT_TRANSFORMERS = { - gfm: { - only_path: true, - - # EmojiFilter - asset_host: Gitlab::Application.config.asset_host, - asset_root: Gitlab.config.gitlab.base_url - }, - full: :gfm, - - atom: [ - :full, - { - only_path: false, - xhtml: true - } - ], - email: [ - :full, - { only_path: false } - ], - description: [ - :full, - { - # SanitizationFilter - inline_sanitization: true + def self.all_context_transformers + @all_context_transformers ||= { + gfm: { + only_path: true, + + # EmojiFilter + asset_host: Gitlab::Application.config.asset_host, + asset_root: Gitlab.config.gitlab.base_url + }, + full: :gfm, + + atom: [ + :full, + { + only_path: false, + xhtml: true + } + ], + email: [ + :full, + { + only_path: false + } + ], + description: [ + :full, + { + # SanitizationFilter + inline_sanitization: true + } + ], + single_line: :gfm, + + post_process: { + post_process: true } - ], - single_line: :gfm, - - post_process: { - post_process: true } - } + end + + def self.html_filters + @html_filters ||= Hash.new do |hash, pipeline| + filters = get_filters(pipeline) + hash[pipeline] = filters if pipeline.is_a?(Symbol) + filters + end + end def self.html_pipelines @html_pipelines ||= Hash.new do |hash, pipeline| filters = get_filters(pipeline) - HTML::Pipeline.new(filters) + html_pipeline = HTML::Pipeline.new(filters) + hash[pipeline] = html_pipeline if pipeline.is_a?(Symbol) + html_pipeline end end - def self.cacheless_render(text, context = {}) - pipeline = context[:pipeline] || :full - - html_pipeline = html_pipelines[pipeline] - - transformers = get_context_transformers(pipeline) - context = transformers.reduce(context) { |context, transformer| transformer.call(context) } - - html_pipeline.to_html(text, context) + def self.context_transformers + @context_transformers ||= Hash.new do |hash, pipeline| + transformers = get_context_transformers(pipeline) + hash[pipeline] = transformers if pipeline.is_a?(Symbol) + transformers + end end def self.get_filters(pipelines) @@ -172,9 +214,9 @@ module Gitlab when Class pipeline when Symbol - get_filters(FILTERS[pipeline]) + html_filters[all_filters[pipeline]] when Array - get_filters(pipeline) + html_filters[pipeline] end end.compact end @@ -187,14 +229,26 @@ module Gitlab when Proc pipeline when Symbol - get_context_transformers(CONTEXT_TRANSFORMERS[pipeline]) + context_transformers[all_context_transformers[pipeline]] when Array - get_context_transformers(pipeline) + context_transformers[pipeline] end end.compact end + def self.cacheless_render(text, context = {}) + result = render_result(text, context) + output = result[:output] + if output.respond_to?(:to_html) + output.to_html + else + output.to_s + end + end + def self.full_cache_key(cache_key, pipeline = :full) + return unless cache_key && pipeline.is_a?(Symbol) + ["markdown", *cache_key, pipeline] end end diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 2262e5a168a..f34bf7d1d0e 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -14,7 +14,8 @@ module Gitlab def analyze(text, cache_key: nil) references.clear - @document = Gitlab::Markdown.render(text, project: project, cache_key: cache_key) + @pipeline = Gitlab::Markdown.cached?(cache_key, pipeline: :full) ? :full : :plain_markdown + @html = Gitlab::Markdown.render(text, project: project, cache_key: cache_key, pipeline: @pipeline) end %i(user label issue merge_request snippet commit commit_range).each do |type| @@ -45,14 +46,19 @@ module Gitlab filter = Gitlab::Markdown.const_get(klass) context = { - project: project, - current_user: current_user, + pipeline: [:reference_extraction], + + project: project, + current_user: current_user, + + # ReferenceGathererFilter load_lazy_references: false, reference_filter: filter } - result = self.class.pipeline.call(@document, context) + context[:pipeline].unshift(filter) unless @pipeline == :full + result = Gitlab::Markdown.render_result(@html, context) values = result[:references][filter_type].uniq if @load_lazy_references @@ -61,9 +67,5 @@ module Gitlab values end - - def self.pipeline - @pipeline ||= HTML::Pipeline.new([Gitlab::Markdown::ReferenceGathererFilter]) - end end end diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index 03e36fd3552..e8c8180e1c4 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -50,9 +50,9 @@ module Gitlab filtered_html = 'ASCII' allow(Asciidoctor).to receive(:convert).and_return(html) - expect_any_instance_of(HTML::Pipeline).to receive(:call) - .with(html, context) - .and_return(output: Nokogiri::HTML.fragment(filtered_html)) + expect(Gitlab::Markdown).to receive(:render) + .with(html, context.merge(pipeline: :asciidoc)) + .and_return(filtered_html) expect( render('foo', context) ).to eql filtered_html end -- cgit v1.2.1 From 925dc5925b672718ea1f0f08c5bdd492b5ab3e5d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 14 Oct 2015 21:29:35 +0200 Subject: Cache rendered contents of issues, MRs and notes --- app/models/concerns/issuable.rb | 3 +- app/models/concerns/mentionable.rb | 39 +++++++++++++--------- app/models/note.rb | 2 +- app/views/projects/issues/show.html.haml | 2 +- .../projects/merge_requests/show/_mr_box.html.haml | 2 +- app/views/projects/notes/_note.html.haml | 2 +- lib/gitlab/markdown.rb | 12 ++++++- lib/gitlab/reference_extractor.rb | 20 +++++------ 8 files changed, 51 insertions(+), 31 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index feee8460b86..e260eae8a43 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -46,7 +46,8 @@ module Issuable allow_nil: true, prefix: true - attr_mentionable :title, :description + attr_mentionable :title + attr_mentionable :description, cache: true participant :author, :assignee, :notes end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 715fc6f689d..ad432144bd9 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -10,8 +10,9 @@ module Mentionable module ClassMethods # Indicate which attributes of the Mentionable to search for GFM references. - def attr_mentionable(*attrs) - mentionable_attrs.concat(attrs.map(&:to_s)) + def attr_mentionable(attr, options = {}) + attr = attr.to_s + mentionable_attrs << [attr, options] end # Accessor for attributes marked mentionable. @@ -37,11 +38,6 @@ module Mentionable "#{friendly_name} #{to_reference(from_project)}" end - # Construct a String that contains possible GFM references. - def mentionable_text - self.class.mentionable_attrs.map { |attr| send(attr) }.compact.join("\n\n") - end - # The GFM reference to this Mentionable, which shouldn't be included in its #references. def local_reference self @@ -54,20 +50,33 @@ module Mentionable end def mentioned_users(current_user = nil, load_lazy_references: true) - return [] if mentionable_text.blank? - + # TODO: Douwe: Will be simplified when the "Simplify ..." MR is merged. ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references) - ext.analyze(mentionable_text) - ext.users.uniq + self.class.mentionable_attrs.each do |attr, options| + text = send(attr) + cache_key = [self, attr] if options[:cache] + ext.analyze(text, cache_key: cache_key, pipeline: options[:pipeline]) + end + ext.users end # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. - def references(p = project, current_user = self.author, text = mentionable_text, load_lazy_references: true) + def references(p = project, current_user = self.author, text = nil, load_lazy_references: true) return [] if text.blank? ext = Gitlab::ReferenceExtractor.new(p, current_user, load_lazy_references: load_lazy_references) - ext.analyze(text) - (ext.issues + ext.merge_requests + ext.commits).uniq - [local_reference] + + if text + ext.analyze(text) + else + self.class.mentionable_attrs.each do |attr, options| + text = send(attr) + cache_key = [self, attr] if options[:cache] + ext.analyze(text, cache_key: cache_key) + end + end + + (ext.issues + ext.merge_requests + ext.commits) - [local_reference] end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. @@ -111,7 +120,7 @@ module Mentionable def detect_mentionable_changes source = (changes.present? ? changes : previous_changes).dup - mentionable = self.class.mentionable_attrs + mentionable = self.class.mentionable_attrs.map { |attr, options| attr } # Only include changed fields that are mentionable source.select { |key, val| mentionable.include?(key) } diff --git a/app/models/note.rb b/app/models/note.rb index 2fbe4784159..b4d6ddba703 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -28,7 +28,7 @@ class Note < ActiveRecord::Base default_value_for :system, false - attr_mentionable :note + attr_mentionable :note, cache: true, pipeline: :note participant :author belongs_to :project diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 3233c6884cc..e27f20bdfb8 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -43,7 +43,7 @@ .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} .wiki = preserve do - = markdown(@issue.description) + = markdown(@issue.description, cache_key: [@issue, "description"]) %textarea.hidden.js-task-list-field = @issue.description diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 448230a377c..9bfe202589e 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -7,6 +7,6 @@ .description{class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : ''} .wiki = preserve do - = markdown(@merge_request.description) + = markdown(@merge_request.description, cache_key: [@merge_request, "description"]) %textarea.hidden.js-task-list-field = @merge_request.description diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 1638ad6891a..bcb42d39c91 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -58,7 +58,7 @@ .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-text = preserve do - = markdown(note.note, {no_header_anchors: true}) + = markdown(note.note, pipeline: :note, cache_key: [note, "note"]) - unless note.system? -# System notes can't be edited = render 'projects/notes/edit_form', note: note diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index d1f22070cba..b9615f95012 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -20,6 +20,8 @@ module Gitlab # # Returns an HTML-safe String def self.render(text, context = {}) + context[:pipeline] ||= :full + cache_key = context.delete(:cache_key) cache_key = full_cache_key(cache_key, context[:pipeline]) @@ -33,7 +35,7 @@ module Gitlab end def self.render_result(text, context = {}) - pipeline = context[:pipeline] || :full + pipeline = context[:pipeline] ||= :full html_pipeline = html_pipelines[pipeline] @@ -129,6 +131,7 @@ module Gitlab atom: :full, email: :full, description: :full, + note: :full, single_line: :gfm, asciidoc: [ @@ -170,6 +173,13 @@ module Gitlab only_path: false } ], + note: [ + :full, + { + # TableOfContentsFilter + no_header_anchors: true + } + ], description: [ :full, { diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index f34bf7d1d0e..37141efb4c3 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -9,13 +9,12 @@ module Gitlab @project = project @current_user = current_user @load_lazy_references = load_lazy_references - end - def analyze(text, cache_key: nil) - references.clear + @texts = [] + end - @pipeline = Gitlab::Markdown.cached?(cache_key, pipeline: :full) ? :full : :plain_markdown - @html = Gitlab::Markdown.render(text, project: project, cache_key: cache_key, pipeline: @pipeline) + def analyze(text, options = {}) + @texts << Gitlab::Markdown.render(text, options.merge(project: project)) end %i(user label issue merge_request snippet commit commit_range).each do |type| @@ -46,7 +45,7 @@ module Gitlab filter = Gitlab::Markdown.const_get(klass) context = { - pipeline: [:reference_extraction], + pipeline: :reference_extraction, project: project, current_user: current_user, @@ -56,10 +55,11 @@ module Gitlab reference_filter: filter } - context[:pipeline].unshift(filter) unless @pipeline == :full - - result = Gitlab::Markdown.render_result(@html, context) - values = result[:references][filter_type].uniq + values = @texts.flat_map do |html| + text_context = context.dup + result = Gitlab::Markdown.render_result(html, text_context) + result[:references][filter_type] + end.uniq if @load_lazy_references values = Gitlab::Markdown::ReferenceFilter::LazyReference.load(values).uniq -- cgit v1.2.1 From 528d2823e9888ae09aa03b72c2e61cd0b9a39937 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 14 Oct 2015 21:52:41 +0200 Subject: Remove unused Gitlab::Markdown#cached? method --- lib/gitlab/markdown.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b9615f95012..5c4481e6497 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -45,11 +45,6 @@ module Gitlab html_pipeline.call(text, context) end - def self.cached?(cache_key, pipeline: :full) - cache_key = full_cache_key(cache_key, pipeline) - cache_key ? Rails.cache.exist?(cache_key) : false - end - # Perform post-processing on an HTML String # # This method is used to perform state-dependent changes to a String of -- cgit v1.2.1 From f7ed469ece5e113273b006dd64cc0d29243cfcde Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Thu, 15 Oct 2015 15:24:26 +1100 Subject: change capitalisation in buttons --- app/views/projects/snippets/_actions.html.haml | 6 ++++-- app/views/projects/snippets/show.html.haml | 2 +- app/views/snippets/_actions.html.haml | 7 +++++-- app/views/snippets/show.html.haml | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml index 4d5f7026373..4a515469422 100644 --- a/app/views/projects/snippets/_actions.html.haml +++ b/app/views/projects/snippets/_actions.html.haml @@ -1,8 +1,10 @@ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do = icon('plus') - new snippet + New Snippet - if can?(current_user, :admin_project_snippet, @snippet) - = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' + = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do + = icon('trash-o') + Delete - if can?(current_user, :update_project_snippet, @snippet) = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do = icon('pencil-square-o') diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 4f236fcc7e2..b6a8e68af22 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -9,6 +9,6 @@ = @snippet.file_name .file-actions .btn-group - = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" + = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml index a4fdf89a4ee..751fafa8942 100644 --- a/app/views/snippets/_actions.html.haml +++ b/app/views/snippets/_actions.html.haml @@ -1,7 +1,10 @@ = link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do = icon('plus') - new snippet -= link_to "remove", snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' + New Snippet +- if can?(current_user, :admin_personal_snippet, @snippet) + = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do + = icon('trash-o') + Delete - if can?(current_user, :update_personal_snippet, @snippet) = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do = icon('pencil-square-o') diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 202a43bc57d..612d7b65d8d 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -8,5 +8,5 @@ = @snippet.file_name .file-actions .btn-group - = link_to "raw", raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" + = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" = render 'shared/snippets/blob' -- cgit v1.2.1 From 45e11d95f27584f699392fad8f54c1807e562d7f Mon Sep 17 00:00:00 2001 From: Han Loong Liauw Date: Thu, 15 Oct 2015 17:01:17 +1100 Subject: fix spinach features to use new button wordings Also fixed an accidental deletion of pratial --- app/views/projects/snippets/show.html.haml | 1 + features/project/snippets.feature | 2 +- features/snippets/snippets.feature | 2 +- features/steps/project/snippets.rb | 6 +++--- features/steps/snippets/snippets.rb | 6 +++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index b6a8e68af22..1aeb0da2016 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -11,4 +11,5 @@ .btn-group = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" + = render 'shared/snippets/blob' %div#notes= render "projects/notes/notes_with_form" diff --git a/features/project/snippets.feature b/features/project/snippets.feature index 77e42a1a38b..270557cbde7 100644 --- a/features/project/snippets.feature +++ b/features/project/snippets.feature @@ -30,5 +30,5 @@ Feature: Project Snippets Scenario: I destroy "Snippet one" Given I visit snippet page "Snippet one" - And I click link "Remove Snippet" + And I click link "Delete" Then I should not see "Snippet one" in snippets diff --git a/features/snippets/snippets.feature b/features/snippets/snippets.feature index 4f617b6bed8..e15d7c79342 100644 --- a/features/snippets/snippets.feature +++ b/features/snippets/snippets.feature @@ -24,7 +24,7 @@ Feature: Snippets Scenario: I destroy "Personal snippet one" Given I visit snippet page "Personal snippet one" - And I click link "Destroy" + And I click link "Delete" Then I should not see "Personal snippet one" in snippets Scenario: I create new internal snippet diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb index db8ad08bb9e..811ded69558 100644 --- a/features/steps/project/snippets.rb +++ b/features/steps/project/snippets.rb @@ -42,13 +42,13 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps end step 'I click link "Edit"' do - page.within ".file-title" do + page.within ".page-title" do click_link "Edit" end end - step 'I click link "Remove Snippet"' do - click_link "remove" + step 'I click link "Delete"' do + click_link "Delete" end step 'I submit new snippet "Snippet three"' do diff --git a/features/steps/snippets/snippets.rb b/features/steps/snippets/snippets.rb index 6ff48e0c6b8..80d1ddeef05 100644 --- a/features/steps/snippets/snippets.rb +++ b/features/steps/snippets/snippets.rb @@ -13,13 +13,13 @@ class Spinach::Features::Snippets < Spinach::FeatureSteps end step 'I click link "Edit"' do - page.within ".file-title" do + page.within ".page-title" do click_link "Edit" end end - step 'I click link "Destroy"' do - click_link "remove" + step 'I click link "Delete"' do + click_link "Delete" end step 'I submit new snippet "Personal snippet three"' do -- cgit v1.2.1 From 9c2214f202e98d0427d86a57888574327a6607dd Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 15 Oct 2015 13:41:03 +0200 Subject: Remove stray conflict marker --- app/models/concerns/participable.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index f5f973c0e69..85367f89f4f 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -37,7 +37,6 @@ module Participable # Be aware that this method makes a lot of sql queries. # Save result into variable if you are going to reuse it inside same request -<<<<<<< HEAD def participants(current_user = self.author, load_lazy_references: true) participants = self.class.participant_attrs.flat_map do |attr| value = -- cgit v1.2.1 From 3f08e4b186bd02b37f34ccf1bc641a95f9d865ce Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 1 Oct 2015 20:34:23 +0200 Subject: Add specs on #license --- CHANGELOG | 2 +- app/models/repository.rb | 6 +++++- spec/models/repository_spec.rb | 11 +++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3d0beb0b7ad..f274942f7b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -62,6 +62,7 @@ v 8.0.4 - Fix "Assign All" button on Runner admin page - Fix search in Files - Add full project namespace to payload of system webhooks (Ricardo Band) + - Accept COPYING as licence file (Zeger-Jan van de Weg) v 8.0.3 - Fix URL shown in Slack notifications @@ -82,7 +83,6 @@ v 8.0.2 - Fix Reply by email for non-UTF-8 messages. - Add option to use StartTLS with Reply by email IMAP server. - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie) - - Accept COPYING as licence file (Zeger-Jan van de Weg) v 8.0.1 - Remove git refs used internally by GitLab from network graph (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index dc7cd926745..8cd182e1b0b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -216,7 +216,11 @@ class Repository # If `licence`, `copying` and `copying.lesser` are found, return in the # following order: licence, copying, copying.lesser - licenses.find { |l| l =~ /\Alicence/i } || licenses.sort.first + regex = [/\Alicense(\..*)?\z/i, /\Acopying(\..{0,3})?\z/i, /\Acopying.lesser/i] + + regex.map do |re| + licenses.find { |l| l.name =~ re } + end.compact.first end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 05e51532eb8..60b93988476 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -91,4 +91,15 @@ describe Repository do it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end end + + describe "#license" do + it 'test selection preference' do + repository.send(:cache).expire(:license) + TestBlob = Struct.new(:name) + files = [TestBlob.new('file'), TestBlob.new('license'), TestBlob.new('copying')] + expect(repository.tree).to receive(:blobs).and_return(files) + + expect(repository.license.name).to eq('license') + end + end end -- cgit v1.2.1 From 58260a0327a953499a07e9cad8d9aaad2d25699b Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 16 Oct 2015 17:16:17 +0200 Subject: Do no rely on basename of builds, uploads --- lib/backup/builds.rb | 6 +++++- lib/backup/files.rb | 9 +++++---- lib/backup/uploads.rb | 6 +++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index d269f8e260c..800f30c2144 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -1,7 +1,11 @@ module Backup class Builds < Files def initialize - super(Settings.gitlab_ci.builds_path) + super('builds', Settings.gitlab_ci.builds_path) + end + + def create_files_dir + Dir.mkdir(app_files_dir, 0700) end end end diff --git a/lib/backup/files.rb b/lib/backup/files.rb index 5a210a0e464..654b4d1c896 100644 --- a/lib/backup/files.rb +++ b/lib/backup/files.rb @@ -4,9 +4,9 @@ module Backup class Files attr_reader :name, :app_files_dir, :backup_tarball, :files_parent_dir - def initialize(app_files_dir) + def initialize(name, app_files_dir) + @name = name @app_files_dir = File.realpath(app_files_dir) - @name = File.basename(app_files_dir) @files_parent_dir = File.realpath(File.join(@app_files_dir, '..')) @backup_tarball = File.join(Gitlab.config.backup.path, name + '.tar.gz') end @@ -15,13 +15,14 @@ module Backup def dump FileUtils.mkdir_p(Gitlab.config.backup.path) FileUtils.rm_f(backup_tarball) - run_pipeline!([%W(tar -C #{files_parent_dir} -cf - #{name}), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) + run_pipeline!([%W(tar -C #{app_files_dir} -cf - .), %W(gzip -c -1)], out: [backup_tarball, 'w', 0600]) end def restore backup_existing_files_dir + create_files_dir - run_pipeline!([%W(gzip -cd), %W(tar -C #{files_parent_dir} -xf -)], in: backup_tarball) + run_pipeline!([%W(gzip -cd), %W(tar -C #{app_files_dir} -xf -)], in: backup_tarball) end def backup_existing_files_dir diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index 7c0838cc8b7..0a0ec564ba4 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -2,7 +2,11 @@ module Backup class Uploads < Files def initialize - super(Rails.root.join('public/uploads')) + super('uploads', Rails.root.join('public/uploads')) + end + + def create_files_dir + Dir.mkdir(app_files_dir) end end end -- cgit v1.2.1 From 5f47b61cef9c110c09eab57a9e5f8f32654f21bd Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 17 Oct 2015 07:19:04 -0700 Subject: Use a relative link instead of full URL with New Issue button to be consistent Relates to #3095 --- app/views/projects/buttons/_dropdown.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 4580c912692..fa8c2c599a7 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -5,7 +5,7 @@ %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - if can?(current_user, :create_issue, @project) %li - = link_to url_for_new_issue do + = link_to url_for_new_issue(@project, only_path: true) do = icon('exclamation-circle fw') New issue - if can?(current_user, :create_merge_request, @project) -- cgit v1.2.1 From b371b6d13974fe8db3601e68922db0b735049a17 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 18 Oct 2015 12:54:02 +0200 Subject: Update style of snippet detail page --- CHANGELOG | 2 +- app/assets/stylesheets/pages/snippets.scss | 62 ++++++++++++++--------------- app/views/projects/snippets/show.html.haml | 29 ++++++++------ app/views/shared/snippets/_header.html.haml | 47 +++++++++++----------- app/views/snippets/show.html.haml | 22 +++++----- 5 files changed, 83 insertions(+), 79 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8b225d264e8..df4daddeac3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -59,7 +59,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) - - Added last modified date to snippets#show (Han Loong Liauw) + - Update style of snippet detail page (Han Loong Liauw) - Allow dashboard and group issues/MRs to be filtered by label - Add spellcheck=false to certain input fields - Invalidate stored service password if the endpoint URL is changed diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index af391481764..f8a8636818a 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -31,50 +31,50 @@ } } -.snippet-details { - .page-title { - margin-top: -15px; - padding: 10px 0; - margin-bottom: 0; - color: #5c5d5e; - font-size: 13px; - @include clearfix(); +.snippet-holder { + .snippet-details { + .page-title { + margin-top: -15px; + padding: 10px 0; + margin-bottom: 0; + color: #5c5d5e; + font-size: 16px; - .creator { - color: $gl-gray; - a { - color: $gl-gray; + .author { + color: #5c5d5e; } - } - .snippet-id { - color: #5c5d5e; + .snippet-id { + color: #5c5d5e; + } } - .btn { - padding: 10px $gl-padding; + + .snippet-title { + margin: 0; + font-size: 23px; + color: #313236; } - } - .snippet-title { - margin: 0; - font-size: 23px; - color: #313236; - } + @media (max-width: $screen-md-max) { + .new-snippet-link { + display: none; + } + } - @media (max-width: $screen-md-max) { - .new-snippet-link { - display: none; + @media (max-width: $screen-sm-max) { + .creator, + .page-title .btn-close { + display: none; + } } } - @media (max-width: $screen-sm-max) { - .creator, - .page-title .btn-close { - display: none; - } + .file-holder { + border-top: 0; } } + .snippet-box { @include border-radius(2px); diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 1aeb0da2016..5d706942f2d 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -1,15 +1,18 @@ - page_title @snippet.title, "Snippets" = render "header_title" -= render 'shared/snippets/header' - -.file-holder - .file-title - %i.fa.fa-file - %strong - = @snippet.file_name - .file-actions - .btn-group - = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" - - = render 'shared/snippets/blob' -%div#notes= render "projects/notes/notes_with_form" + +.snippet-holder + = render 'shared/snippets/header' + + %article.file-holder + .file-title + = blob_icon 0, @snippet.file_name + %strong + = @snippet.file_name + .file-actions.hidden-xs + .btn-group.tree-btn-group + = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" + + = render 'shared/snippets/blob' + + %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 95786ee3377..0a4a790ec5e 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,25 +1,24 @@ -.snippet - .snippet-details - .page-title - .snippet-box{class: visibility_level_color(@snippet.visibility_level)} - = visibility_level_icon(@snippet.visibility_level) - = visibility_level_label(@snippet.visibility_level) - %span.snippet-id Snippet ##{@snippet.id} - %span.creator - · created by #{link_to_member(@project, @snippet.author, size: 24)} - · - = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') - - if @snippet.updated_at != @snippet.created_at - %span - · - = icon('edit', title: 'edited') - = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') +.snippet-details + .page-title + .snippet-box{class: visibility_level_color(@snippet.visibility_level)} + = visibility_level_icon(@snippet.visibility_level) + = visibility_level_label(@snippet.visibility_level) + %span.snippet-id Snippet ##{@snippet.id} + %span.creator + · created by #{link_to_member(@project, @snippet.author, size: 24)} + · + = time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago') + - if @snippet.updated_at != @snippet.created_at + %span + · + = icon('edit', title: 'edited') + = time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago') - .pull-right - - if @snippet.project_id? - = render "projects/snippets/actions" - - else - = render "snippets/actions" - .gray-content-block.middle-block - %h2.snippet-title - = gfm escape_once(@snippet.title) + .pull-right + - if @snippet.project_id? + = render "projects/snippets/actions" + - else + = render "snippets/actions" + .gray-content-block.middle-block + %h2.snippet-title + = gfm escape_once(@snippet.title) diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 612d7b65d8d..69d8899d4c1 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -1,12 +1,14 @@ - page_title @snippet.title, "Snippets" -= render 'shared/snippets/header' -.file-holder - .file-title - %i.fa.fa-file - %strong - = @snippet.file_name - .file-actions - .btn-group - = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" - = render 'shared/snippets/blob' +.snippet-holder + = render 'shared/snippets/header' + + %article.file-holder + .file-title + = blob_icon 0, @snippet.file_name + %strong + = @snippet.file_name + .file-actions.hidden-xs + .btn-group.tree-btn-group + = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" + = render 'shared/snippets/blob' -- cgit v1.2.1 From 946f00ed7f2b487273bb5dabdb5997da60f1dc92 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 18 Oct 2015 13:03:26 +0200 Subject: Update style of snippets pages --- CHANGELOG | 2 +- app/assets/stylesheets/pages/snippets.scss | 5 ----- app/controllers/projects/snippets_controller.rb | 1 + app/views/dashboard/snippets/index.html.haml | 28 +++---------------------- app/views/explore/snippets/index.html.haml | 3 ++- app/views/projects/snippets/index.html.haml | 20 +++++++----------- 6 files changed, 15 insertions(+), 44 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index df4daddeac3..557216879f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -59,7 +59,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) - - Update style of snippet detail page (Han Loong Liauw) + - Update style of snippets pages (Han Loong Liauw) - Allow dashboard and group issues/MRs to be filtered by label - Add spellcheck=false to certain input fields - Invalidate stored service password if the endpoint URL is changed diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index f8a8636818a..242783a7b7e 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -1,8 +1,3 @@ -.my-snippets li:first-child { - h4 { margin-top: 0; } - padding-top: 0; -} - .snippet-form-holder .file-holder .file-title { padding: 2px; } diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index b07a2a8db2f..2104c7a7a71 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -21,6 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController filter: :by_project, project: @project }) + @snippets = @snippets.page(params[:page]).per(PER_PAGE) end def new diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index d3908062f43..d92149dcc70 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -6,33 +6,11 @@ .gray-content-block .pull-right = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet + = icon('plus') + New Snippet .oneline Share code pastes with others out of git repository -%ul.nav.nav-tabs.prepend-top-20 - = nav_tab :scope, nil do - = link_to dashboard_snippets_path do - All - %span.badge - = current_user.snippets.count - = nav_tab :scope, 'are_private' do - = link_to dashboard_snippets_path(scope: 'are_private') do - Private - %span.badge - = current_user.snippets.are_private.count - = nav_tab :scope, 'are_internal' do - = link_to dashboard_snippets_path(scope: 'are_internal') do - Internal - %span.badge - = current_user.snippets.are_internal.count - = nav_tab :scope, 'are_public' do - = link_to dashboard_snippets_path(scope: 'are_public') do - Public - %span.badge - = current_user.snippets.are_public.count - -.my-snippets - = render 'snippets/snippets' += render 'snippets/snippets' diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml index 7e4fa7d4873..0f100c39ffb 100644 --- a/app/views/explore/snippets/index.html.haml +++ b/app/views/explore/snippets/index.html.haml @@ -10,7 +10,8 @@ - if current_user .pull-right = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do - Add new snippet + = icon('plus') + New Snippet .oneline Public snippets created by you and other users are listed here diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml index 3fed2c9949d..4af963e14da 100644 --- a/app/views/projects/snippets/index.html.haml +++ b/app/views/projects/snippets/index.html.haml @@ -1,17 +1,13 @@ - page_title "Snippets" = render "header_title" -%h3.page-title - Snippets - - if can? current_user, :create_project_snippet, @project - = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do - Add new snippet +.gray-content-block.top-block + .pull-right + = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do + = icon('plus') + New Snippet -%p.light - Share code pastes with others out of git repository + .oneline + Share code pastes with others out of git repository -%ul.bordered-list - = render partial: "shared/snippets/snippet", collection: @snippets - - if @snippets.empty? - %li - .nothing-here-block Nothing here. += render 'snippets/snippets' -- cgit v1.2.1 From 44df201280ce67ac704c31e7cc24d5c6b34cff90 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 18 Oct 2015 14:17:15 +0200 Subject: Restore dashboard snippets tabs. --- app/views/dashboard/snippets/index.html.haml | 22 ++++++++++++++++++++++ features/steps/snippets/user.rb | 6 +++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index d92149dcc70..7ac3a12baf9 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -12,5 +12,27 @@ .oneline Share code pastes with others out of git repository +%ul.center-top-menu.no-top.no-bottom.snippet-scope-menu + = nav_tab :scope, nil do + = link_to dashboard_snippets_path do + All + %span.badge + = current_user.snippets.count + = nav_tab :scope, 'are_private' do + = link_to dashboard_snippets_path(scope: 'are_private') do + Private + %span.badge + = current_user.snippets.are_private.count + = nav_tab :scope, 'are_internal' do + = link_to dashboard_snippets_path(scope: 'are_internal') do + Internal + %span.badge + = current_user.snippets.are_internal.count + = nav_tab :scope, 'are_public' do + = link_to dashboard_snippets_path(scope: 'are_public') do + Public + %span.badge + = current_user.snippets.are_public.count + = render 'snippets/snippets' diff --git a/features/steps/snippets/user.rb b/features/steps/snippets/user.rb index dea3256229f..997c605bce2 100644 --- a/features/steps/snippets/user.rb +++ b/features/steps/snippets/user.rb @@ -32,19 +32,19 @@ class Spinach::Features::SnippetsUser < Spinach::FeatureSteps end step 'I click "Internal" filter' do - page.within('.nav-tabs') do + page.within('.snippet-scope-menu') do click_link "Internal" end end step 'I click "Private" filter' do - page.within('.nav-tabs') do + page.within('.snippet-scope-menu') do click_link "Private" end end step 'I click "Public" filter' do - page.within('.nav-tabs') do + page.within('.snippet-scope-menu') do click_link "Public" end end -- cgit v1.2.1 From d9944fdb59fe286022130f133f316b80dec04ea6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Sun, 18 Oct 2015 15:36:02 +0200 Subject: Fix spec --- features/steps/project/snippets.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/project/snippets.rb b/features/steps/project/snippets.rb index 811ded69558..a3aef9bf8c3 100644 --- a/features/steps/project/snippets.rb +++ b/features/steps/project/snippets.rb @@ -22,7 +22,7 @@ class Spinach::Features::ProjectSnippets < Spinach::FeatureSteps end step 'I click link "New Snippet"' do - click_link "Add new snippet" + click_link "New Snippet" end step 'I click link "Snippet one"' do -- cgit v1.2.1 From 7d7b6dbb9f2a8c6dbee8ab6eb0ab075705abcfbd Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Mon, 19 Oct 2015 01:21:21 +0200 Subject: Update spec --- spec/helpers/application_helper_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 742420f550e..b5a29346dcd 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -95,9 +95,9 @@ describe ApplicationHelper do end it 'should call gravatar_icon when no User exists with the given email' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20) + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) - helper.avatar_icon('foo@example.com', 20) + helper.avatar_icon('foo@example.com', 20, 2) end end -- cgit v1.2.1 From 34d0cd438a7173c35c687ecb37eae1f3293b84b5 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Mon, 19 Oct 2015 01:22:20 +0200 Subject: Update spec --- spec/helpers/application_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index e6e9bc99a16..bed6658ce07 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -95,7 +95,7 @@ describe ApplicationHelper do end it 'should call gravatar_icon when no User exists with the given email' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20) + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) helper.avatar_icon('foo@example.com', 20) end -- cgit v1.2.1 From ab6d426a480ef45ba3d13db3049294304b33f6c0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 19 Oct 2015 21:04:05 +0300 Subject: Add missing dot in confirm modal --- app/views/shared/_confirm_modal.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 5f51b0d450f..2a44817e05a 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -14,7 +14,7 @@ %br Please type %code.js-confirm-danger-match #{phrase} - to proceed or close this modal to cancel + to proceed or close this modal to cancel. .form-group = text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input' -- cgit v1.2.1 From 3d50b99d016af91c06a40f7a8bf4c298e241220a Mon Sep 17 00:00:00 2001 From: Dirceu Pereira Tiegs Date: Mon, 19 Oct 2015 20:25:35 -0200 Subject: Add option to create merge request when editing/creating a file --- app/assets/javascripts/blob/edit_blob.js.coffee | 7 ++++++ app/assets/javascripts/blob/new_blob.js.coffee | 7 ++++++ app/assets/stylesheets/pages/editor.scss | 4 ++++ app/controllers/application_controller.rb | 30 +++++++++++++++++++++++++ app/controllers/projects/blob_controller.rb | 18 +++++++++++++-- app/helpers/merge_requests_helper.rb | 28 ----------------------- app/views/projects/blob/edit.html.haml | 8 +++++++ app/views/projects/blob/new.html.haml | 8 +++++++ 8 files changed, 80 insertions(+), 30 deletions(-) diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 050888f9c15..a6f65642d78 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,6 +11,13 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode + $('#new_branch').keyup -> + if $(this).val() != $('#original_branch').val() + $('.form-group.destination').show() + else + $('.form-group.destination').hide() + $('#create_merge_request').prop('checked', false) + $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index 1f36a53f191..f4e06ad088b 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,6 +11,13 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode + $('#new_branch').keyup -> + if $(this).val() != $('#original_branch').val() + $('.form-group.destination').show() + else + $('.form-group.destination').hide() + $('#create_merge_request').prop('checked', false) + $(".js-commit-button").click -> $("#file-content").val editor.getValue() $(".file-editor form").submit() diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 1d565477dd4..6bc9b1c3eaf 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -63,4 +63,8 @@ margin-top: 0; padding: $gl-padding } + + .destination { + display: none; + } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f0124c6bd60..258e37e98c0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,6 +22,7 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? + helper_method :new_mr_from_push_event, :new_mr_path_for_fork_from_push_event, :new_mr_path_from_push_event rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -343,4 +344,33 @@ class ApplicationController < ActionController::Base def git_import_enabled? current_application_settings.import_sources.include?('git') end + + # new merge requests routing helpers + def new_mr_path_from_push_event(event, target_branch=nil) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project, target_branch) + ) + end + + def new_mr_path_for_fork_from_push_event(event, target_branch=nil) + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, event.project.forked_from_project, target_branch) + ) + end + + def new_mr_from_push_event(event, target_project, target_branch) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_branch || target_project.repository.root_ref + } + } + end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 8cc2f21d887..f49c094b591 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -27,7 +27,14 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success 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.html do + url = if params[:create_merge_request] + new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) + else + namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) + end + redirect_to url + end format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } end else @@ -52,7 +59,14 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" respond_to do |format| - format.html { redirect_to after_edit_path } + format.html do + url = if params[:create_merge_request] + new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) + else + after_edit_path + end + redirect_to url + end format.json { render json: { message: "success", filePath: after_edit_path } } end else diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 728d877ace2..7e8f61fd274 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,32 +1,4 @@ module MergeRequestsHelper - def new_mr_path_from_push_event(event) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project) - ) - end - - def new_mr_path_for_fork_from_push_event(event) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project) - ) - end - - def new_mr_from_push_event(event, target_project) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_project.repository.root_ref - } - } - end - def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index a811adc5094..26216462707 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -23,9 +23,17 @@ .col-sm-10 = text_field_tag 'new_branch', @ref, class: "form-control" + .form-group.destination + .col-sm-offset-2.col-sm-10 + .checkbox + = label_tag :create_merge_request do + = check_box_tag :create_merge_request, 1, false + Start a new merge request + = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] + = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path :javascript diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 7975137c37f..0439e55131e 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -17,7 +17,15 @@ .col-sm-10 = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit" + .form-group.destination + .col-sm-offset-2.col-sm-10 + .checkbox + = label_tag :create_merge_request do + = check_box_tag :create_merge_request, 1, false + Start a new merge request + = hidden_field_tag 'content', '', id: 'file-content' + = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) -- cgit v1.2.1 From 2f7fc7e9f7e7a43914abe81a510bd0dffa113979 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 20 Oct 2015 16:16:08 +0200 Subject: Prefer project with exact path to differently cased one when both exist. --- app/controllers/application_controller.rb | 4 ++-- app/models/project.rb | 17 +++++++++++----- spec/controllers/projects_controller_spec.rb | 30 ++++++++++++++++++++++------ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f0124c6bd60..38e6b44eb6f 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -118,8 +118,8 @@ class ApplicationController < ActionController::Base end project_path = "#{namespace}/#{id}" - @project = Project.find_with_namespace(project_path) - + @project = Project.find_with_namespace(project_path) || + Project.find_with_namespace(project_path, case_sensitive: false) if @project and can?(current_user, :read_project, @project) if @project.path_with_namespace != project_path diff --git a/app/models/project.rb b/app/models/project.rb index 88cd88dcb5a..b2a8dde9ba2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -235,7 +235,7 @@ class Project < ActiveRecord::Base where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%") end - def find_with_namespace(id) + def find_with_namespace(id, case_sensitive: true) namespace_path, project_path = id.split('/') return nil if !namespace_path || !project_path @@ -243,11 +243,18 @@ class Project < ActiveRecord::Base # 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. + projects = unscoped. joins(:namespace). - iwhere('namespaces.path' => namespace_path). - iwhere('projects.path' => project_path). - take + iwhere('namespaces.path' => namespace_path) + + projects = + if case_sensitive + projects.where('projects.path' => project_path) + else + projects.iwhere('projects.path' => project_path) + end + + projects.take end def visibility_levels diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4460bf12f96..5090f87c73d 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -51,16 +51,34 @@ describe ProjectsController do end context "when requested with case sensitive namespace and project path" do - it "redirects to the normalized path for case mismatch" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + context "when there is a match with the same casing" do + it "loads the project" do + get :show, namespace_id: public_project.namespace.path, id: public_project.path - expect(response).to redirect_to("/#{public_project.path_with_namespace}") + expect(assigns(:project)).to eq(public_project) + expect(response.status).to eq(200) + end end - it "loads the page if normalized path matches request path" do - get :show, namespace_id: public_project.namespace.path, id: public_project.path + context "when there is a match with different casing" do + it "redirects to the normalized path" do + get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + + expect(assigns(:project)).to eq(public_project) + expect(response).to redirect_to("/#{public_project.path_with_namespace}") + end - expect(response.status).to eq(200) + context "when there is also a match with the same casing" do + + let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } + + it "loads the exactly matched project" do + get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + + expect(assigns(:project)).to eq(other_project) + expect(response.status).to eq(200) + end + end end end end -- cgit v1.2.1 From e17e5a5ce462022761d3cdc29d677f968ca9738a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 10:09:32 +0200 Subject: Move case sensitivity check to find_with_namespace. --- app/controllers/application_controller.rb | 3 +-- app/models/project.rb | 12 +++--------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 38e6b44eb6f..1a47a3c0ee3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -118,8 +118,7 @@ class ApplicationController < ActionController::Base end project_path = "#{namespace}/#{id}" - @project = Project.find_with_namespace(project_path) || - Project.find_with_namespace(project_path, case_sensitive: false) + @project = Project.find_with_namespace(project_path) if @project and can?(current_user, :read_project, @project) if @project.path_with_namespace != project_path diff --git a/app/models/project.rb b/app/models/project.rb index b2a8dde9ba2..f9bed7cf89f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -235,7 +235,7 @@ class Project < ActiveRecord::Base where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%") end - def find_with_namespace(id, case_sensitive: true) + def find_with_namespace(id) namespace_path, project_path = id.split('/') return nil if !namespace_path || !project_path @@ -247,14 +247,8 @@ class Project < ActiveRecord::Base joins(:namespace). iwhere('namespaces.path' => namespace_path) - projects = - if case_sensitive - projects.where('projects.path' => project_path) - else - projects.iwhere('projects.path' => project_path) - end - - projects.take + projects.where('projects.path' => project_path).take || + projects.iwhere('projects.path' => project_path).take end def visibility_levels -- cgit v1.2.1 From 4a02dfa48f46688acdf03d2901cb4f36364dccdf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 10:09:40 +0200 Subject: Disable case sensitive spec for MySQL. --- spec/controllers/projects_controller_spec.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 5090f87c73d..0fe9d4da848 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -73,6 +73,9 @@ describe ProjectsController do let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } it "loads the exactly matched project" do + # MySQL queries are case insensitive by default, so this spec would fail. + skip if Gitlab::Database.mysql? + get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase expect(assigns(:project)).to eq(other_project) -- cgit v1.2.1 From 310f49af35dad496041b21bbb8b05c74c14075ba Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 10:21:01 +0200 Subject: Don't load entire spec with MySQL --- spec/controllers/projects_controller_spec.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 0fe9d4da848..9b49f60b66c 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -68,18 +68,20 @@ describe ProjectsController do expect(response).to redirect_to("/#{public_project.path_with_namespace}") end - context "when there is also a match with the same casing" do - let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } + # MySQL queries are case insensitive by default, so this spec would fail. + unless Gitlab::Database.mysql? + context "when there is also a match with the same casing" do - it "loads the exactly matched project" do - # MySQL queries are case insensitive by default, so this spec would fail. - skip if Gitlab::Database.mysql? + let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } - get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + it "loads the exactly matched project" do - expect(assigns(:project)).to eq(other_project) - expect(response.status).to eq(200) + get :show, namespace_id: public_project.namespace.path, id: public_project.path.upcase + + expect(assigns(:project)).to eq(other_project) + expect(response.status).to eq(200) + end end end end -- cgit v1.2.1 From de9886bece6455f2a35ce5128cb5444fdb5c4659 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 11:10:09 +0200 Subject: Restyle dashboard snippets visibility level tabs. --- app/assets/stylesheets/framework/buttons.scss | 11 +++++++++++ app/assets/stylesheets/framework/mixins.scss | 1 - app/helpers/tab_helper.rb | 18 ------------------ app/views/dashboard/snippets/index.html.haml | 20 ++++++++------------ 4 files changed, 19 insertions(+), 31 deletions(-) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index e5f0c0ad9ef..04024419584 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -162,10 +162,21 @@ border-color: #e7e9ed; width: 140px; + .badge { + font-weight: normal; + background-color: #eee; + color: #78a; + } + &.active { border-color: $gl-info; background: $gl-info; color: #fff; + + .badge { + color: $gl-info; + background-color: white; + } } } } diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 089e6958eeb..1d798344890 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -147,7 +147,6 @@ .badge { font-weight: normal; - background-color: #fff; background-color: #eee; color: #78a; } diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 0e7d8065ac7..04e53fe7c61 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -110,22 +110,4 @@ module TabHelper 'active' end end - - # Use nav_tab for save controller/action but different params - def nav_tab(key, value, &block) - o = {} - o[:class] = "" - - if value.nil? - o[:class] << " active" if params[key].blank? - else - o[:class] << " active" if params[key] == value - end - - if block_given? - content_tag(:li, capture(&block), o) - else - content_tag(:li, nil, o) - end - end end diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index 7ac3a12baf9..2b67fd05b9d 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -9,27 +9,23 @@ = icon('plus') New Snippet - .oneline - Share code pastes with others out of git repository - -%ul.center-top-menu.no-top.no-bottom.snippet-scope-menu - = nav_tab :scope, nil do - = link_to dashboard_snippets_path do + .btn-group.btn-group-next.event-filter + = link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do All %span.badge = current_user.snippets.count - = nav_tab :scope, 'are_private' do - = link_to dashboard_snippets_path(scope: 'are_private') do + + = link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do Private %span.badge = current_user.snippets.are_private.count - = nav_tab :scope, 'are_internal' do - = link_to dashboard_snippets_path(scope: 'are_internal') do + + = link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do Internal %span.badge = current_user.snippets.are_internal.count - = nav_tab :scope, 'are_public' do - = link_to dashboard_snippets_path(scope: 'are_public') do + + = link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do Public %span.badge = current_user.snippets.are_public.count -- cgit v1.2.1 From 0179d5df18dccee6f44908b91a9975352e652498 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 21 Oct 2015 12:15:39 +0200 Subject: Update SourceSansPro fonts Signed-off-by: Dmitriy Zaporozhets --- app/assets/fonts/SourceSansPro-Black.ttf | Bin 148368 -> 289364 bytes app/assets/fonts/SourceSansPro-BlackIt.ttf | Bin 0 -> 103404 bytes app/assets/fonts/SourceSansPro-Bold.ttf | Bin app/assets/fonts/SourceSansPro-BoldIt.ttf | Bin 0 -> 103608 bytes app/assets/fonts/SourceSansPro-ExtraLight.ttf | Bin 150528 -> 291652 bytes app/assets/fonts/SourceSansPro-ExtraLightIt.ttf | Bin 0 -> 104768 bytes app/assets/fonts/SourceSansPro-It.ttf | Bin 0 -> 104236 bytes app/assets/fonts/SourceSansPro-Light.ttf | Bin app/assets/fonts/SourceSansPro-LightIt.ttf | Bin 0 -> 104616 bytes app/assets/fonts/SourceSansPro-Regular.ttf | Bin app/assets/fonts/SourceSansPro-Semibold.ttf | Bin app/assets/fonts/SourceSansPro-SemiboldIt.ttf | Bin 0 -> 104020 bytes 12 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 app/assets/fonts/SourceSansPro-Black.ttf create mode 100644 app/assets/fonts/SourceSansPro-BlackIt.ttf mode change 100755 => 100644 app/assets/fonts/SourceSansPro-Bold.ttf create mode 100644 app/assets/fonts/SourceSansPro-BoldIt.ttf mode change 100755 => 100644 app/assets/fonts/SourceSansPro-ExtraLight.ttf create mode 100644 app/assets/fonts/SourceSansPro-ExtraLightIt.ttf create mode 100644 app/assets/fonts/SourceSansPro-It.ttf mode change 100755 => 100644 app/assets/fonts/SourceSansPro-Light.ttf create mode 100644 app/assets/fonts/SourceSansPro-LightIt.ttf mode change 100755 => 100644 app/assets/fonts/SourceSansPro-Regular.ttf mode change 100755 => 100644 app/assets/fonts/SourceSansPro-Semibold.ttf create mode 100644 app/assets/fonts/SourceSansPro-SemiboldIt.ttf diff --git a/app/assets/fonts/SourceSansPro-Black.ttf b/app/assets/fonts/SourceSansPro-Black.ttf old mode 100755 new mode 100644 index cb89a2d171e..9c9b5cb7f03 Binary files a/app/assets/fonts/SourceSansPro-Black.ttf and b/app/assets/fonts/SourceSansPro-Black.ttf differ diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf b/app/assets/fonts/SourceSansPro-BlackIt.ttf new file mode 100644 index 00000000000..294ce5abe8f Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BlackIt.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf b/app/assets/fonts/SourceSansPro-Bold.ttf old mode 100755 new mode 100644 diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf b/app/assets/fonts/SourceSansPro-BoldIt.ttf new file mode 100644 index 00000000000..3decd130070 Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BoldIt.ttf differ diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf b/app/assets/fonts/SourceSansPro-ExtraLight.ttf old mode 100755 new mode 100644 index bb4176c6fff..253eafa3783 Binary files a/app/assets/fonts/SourceSansPro-ExtraLight.ttf and b/app/assets/fonts/SourceSansPro-ExtraLight.ttf differ diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf new file mode 100644 index 00000000000..00d7e9a7aa8 Binary files /dev/null and b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf differ diff --git a/app/assets/fonts/SourceSansPro-It.ttf b/app/assets/fonts/SourceSansPro-It.ttf new file mode 100644 index 00000000000..f7af5377595 Binary files /dev/null and b/app/assets/fonts/SourceSansPro-It.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Light.ttf b/app/assets/fonts/SourceSansPro-Light.ttf old mode 100755 new mode 100644 diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf b/app/assets/fonts/SourceSansPro-LightIt.ttf new file mode 100644 index 00000000000..f18827985ef Binary files /dev/null and b/app/assets/fonts/SourceSansPro-LightIt.ttf differ diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf b/app/assets/fonts/SourceSansPro-Regular.ttf old mode 100755 new mode 100644 diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf b/app/assets/fonts/SourceSansPro-Semibold.ttf old mode 100755 new mode 100644 diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf new file mode 100644 index 00000000000..13d66a1fc45 Binary files /dev/null and b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf differ -- cgit v1.2.1 From abbca6151d66ebc227c883af35cd01cf4d7e24eb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 12:18:23 +0200 Subject: Make pipelines actually make sense --- lib/gitlab/markdown.rb | 216 ++++----------------- lib/gitlab/markdown/asciidoc_pipeline.rb | 13 ++ lib/gitlab/markdown/atom_pipeline.rb | 14 ++ lib/gitlab/markdown/combined_pipeline.rb | 25 +++ lib/gitlab/markdown/description_pipeline.rb | 14 ++ lib/gitlab/markdown/email_pipeline.rb | 13 ++ lib/gitlab/markdown/full_pipeline.rb | 9 + lib/gitlab/markdown/gfm_pipeline.rb | 41 ++++ lib/gitlab/markdown/note_pipeline.rb | 14 ++ lib/gitlab/markdown/pipeline.rb | 29 +++ lib/gitlab/markdown/plain_markdown_pipeline.rb | 13 ++ lib/gitlab/markdown/post_process_pipeline.rb | 20 ++ .../markdown/reference_extraction_pipeline.rb | 13 ++ lib/gitlab/markdown/single_line_pipeline.rb | 9 + 14 files changed, 265 insertions(+), 178 deletions(-) create mode 100644 lib/gitlab/markdown/asciidoc_pipeline.rb create mode 100644 lib/gitlab/markdown/atom_pipeline.rb create mode 100644 lib/gitlab/markdown/combined_pipeline.rb create mode 100644 lib/gitlab/markdown/description_pipeline.rb create mode 100644 lib/gitlab/markdown/email_pipeline.rb create mode 100644 lib/gitlab/markdown/full_pipeline.rb create mode 100644 lib/gitlab/markdown/gfm_pipeline.rb create mode 100644 lib/gitlab/markdown/note_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline.rb create mode 100644 lib/gitlab/markdown/plain_markdown_pipeline.rb create mode 100644 lib/gitlab/markdown/post_process_pipeline.rb create mode 100644 lib/gitlab/markdown/reference_extraction_pipeline.rb create mode 100644 lib/gitlab/markdown/single_line_pipeline.rb diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 5c4481e6497..3ae4ccfacb6 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -35,14 +35,8 @@ module Gitlab end def self.render_result(text, context = {}) - pipeline = context[:pipeline] ||= :full - - html_pipeline = html_pipelines[pipeline] - - transformers = context_transformers[pipeline] - context = transformers.reduce(context) { |context, transformer| transformer.call(context) } - - html_pipeline.call(text, context) + pipeline_type = context[:pipeline] ||= :full + pipeline_by_type(pipeline_type).call(text, context) end # Perform post-processing on an HTML String @@ -59,17 +53,37 @@ module Gitlab # # Returns an HTML-safe String def self.post_process(html, context) - html_pipeline = html_pipelines[:post_process] + pipeline = pipeline_by_type(:post_process) if context[:xhtml] - html_pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) + pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) else - html_pipeline.to_html(html, context) + pipeline.to_html(html, context) end.html_safe end private + def self.cacheless_render(text, context = {}) + result = render_result(text, context) + output = result[:output] + if output.respond_to?(:to_html) + output.to_html + else + output.to_s + end + end + + def self.full_cache_key(cache_key, pipeline = :full) + return unless cache_key + + ["markdown", *cache_key, pipeline] + end + + def self.pipeline_by_type(pipeline_type) + const_get("#{pipeline_type.to_s.camelize}Pipeline") + end + # Provide autoload paths for filters to prevent a circular dependency error autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter' autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter' @@ -91,172 +105,18 @@ module Gitlab autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' autoload :UploadLinkFilter, 'gitlab/markdown/upload_link_filter' - def self.gfm_filters - @gfm_filters ||= [ - Gitlab::Markdown::SyntaxHighlightFilter, - Gitlab::Markdown::SanitizationFilter, - - Gitlab::Markdown::UploadLinkFilter, - Gitlab::Markdown::EmojiFilter, - Gitlab::Markdown::TableOfContentsFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, - - Gitlab::Markdown::UserReferenceFilter, - Gitlab::Markdown::IssueReferenceFilter, - Gitlab::Markdown::ExternalIssueReferenceFilter, - Gitlab::Markdown::MergeRequestReferenceFilter, - Gitlab::Markdown::SnippetReferenceFilter, - Gitlab::Markdown::CommitRangeReferenceFilter, - Gitlab::Markdown::CommitReferenceFilter, - Gitlab::Markdown::LabelReferenceFilter, - - Gitlab::Markdown::TaskListFilter - ] - end - - def self.all_filters - @all_filters ||= { - plain_markdown: [ - Gitlab::Markdown::MarkdownFilter - ], - gfm: gfm_filters, - - full: [:plain_markdown, :gfm], - atom: :full, - email: :full, - description: :full, - note: :full, - single_line: :gfm, - - asciidoc: [ - Gitlab::Markdown::RelativeLinkFilter - ], - - post_process: [ - Gitlab::Markdown::RelativeLinkFilter, - Gitlab::Markdown::RedactorFilter - ], - - reference_extraction: [ - Gitlab::Markdown::ReferenceGathererFilter - ] - } - end - - def self.all_context_transformers - @all_context_transformers ||= { - gfm: { - only_path: true, - - # EmojiFilter - asset_host: Gitlab::Application.config.asset_host, - asset_root: Gitlab.config.gitlab.base_url - }, - full: :gfm, - - atom: [ - :full, - { - only_path: false, - xhtml: true - } - ], - email: [ - :full, - { - only_path: false - } - ], - note: [ - :full, - { - # TableOfContentsFilter - no_header_anchors: true - } - ], - description: [ - :full, - { - # SanitizationFilter - inline_sanitization: true - } - ], - single_line: :gfm, - - post_process: { - post_process: true - } - } - end - - def self.html_filters - @html_filters ||= Hash.new do |hash, pipeline| - filters = get_filters(pipeline) - hash[pipeline] = filters if pipeline.is_a?(Symbol) - filters - end - end - - def self.html_pipelines - @html_pipelines ||= Hash.new do |hash, pipeline| - filters = get_filters(pipeline) - html_pipeline = HTML::Pipeline.new(filters) - hash[pipeline] = html_pipeline if pipeline.is_a?(Symbol) - html_pipeline - end - end - - def self.context_transformers - @context_transformers ||= Hash.new do |hash, pipeline| - transformers = get_context_transformers(pipeline) - hash[pipeline] = transformers if pipeline.is_a?(Symbol) - transformers - end - end - - def self.get_filters(pipelines) - Array.wrap(pipelines).flat_map do |pipeline| - case pipeline - when Class - pipeline - when Symbol - html_filters[all_filters[pipeline]] - when Array - html_filters[pipeline] - end - end.compact - end - - def self.get_context_transformers(pipelines) - Array.wrap(pipelines).flat_map do |pipeline| - case pipeline - when Hash - ->(context) { context.merge(pipeline) } - when Proc - pipeline - when Symbol - context_transformers[all_context_transformers[pipeline]] - when Array - context_transformers[pipeline] - end - end.compact - end - - def self.cacheless_render(text, context = {}) - result = render_result(text, context) - output = result[:output] - if output.respond_to?(:to_html) - output.to_html - else - output.to_s - end - end - - def self.full_cache_key(cache_key, pipeline = :full) - return unless cache_key && pipeline.is_a?(Symbol) - - ["markdown", *cache_key, pipeline] - end + autoload :AsciidocPipeline, 'gitlab/markdown/asciidoc_pipeline' + autoload :AtomPipeline, 'gitlab/markdown/atom_pipeline' + autoload :CombinedPipeline, 'gitlab/markdown/combined_pipeline' + autoload :DescriptionPipeline, 'gitlab/markdown/description_pipeline' + autoload :EmailPipeline, 'gitlab/markdown/email_pipeline' + autoload :FullPipeline, 'gitlab/markdown/full_pipeline' + autoload :GfmPipeline, 'gitlab/markdown/gfm_pipeline' + autoload :NotePipeline, 'gitlab/markdown/note_pipeline' + autoload :Pipeline, 'gitlab/markdown/pipeline' + autoload :PlainMarkdownPipeline, 'gitlab/markdown/plain_markdown_pipeline' + autoload :PostProcessPipeline, 'gitlab/markdown/post_process_pipeline' + autoload :ReferenceExtractionPipeline, 'gitlab/markdown/reference_extraction_pipeline' + autoload :SingleLinePipeline, 'gitlab/markdown/single_line_pipeline' end end diff --git a/lib/gitlab/markdown/asciidoc_pipeline.rb b/lib/gitlab/markdown/asciidoc_pipeline.rb new file mode 100644 index 00000000000..6829b4acb95 --- /dev/null +++ b/lib/gitlab/markdown/asciidoc_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class AsciidocPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::RelativeLinkFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/atom_pipeline.rb b/lib/gitlab/markdown/atom_pipeline.rb new file mode 100644 index 00000000000..e151f8f5e5a --- /dev/null +++ b/lib/gitlab/markdown/atom_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class AtomPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + only_path: false, + xhtml: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/combined_pipeline.rb b/lib/gitlab/markdown/combined_pipeline.rb new file mode 100644 index 00000000000..a3d717550f5 --- /dev/null +++ b/lib/gitlab/markdown/combined_pipeline.rb @@ -0,0 +1,25 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + module CombinedPipeline + def self.new(*pipelines) + Class.new(Pipeline) do + const_set :PIPELINES, pipelines + + def self.filters + pipelines.flat_map(&:filters) + end + + def self.transform_context(context) + pipelines.reduce(context) { |context, pipeline| pipeline.transform_context(context) } + end + + def self.pipelines + self::PIPELINES + end + end + end + end + end +end diff --git a/lib/gitlab/markdown/description_pipeline.rb b/lib/gitlab/markdown/description_pipeline.rb new file mode 100644 index 00000000000..76f6948af8f --- /dev/null +++ b/lib/gitlab/markdown/description_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class DescriptionPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + # SanitizationFilter + inline_sanitization: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/email_pipeline.rb b/lib/gitlab/markdown/email_pipeline.rb new file mode 100644 index 00000000000..b88cb790270 --- /dev/null +++ b/lib/gitlab/markdown/email_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class EmailPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + only_path: false + ) + end + end + end +end diff --git a/lib/gitlab/markdown/full_pipeline.rb b/lib/gitlab/markdown/full_pipeline.rb new file mode 100644 index 00000000000..553e9367c1c --- /dev/null +++ b/lib/gitlab/markdown/full_pipeline.rb @@ -0,0 +1,9 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline) + + end + end +end diff --git a/lib/gitlab/markdown/gfm_pipeline.rb b/lib/gitlab/markdown/gfm_pipeline.rb new file mode 100644 index 00000000000..ca90bd75d77 --- /dev/null +++ b/lib/gitlab/markdown/gfm_pipeline.rb @@ -0,0 +1,41 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class GfmPipeline < Pipeline + def self.filters + @filters ||= [ + Gitlab::Markdown::SyntaxHighlightFilter, + Gitlab::Markdown::SanitizationFilter, + + Gitlab::Markdown::UploadLinkFilter, + Gitlab::Markdown::EmojiFilter, + Gitlab::Markdown::TableOfContentsFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, + + Gitlab::Markdown::UserReferenceFilter, + Gitlab::Markdown::IssueReferenceFilter, + Gitlab::Markdown::ExternalIssueReferenceFilter, + Gitlab::Markdown::MergeRequestReferenceFilter, + Gitlab::Markdown::SnippetReferenceFilter, + Gitlab::Markdown::CommitRangeReferenceFilter, + Gitlab::Markdown::CommitReferenceFilter, + Gitlab::Markdown::LabelReferenceFilter, + + Gitlab::Markdown::TaskListFilter + ] + end + + def self.transform_context(context) + context.merge( + only_path: true, + + # EmojiFilter + asset_host: Gitlab::Application.config.asset_host, + asset_root: Gitlab.config.gitlab.base_url + ) + end + end + end +end diff --git a/lib/gitlab/markdown/note_pipeline.rb b/lib/gitlab/markdown/note_pipeline.rb new file mode 100644 index 00000000000..a8bf5f42d8e --- /dev/null +++ b/lib/gitlab/markdown/note_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class NotePipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + # TableOfContentsFilter + no_header_anchors: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb new file mode 100644 index 00000000000..3c0b676a073 --- /dev/null +++ b/lib/gitlab/markdown/pipeline.rb @@ -0,0 +1,29 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class Pipeline + def self.filters + [] + end + + def self.transform_context(context) + context + end + + def self.html_pipeline + @html_pipeline ||= HTML::Pipeline.new(filters) + end + + class << self + %i(call to_document to_html).each do |meth| + define_method(meth) do |text, context| + context = transform_context(context) + + html_pipeline.send(meth, text, context) + end + end + end + end + end +end diff --git a/lib/gitlab/markdown/plain_markdown_pipeline.rb b/lib/gitlab/markdown/plain_markdown_pipeline.rb new file mode 100644 index 00000000000..0abb93f8a03 --- /dev/null +++ b/lib/gitlab/markdown/plain_markdown_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class PlainMarkdownPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::MarkdownFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/post_process_pipeline.rb b/lib/gitlab/markdown/post_process_pipeline.rb new file mode 100644 index 00000000000..60cc32f490e --- /dev/null +++ b/lib/gitlab/markdown/post_process_pipeline.rb @@ -0,0 +1,20 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class PostProcessPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::RedactorFilter + ] + end + + def self.transform_context(context) + context.merge( + post_process: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/reference_extraction_pipeline.rb b/lib/gitlab/markdown/reference_extraction_pipeline.rb new file mode 100644 index 00000000000..a89ab462bac --- /dev/null +++ b/lib/gitlab/markdown/reference_extraction_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class ReferenceExtractionPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::ReferenceGathererFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/single_line_pipeline.rb b/lib/gitlab/markdown/single_line_pipeline.rb new file mode 100644 index 00000000000..2f24927b879 --- /dev/null +++ b/lib/gitlab/markdown/single_line_pipeline.rb @@ -0,0 +1,9 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class SingleLinePipeline < GfmPipeline + + end + end +end -- cgit v1.2.1 From c21d46c29ffe2547cb454c2658fa9644a19f1dd8 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 21 Oct 2015 12:34:09 +0200 Subject: Fix build trace updating --- CHANGELOG | 1 + app/assets/javascripts/ci/build.coffee | 2 +- app/views/projects/builds/show.html.haml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4a9a85d5ebf..4881d4e9e8e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ v 8.1.0 (unreleased) - Improved performance of the trending projects page - Improved performance of finding projects by their namespace - Fix bug where transferring a project would result in stale commit links (Stan Hu) + - Fix build trace updating - 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) - Add option to admin area to sign in as a specific user (Pavel Forkert) diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee index c30859b484b..93385b32a13 100644 --- a/app/assets/javascripts/ci/build.coffee +++ b/app/assets/javascripts/ci/build.coffee @@ -22,7 +22,7 @@ class CiBuild # Only valid for runnig build when output changes during time # CiBuild.interval = setInterval => - if window.location.href is build_url + if window.location.href.split("#").first() is build_url $.ajax url: build_url dataType: "json" diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index c45bfb27b8f..3a8172dc8e6 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -175,4 +175,4 @@ :javascript - new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}") + new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}") -- cgit v1.2.1 From c192444db5ab4b770ec081b48229ee50b6eaa57e Mon Sep 17 00:00:00 2001 From: "Artem V. Navrotskiy" Date: Tue, 20 Oct 2015 21:47:29 +0300 Subject: Show empty repository page if repository don't have branches --- CHANGELOG | 1 + app/models/project.rb | 2 +- app/models/repository.rb | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 4a9a85d5ebf..bb0e97603c5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -88,6 +88,7 @@ v 8.0.5 - Correct lookup-by-email for LDAP logins - Fix loading spinner sometimes not being hidden on Merge Request tab switches - Animate the logo on hover + - Show "Empty Repository Page" for repository without branches (#9728) 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/project.rb b/app/models/project.rb index 88cd88dcb5a..4c5bf220f24 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -567,7 +567,7 @@ class Project < ActiveRecord::Base end def empty_repo? - !repository.exists? || repository.empty? + !repository.exists? || !repository.has_visible_content? end def repo diff --git a/app/models/repository.rb b/app/models/repository.rb index 0808896fd87..88d3d73a40e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -44,6 +44,19 @@ class Repository raw_repository.empty? end + # + # Git repository can contains some hidden refs like: + # /refs/notes/* + # /refs/git-as-svn/* + # /refs/pulls/* + # This refs by default not visible in project page and not cloned to client side. + # + # This method return true if repository contains some content visible in project page. + # + def has_visible_content? + !raw_repository.branches.empty? + end + def commit(id = 'HEAD') return nil unless raw_repository commit = Gitlab::Git::Commit.find(raw_repository, id) -- cgit v1.2.1 From d7bcfe4fc020529f9b6ed2a75dfb2f31acded080 Mon Sep 17 00:00:00 2001 From: Dirceu Pereira Tiegs Date: Wed, 14 Oct 2015 20:14:24 -0300 Subject: Fix issue #3055 (project search with unmatched parentheses) --- app/models/repository.rb | 2 +- lib/gitlab/project_search_results.rb | 2 +- spec/lib/gitlab/project_search_results_spec.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 8b51602bc23..04e1114608b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -482,7 +482,7 @@ class Repository def search_files(query, ref) offset = 2 - args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} #{query} #{ref || root_ref}) + args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 0dab7bcfa4d..0a2be605af9 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -9,7 +9,7 @@ module Gitlab else nil end - @query = Shellwords.shellescape(query) if query.present? + @query = query end def objects(scope, page = nil) diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 32a25f08cac..19327ac8ce0 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::ProjectSearchResults do it { expect(results.project).to eq(project) } it { expect(results.repository_ref).to be_nil } - it { expect(results.query).to eq('hello\\ world') } + it { expect(results.query).to eq('hello world') } end describe 'initialize with ref' do @@ -18,6 +18,6 @@ describe Gitlab::ProjectSearchResults do it { expect(results.project).to eq(project) } it { expect(results.repository_ref).to eq(ref) } - it { expect(results.query).to eq('hello\\ world') } + it { expect(results.query).to eq('hello world') } end end -- cgit v1.2.1 From 4eac37fd872db5e1b73d3d74b943a943ae7e1d6a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 16 Oct 2015 23:33:49 -0700 Subject: Don't show "Add README" link in an empty repository if user doesn't have access to push Closes #3094 --- CHANGELOG | 1 + app/views/projects/empty.html.haml | 85 ++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 41 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c1933412aec..281ec1b9bc0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.1.0 (unreleased) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. - Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu) + - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu) - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) - Speed up load times of issue detail pages by roughly 1.5x - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg) diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index e06454fd148..c3858e78cad 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -2,53 +2,56 @@ - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' = render 'shared/no_password' - + = render "home_panel" .gray-content-block.center %h3.page-title The repository for this project is empty - %p - If you already have files you can push them using command line instructions below. - %br - Otherwise you can start with - = link_to "adding README", new_readme_path, class: 'underlined-link' - file to this project. + - if can?(current_user, :download_code, @project) + %p + If you already have files you can push them using command line instructions below. + %br + - if can?(current_user, :push_code, @project) + Otherwise you can start with + = link_to "adding README", new_readme_path, class: 'underlined-link' + file to this project. -.prepend-top-20 -.empty_wrapper - %h3.page-title-empty - Command line instructions - %div.git-empty - %fieldset - %h5 Git global setup - %pre.light-well - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" +- if can?(current_user, :download_code, @project) + .prepend-top-20 + .empty_wrapper + %h3.page-title-empty + Command line instructions + %div.git-empty + %fieldset + %h5 Git global setup + %pre.light-well + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" - %fieldset - %h5 Create a new repository - %pre.light-well - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} - cd #{h @project.path} - touch README.md - git add README.md - git commit -m "add README" - git push -u origin master + %fieldset + %h5 Create a new repository + %pre.light-well + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')} + cd #{h @project.path} + touch README.md + git add README.md + git commit -m "add README" + git push -u origin master - %fieldset - %h5 Existing folder or Git repository - %pre.light-well - :preserve - cd existing_folder - git init - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git add . - git commit - git push -u origin master + %fieldset + %h5 Existing folder or Git repository + %pre.light-well + :preserve + cd existing_folder + git init + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} + git add . + git commit + git push -u origin master - - if can? current_user, :remove_project, @project - .prepend-top-20 - = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" + - if can? current_user, :remove_project, @project + .prepend-top-20 + = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right" -- cgit v1.2.1 From 9c61b73c21f70b3f4a7e3e8f4b7e4d8ad0544094 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 17:24:09 +0200 Subject: Make sure MR refresh service correctly determines newly added commits. --- app/services/merge_requests/refresh_service.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 121f6899011..2808b98a4a0 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -7,6 +7,8 @@ module MergeRequests @branch_name = Gitlab::Git.ref_name(ref) @fork_merge_requests = @project.fork_merge_requests.opened @commits = [] + + reload_merge_requests # Leave a system note if a branch were deleted/added if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) @@ -18,7 +20,6 @@ module MergeRequests close_merge_requests end - reload_merge_requests execute_mr_web_hooks true -- cgit v1.2.1 From 464c939a7e55f9677b5c1f25b50c31f7d10660ea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 21 Oct 2015 17:34:12 +0200 Subject: Clean up MR refresh service somewhat. --- app/services/merge_requests/refresh_service.rb | 74 ++++++++++++++++---------- app/services/system_note_service.rb | 2 +- 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 2808b98a4a0..d68bc79ecc0 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -5,17 +5,15 @@ module MergeRequests @oldrev, @newrev = oldrev, newrev @branch_name = Gitlab::Git.ref_name(ref) - @fork_merge_requests = @project.fork_merge_requests.opened - @commits = [] - + + find_new_commits reload_merge_requests - # Leave a system note if a branch were deleted/added - if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) + # Leave a system note if a branch was deleted/added + if branch_added? || branch_removed? comment_mr_branch_presence_changed - comment_mr_with_commits if @commits.present? + comment_mr_with_commits else - @commits = @project.repository.commits_between(oldrev, newrev) comment_mr_with_commits close_merge_requests end @@ -55,7 +53,7 @@ module MergeRequests # Note: we should update merge requests from forks too def reload_merge_requests merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a - merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a + merge_requests += fork_merge_requests.by_branch(@branch_name).to_a merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| @@ -78,29 +76,37 @@ module MergeRequests end end - # Add comment about branches being deleted or added to merge requests - def comment_mr_branch_presence_changed - presence = Gitlab::Git.blank_ref?(@oldrev) ? :add : :delete + def find_new_commits + if branch_added? + @commits = [] - merge_requests_for_source_branch.each do |merge_request| - last_commit = merge_request.last_commit + merge_request = merge_requests_for_source_branch.first + return unless merge_request - # Only look at changed commits in restore branch case - unless Gitlab::Git.blank_ref?(@newrev) - begin - # Since any number of commits could have been made to the restored branch, - # find the common root to see what has been added. - common_ref = @project.repository.merge_base(last_commit.id, @newrev) - # If the a commit no longer exists in this repo, gitlab_git throws - # a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52 - @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref - rescue - end + last_commit = merge_request.last_commit - # Prevent system notes from seeing a blank SHA - @oldrev = nil + begin + # Since any number of commits could have been made to the restored branch, + # find the common root to see what has been added. + common_ref = @project.repository.merge_base(last_commit.id, @newrev) + # If the a commit no longer exists in this repo, gitlab_git throws + # a Rugged::OdbError. This is fixed in https://gitlab.com/gitlab-org/gitlab_git/merge_requests/52 + @commits = @project.repository.commits_between(common_ref, @newrev) if common_ref + rescue end + elsif branch_removed? + # No commits for a deleted branch. + @commits = [] + else + @commits = @project.repository.commits_between(@oldrev, @newrev) + end + end + # Add comment about branches being deleted or added to merge requests + def comment_mr_branch_presence_changed + presence = branch_added? ? :add : :delete + + merge_requests_for_source_branch.each do |merge_request| SystemNoteService.change_branch_presence( merge_request, merge_request.project, @current_user, :source, @branch_name, presence) @@ -109,6 +115,8 @@ module MergeRequests # Add comment about pushing new commits to merge requests def comment_mr_with_commits + return unless @commits.present? + merge_requests_for_source_branch.each do |merge_request| mr_commit_ids = Set.new(merge_request.commits.map(&:id)) @@ -136,9 +144,21 @@ module MergeRequests def merge_requests_for_source_branch @source_merge_requests ||= begin merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a - merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a + merge_requests += fork_merge_requests.where(source_branch: @branch_name).to_a filter_merge_requests(merge_requests) end end + + def fork_merge_requests + @fork_merge_requests ||= @project.fork_merge_requests.opened + end + + def branch_added? + Gitlab::Git.blank_ref?(@oldrev) + end + + def branch_removed? + Gitlab::Git.blank_ref?(@newrev) + end end end diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 37f454cfc3f..708c2f00486 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -327,7 +327,7 @@ class SystemNoteService commit_ids = if count == 1 existing_commits.first.short_id else - if oldrev + if oldrev && !Gitlab::Git.blank_ref?(oldrev) "#{Commit.truncate_sha(oldrev)}...#{existing_commits.last.short_id}" else "#{existing_commits.first.short_id}..#{existing_commits.last.short_id}" -- cgit v1.2.1 From 38a93a25f394283d36d5d4e2f4bc5166a6dfbedd Mon Sep 17 00:00:00 2001 From: Sander Boom Date: Wed, 21 Oct 2015 22:52:17 +0200 Subject: Fixed linting error 'variables should be a map of key-valued strings' --- doc/ci/docker/using_docker_images.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 191e3a8144d..ef8a7ec1e86 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -90,7 +90,7 @@ you need to set MYSQL_ALLOW_EMPTY_PASSWORD. - mysql variables: - MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" ``` For other possible configuration variables check the -- cgit v1.2.1 From 05cb65dc1f816a75677d0db401e29a9b640cf4d7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 10:18:44 +0200 Subject: Fix Gitlab::Database#mysql? --- lib/gitlab/database.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 741a52714ac..71f37f1fef8 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -1,7 +1,7 @@ module Gitlab module Database def self.mysql? - ActiveRecord::Base.connection.adapter_name.downcase == 'mysql' + ActiveRecord::Base.connection.adapter_name.downcase == 'mysql2' end def self.postgresql? -- cgit v1.2.1 From 98f982f91d6e2d6ec1b59ea5645d8320989e2de6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 10:19:12 +0200 Subject: Only postgres does case sensitive compares --- spec/controllers/projects_controller_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 9b49f60b66c..4bb47c6b025 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -70,7 +70,7 @@ describe ProjectsController do # MySQL queries are case insensitive by default, so this spec would fail. - unless Gitlab::Database.mysql? + if Gitlab::Database.postgresql? context "when there is also a match with the same casing" do let!(:other_project) { create(:project, :public, namespace: public_project.namespace, path: public_project.path.upcase) } -- cgit v1.2.1 From 57382d6170a637c30d84efc11f6d7bd111cd6ca9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 10:25:17 +0200 Subject: Fix changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index bb0e97603c5..eb5a704f48c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,7 @@ v 8.2.0 (unreleased) - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw) - Improved performance of sorting milestone issues - Allow users to select the Files view as default project view (Cristian Bica) + - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy) v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) @@ -88,7 +89,6 @@ v 8.0.5 - Correct lookup-by-email for LDAP logins - Fix loading spinner sometimes not being hidden on Merge Request tab switches - Animate the logo on hover - - Show "Empty Repository Page" for repository without branches (#9728) v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) -- cgit v1.2.1 From 2305483d7d0f398caedabf6a99a94913be75138a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 22 Oct 2015 10:38:47 +0200 Subject: Require jobs to be named --- CHANGELOG | 1 + lib/ci/gitlab_ci_yaml_processor.rb | 38 +++++++++++++++++----------- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 14 ++++++++++ 3 files changed, 38 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 671955d70cf..aa70d49435e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ v 8.1.0 (unreleased) - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu) - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) - Speed up load times of issue detail pages by roughly 1.5x + - Require CI jobs to be named - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg) - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu) - Make diff file view easier to use on mobile screens (Stan Hu) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 0da73e387e1..efcd2faffc7 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -139,66 +139,74 @@ module Ci end @jobs.each do |name, job| - validate_job!("#{name} job", job) + validate_job!(name, job) end true end def validate_job!(name, job) + if name.blank? || !validate_string(name) + raise ValidationError, "job name should be non-empty string" + end + job.keys.each do |key| unless ALLOWED_JOB_KEYS.include? key - raise ValidationError, "#{name}: unknown parameter #{key}" + raise ValidationError, "#{name} job: unknown parameter #{key}" end end - if !job[:script].is_a?(String) && !validate_array_of_strings(job[:script]) - raise ValidationError, "#{name}: script should be a string or an array of a strings" + if !validate_string(job[:script]) && !validate_array_of_strings(job[:script]) + raise ValidationError, "#{name} job: script should be a string or an array of a strings" end if job[:stage] unless job[:stage].is_a?(String) && job[:stage].in?(stages) - raise ValidationError, "#{name}: stage parameter should be #{stages.join(", ")}" + raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" end end - if job[:image] && !job[:image].is_a?(String) - raise ValidationError, "#{name}: image should be a string" + if job[:image] && !validate_string(job[:image]) + raise ValidationError, "#{name} job: image should be a string" end if job[:services] && !validate_array_of_strings(job[:services]) - raise ValidationError, "#{name}: services should be an array of strings" + raise ValidationError, "#{name} job: services should be an array of strings" end if job[:tags] && !validate_array_of_strings(job[:tags]) - raise ValidationError, "#{name}: tags parameter should be an array of strings" + raise ValidationError, "#{name} job: tags parameter should be an array of strings" end if job[:only] && !validate_array_of_strings(job[:only]) - raise ValidationError, "#{name}: only parameter should be an array of strings" + raise ValidationError, "#{name} job: only parameter should be an array of strings" end if job[:except] && !validate_array_of_strings(job[:except]) - raise ValidationError, "#{name}: except parameter should be an array of strings" + raise ValidationError, "#{name} job: except parameter should be an array of strings" end if job[:allow_failure] && !job[:allow_failure].in?([true, false]) - raise ValidationError, "#{name}: allow_failure parameter should be an boolean" + raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" end if job[:when] && !job[:when].in?(%w(on_success on_failure always)) - raise ValidationError, "#{name}: when parameter should be on_success, on_failure or always" + raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always" end end private def validate_array_of_strings(values) - values.is_a?(Array) && values.all? {|tag| tag.is_a?(String)} + values.is_a?(Array) && values.all? { |value| validate_string(value) } end def validate_variables(variables) - variables.is_a?(Hash) && variables.all? {|key, value| key.is_a?(Symbol) && value.is_a?(String)} + variables.is_a?(Hash) && variables.all? { |key, value| validate_string(key) && validate_string(value) } + end + + def validate_string(value) + value.is_a?(String) || value.is_a?(Symbol) end end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 2260a6f8130..abdb6b89ac5 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -218,6 +218,20 @@ module Ci end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") end + it "returns errors if job name is blank" do + config = YAML.dump({ '' => { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") + end + + it "returns errors if job name is non-string" do + config = YAML.dump({ 10 => { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") + end + it "returns errors if job image parameter is invalid" do config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do -- cgit v1.2.1 From c7c5bd48434d3f002010032ebd2753352d5e9e87 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 10:46:54 +0200 Subject: Change dashboard snippet index menu class to match test --- app/views/dashboard/snippets/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml index 2b67fd05b9d..07b6d57932e 100644 --- a/app/views/dashboard/snippets/index.html.haml +++ b/app/views/dashboard/snippets/index.html.haml @@ -9,7 +9,7 @@ = icon('plus') New Snippet - .btn-group.btn-group-next.event-filter + .btn-group.btn-group-next.snippet-scope-menu = link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do All %span.badge -- cgit v1.2.1 From f65840bc842b86cd71e70af8b9acdb1a786d9054 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 11:00:34 +0200 Subject: Fix Markdown XHTML context param --- lib/gitlab/markdown.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 3ae4ccfacb6..5f1a6c8ceec 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -20,8 +20,6 @@ module Gitlab # # Returns an HTML-safe String def self.render(text, context = {}) - context[:pipeline] ||= :full - cache_key = context.delete(:cache_key) cache_key = full_cache_key(cache_key, context[:pipeline]) @@ -35,8 +33,7 @@ module Gitlab end def self.render_result(text, context = {}) - pipeline_type = context[:pipeline] ||= :full - pipeline_by_type(pipeline_type).call(text, context) + pipeline_by_name(context[:pipeline]).call(text, context) end # Perform post-processing on an HTML String @@ -53,7 +50,10 @@ module Gitlab # # Returns an HTML-safe String def self.post_process(html, context) - pipeline = pipeline_by_type(:post_process) + pipeline = pipeline_by_name(context[:pipeline]) + context = pipeline.transform_context(context) + + pipeline = pipeline_by_name(:post_process) if context[:xhtml] pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) @@ -74,14 +74,15 @@ module Gitlab end end - def self.full_cache_key(cache_key, pipeline = :full) + def self.full_cache_key(cache_key, pipeline_name) return unless cache_key - + pipeline_name ||= :full ["markdown", *cache_key, pipeline] end - def self.pipeline_by_type(pipeline_type) - const_get("#{pipeline_type.to_s.camelize}Pipeline") + def self.pipeline_by_name(pipeline_name) + pipeline_name ||= :full + const_get("#{pipeline_name.to_s.camelize}Pipeline") end # Provide autoload paths for filters to prevent a circular dependency error -- cgit v1.2.1 From ae9f81a9501f401133c9ba70e84a2148cd92f1ee Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 22 Oct 2015 11:00:48 +0200 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 671955d70cf..9a550ca1139 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) - - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Improved performance of replacing references in comments - - Fix duplicate repositories in GitHub import page (Stan Hu) - - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw) @@ -12,7 +9,10 @@ v 8.2.0 (unreleased) - Allow users to select the Files view as default project view (Cristian Bica) - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy) -v 8.1.0 (unreleased) +v 8.1.0 + - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) + - Fix duplicate repositories in GitHub import page (Stan Hu) + - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. @@ -87,11 +87,11 @@ v 8.1.0 (unreleased) - Let gitlab-git-http-server generate and serve 'git archive' downloads - Optimize query when filtering on issuables (Zeger-Jan van de Weg) - Fix padding of outdated discussion item. + - Animate the logo on hover v 8.0.5 - Correct lookup-by-email for LDAP logins - Fix loading spinner sometimes not being hidden on Merge Request tab switches - - Animate the logo on hover v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) -- cgit v1.2.1 From adba37f99aa6351ce7ca29055d5aab284058bc2e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 11:02:21 +0200 Subject: Use markdown with gfm pipeline rather than gfm method --- app/views/projects/commits/show.atom.builder | 2 +- app/views/projects/issues/_closed_by_box.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 3854ad5d611..7aab98e07c4 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.name commit.author_name xml.email commit.author_email end - xml.summary gfm(commit.description) + xml.summary markdown(commit.description, pipeline: :atom) end end end diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml index aef352029d0..eebe36bc441 100644 --- a/app/views/projects/issues/_closed_by_box.html.haml +++ b/app/views/projects/issues/_closed_by_box.html.haml @@ -1,3 +1,3 @@ .issue-closed-by-widget = icon('check') - This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted. + This issue will be closed automatically when merge request #{markdown(merge_requests_sentence(@closed_by_merge_requests.sort), pipeline: :gfm)} is accepted. -- cgit v1.2.1 From 40934ae39bc9aea6ffb176f96bba72fa688de837 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 22 Oct 2015 11:13:19 +0200 Subject: Fix 500 when editing CI services --- CHANGELOG | 1 + app/controllers/projects/ci_services_controller.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9a550ca1139..e4e58a3e83a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 8.1.0 - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu) - Make diff file view easier to use on mobile screens (Stan Hu) - Improved performance of finding users by username or Email address + - Fix 500 when editing CI service - Fix bug where merge request comments created by API would not trigger notifications (Stan Hu) - Add support for creating directories from Files page (Stan Hu) - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb index 406f313ae79..550a019e8e2 100644 --- a/app/controllers/projects/ci_services_controller.rb +++ b/app/controllers/projects/ci_services_controller.rb @@ -14,17 +14,17 @@ class Projects::CiServicesController < Projects::ApplicationController end def update - if @service.update_attributes(service_params) - redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param) + if service.update_attributes(service_params) + redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) else render 'edit' end end def test - last_build = @project.builds.last + last_build = @project.ci_builds.last - if @service.execute(last_build) + if service.execute(last_build) message = { notice: 'We successfully tested the service' } else message = { alert: 'We tried to test the service but error occurred' } -- cgit v1.2.1 From 337b6632b643e4eb0417e42302a347ea87a2315c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 22 Oct 2015 11:31:00 +0200 Subject: Fix CSS class for runner status --- CHANGELOG | 1 + app/assets/stylesheets/pages/runners.scss | 52 +++++++++++++++---------------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9a550ca1139..ebee45ace9e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) + - Fix CSS for runner status - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss index 2b15ab83129..a9111a7388f 100644 --- a/app/assets/stylesheets/pages/runners.scss +++ b/app/assets/stylesheets/pages/runners.scss @@ -1,36 +1,34 @@ -.ci-body { - .runner-state { - padding: 6px 12px; - margin-right: 10px; - color: #FFF; +.runner-state { + padding: 6px 12px; + margin-right: 10px; + color: #FFF; - &.runner-state-shared { - background: #32b186; - } - &.runner-state-specific { - background: #3498db; - } + &.runner-state-shared { + background: #32b186; } - - .runner-status-online { - color: green; + &.runner-state-specific { + background: #3498db; } +} - .runner-status-offline { - color: gray; - } +.runner-status-online { + color: green; +} - .runner-status-paused { - color: red; - } +.runner-status-offline { + color: gray; +} + +.runner-status-paused { + color: red; +} - .runner { - .btn { - padding: 1px 6px; - } +.runner { + .btn { + padding: 1px 6px; + } - h4 { - font-weight: normal; - } + h4 { + font-weight: normal; } } -- cgit v1.2.1 From 3c3e57705f801c5a810183523c5898d44d5de872 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 22 Oct 2015 14:22:49 +0200 Subject: Add builds to the docs as a valid skip option for backups. --- doc/raketasks/backup_restore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 06f582dcee8..606532a6fbe 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` Also you can choose what should be backed up by adding environment variable SKIP. Available options: db, -uploads (attachments), repositories. Use a comma to specify several options at the same time. +uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time. ``` sudo gitlab-rake gitlab:backup:create SKIP=db,uploads -- cgit v1.2.1 From c1ecfb5de960a675888640a05c75c07217c2d293 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 15:38:11 +0200 Subject: Disabling caching in test environment because it was causing issues with Markdown --- config/environments/test.rb | 2 ++ config/initializers/session_store.rb | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/config/environments/test.rb b/config/environments/test.rb index 2d5e7addcd3..e03f54c5530 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -7,6 +7,8 @@ Gitlab::Application.configure do # and recreated between test runs. Don't rely on the data there! config.cache_classes = false + config.cache_store = :null_store + # Configure static asset server for tests with Cache-Control for performance config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 04ed9e90df5..6be21a771e3 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -9,12 +9,16 @@ begin rescue end -Gitlab::Application.config.session_store( - :redis_store, # Using the cookie_store would enable session replay attacks. - servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store - key: '_gitlab_session', - secure: Gitlab.config.gitlab.https, - httponly: true, - expire_after: Settings.gitlab['session_expire_delay'] * 60, - path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root -) +if Rails.env.test? + Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" +else + Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store + key: '_gitlab_session', + secure: Gitlab.config.gitlab.https, + httponly: true, + expire_after: Settings.gitlab['session_expire_delay'] * 60, + path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root + ) +end -- cgit v1.2.1 From c746a1de4ef75034c75d20e763ccfa1f73750722 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 15:40:02 +0200 Subject: Use faster, more appropriate pipeline for mentionable attributes --- app/models/commit.rb | 2 +- app/models/concerns/issuable.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 492f6be1ce3..a5f70b3a351 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -7,7 +7,7 @@ class Commit include Referable include StaticModel - attr_mentionable :safe_message + attr_mentionable :safe_message, pipeline: :single_line participant :author, :committer, :notes attr_accessor :project diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 54bfd4eced5..1d7cecef97b 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -46,7 +46,7 @@ module Issuable allow_nil: true, prefix: true - attr_mentionable :title + attr_mentionable :title, pipeline: :single_line attr_mentionable :description, cache: true participant :author, :assignee, :notes_with_associations end -- cgit v1.2.1 From 51ed8fd706802fe0fffe7a2db0b5adc1f2196e50 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 15:40:21 +0200 Subject: Use correct var name --- lib/gitlab/markdown.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 5f1a6c8ceec..969607558ef 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -77,7 +77,7 @@ module Gitlab def self.full_cache_key(cache_key, pipeline_name) return unless cache_key pipeline_name ||= :full - ["markdown", *cache_key, pipeline] + ["markdown", *cache_key, pipeline_name] end def self.pipeline_by_name(pipeline_name) -- cgit v1.2.1 From aa4007a597ea6c7de11a8f37de2fd889aa6e6646 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 15:40:36 +0200 Subject: Slight refactoring --- lib/gitlab/reference_extractor.rb | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 69c7a0f4779..e5cafebdbc0 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -11,6 +11,7 @@ module Gitlab @load_lazy_references = load_lazy_references @texts = [] + @references = {} end def analyze(text, options = {}) @@ -19,21 +20,12 @@ module Gitlab %i(user label issue merge_request snippet commit commit_range).each do |type| define_method("#{type}s") do - references[type] + @references[type] ||= pipeline_result(type) end end private - def references - @references ||= Hash.new do |references, type| - type = type.to_sym - next references[type] if references.has_key?(type) - - references[type] = pipeline_result(type) - end - end - # Instantiate and call HTML::Pipeline with a single reference filter type, # returning the result # -- cgit v1.2.1 From 46025ffb8c717eedced397edd19ce8f0d67802e7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 15:40:42 +0200 Subject: Fix specs --- spec/lib/gitlab/markdown/sanitization_filter_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb index e50c82d0b3c..8243c994600 100644 --- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb +++ b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb @@ -93,16 +93,16 @@ module Gitlab::Markdown end end - context 'when pipeline is :description' do + context 'when inline_sanitization is true' do it 'uses a stricter whitelist' do - doc = filter('

Description

', pipeline: :description) + doc = filter('

Description

', inline_sanitization: true) expect(doc.to_html.strip).to eq 'Description' end %w(pre code img ol ul li).each do |elem| it "removes '#{elem}' elements" do act = "<#{elem}>Description" - expect(filter(act, pipeline: :description).to_html.strip). + expect(filter(act, inline_sanitization: true).to_html.strip). to eq 'Description' end end @@ -110,7 +110,7 @@ module Gitlab::Markdown %w(b i strong em a ins del sup sub p).each do |elem| it "still allows '#{elem}' elements" do exp = act = "<#{elem}>Description" - expect(filter(act, pipeline: :description).to_html).to eq exp + expect(filter(act, inline_sanitization: true).to_html).to eq exp end end end -- cgit v1.2.1 From 5f1c99b757b7c23d476b6b523160c242db09259f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 22 Oct 2015 16:08:45 +0200 Subject: Fix alignment of the filter counter for Issuables --- app/assets/stylesheets/pages/issuable.scss | 8 ++++++++ app/views/projects/issues/_issues.html.haml | 7 ++++--- app/views/projects/merge_requests/_merge_requests.html.haml | 6 ++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9da085a3473..c60aa5c7fe7 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -80,3 +80,11 @@ } } } + +.issuable-filter-count { + span { + display: block; + margin-bottom: -16px; + padding: 13px 0; + } +} diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index a3399c57aa2..ca5b1a8386d 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -5,8 +5,9 @@ .nothing-here-block No issues to show - if @issues.present? - .pull-right - %span.issue_counter #{@issues.total_count} - issues for this filter + .issuable-filter-count + %span.pull-right + = @issues.total_count + issues for this filter = paginate @issues, theme: "gitlab" diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml index d86707b3d97..0af970e4b92 100644 --- a/app/views/projects/merge_requests/_merge_requests.html.haml +++ b/app/views/projects/merge_requests/_merge_requests.html.haml @@ -5,8 +5,10 @@ .nothing-here-block No merge requests to show - if @merge_requests.present? - .pull-right - %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter + .issuable-filter-count + %span.pull-right + = @merge_requests.total_count + merge requests for this filter = paginate @merge_requests, theme: "gitlab" -- cgit v1.2.1 From 2db7868a55570dd1a635725c7e8420fc8a8a4379 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 Oct 2015 16:50:21 +0200 Subject: Make file list more compact Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/tree.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 1b0cef481d6..d4ab6967ccd 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -5,7 +5,7 @@ tr { > td, > th { - line-height: 32px; + line-height: 28px; } &:hover { -- cgit v1.2.1 From 097238f0a5f6735b7f0596e2283068be17d39e91 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 22 Oct 2015 17:11:21 +0200 Subject: Restore edit button in project readme --- app/assets/stylesheets/pages/projects.scss | 8 ++++++++ app/views/projects/_readme.html.haml | 10 ++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 41bea0ec5c8..27bedea0e1f 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -544,5 +544,13 @@ pre.light-well { } .project-show-readme .readme-holder { + margin-left: -$gl-padding; + margin-right: -$gl-padding; + padding: $gl-padding; border-top: 0; + + .edit-project-readme { + z-index: 100; + position: relative; + } } diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index 0a1cecfdcdf..b5ef0aca540 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -1,10 +1,8 @@ - if readme = @repository.readme - %article.file-holder.readme-holder - .file-title - = blob_icon readme.mode, readme.name - = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do - %strong - = readme.name + %article.readme-holder + .pull-right + - if can?(current_user, :push_code, @project) + = link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme' .file-content.wiki = cache(readme_cache_key) do = render_readme(readme) -- cgit v1.2.1 From 8655f4793e14b1361ca7f1900a74390b21cb2b1d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 Oct 2015 17:25:06 +0200 Subject: Remove bad css style Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/mixins.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 1d798344890..fe078d016d7 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -152,8 +152,3 @@ } } } - -.fa-align { - top: 20px; - position: relative; -} -- cgit v1.2.1 From ddb984470d097d4296b6f23fb533429b23d7d7b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 Oct 2015 17:33:51 +0200 Subject: Add extra padding to README block Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/projects.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 27bedea0e1f..8dd782b624c 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -546,9 +546,9 @@ pre.light-well { .project-show-readme .readme-holder { margin-left: -$gl-padding; margin-right: -$gl-padding; - padding: $gl-padding; + padding: ($gl-padding + 7px); border-top: 0; - + .edit-project-readme { z-index: 100; position: relative; -- cgit v1.2.1 From f61dd16c3f9a0035b0579677303ac44b3cf7848e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 22 Oct 2015 17:58:09 +0200 Subject: Improve css code quality for projects.scss Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/projects.scss | 29 +++++++++++----------- .../projects/buttons/_notifications.html.haml | 4 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 27bedea0e1f..dfb25dc20af 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -50,7 +50,17 @@ } .project-home-dropdown { - margin: 11px 3px 0; + margin: 13px 0px 0; + } + + .notifications-btn { + .fa-bell { + margin-right: 6px; + } + + .fa-angle-down { + margin-left: 6px; + } } .project-home-desc { @@ -85,6 +95,7 @@ color: inherit; } } + .input-group { display: inline-table; position: relative; @@ -233,23 +244,11 @@ } } - .fa-fw { + i { margin-right: 8px; } } -.fa-bell { - margin-right: 6px; -} - -.fa-angle-down { - margin-left: 6px; -} - -.project-home-panel .project-home-dropdown { - margin: 13px 0px 0; -} - .project-visibility-level-holder { .radio { margin-bottom: 10px; @@ -548,7 +547,7 @@ pre.light-well { margin-right: -$gl-padding; padding: $gl-padding; border-top: 0; - + .edit-project-readme { z-index: 100; position: relative; diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 0c298844912..3e83ec3912f 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -5,7 +5,7 @@ = hidden_field_tag :notification_id, @membership.id = hidden_field_tag :notification_level %span.dropdown - %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-new.btn.notifications-btn#notifications-button{href: '#', "data-toggle" => "dropdown"} = icon('bell') = notification_label(@membership) = icon('angle-down') @@ -14,7 +14,7 @@ = notification_list_item(level, @membership) - when GroupMember - .btn.btn-new.disabled.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} + .btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} = icon('bell') = notification_label(@membership) = icon('angle-down') -- cgit v1.2.1 From 835f6a2b51f43c65eb70c6e9e764263786ed9f8e Mon Sep 17 00:00:00 2001 From: "Artem V. Navrotskiy" Date: Thu, 22 Oct 2015 19:02:55 +0300 Subject: Bump gitlab-shell to v2.6.6 --- GITLAB_SHELL_VERSION | 2 +- doc/install/installation.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 57cf282ebbc..338a5b5d8fe 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.5 +2.6.6 diff --git a/doc/install/installation.md b/doc/install/installation.md index 2e9ac7393e3..a470033f9ad 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -310,7 +310,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.5] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.6] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: -- cgit v1.2.1 From 95df86638d364a87469550cce852871634ace262 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 22 Oct 2015 18:38:00 +0200 Subject: Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork --- CHANGELOG | 1 + app/controllers/projects/commits_controller.rb | 2 +- app/models/merge_request.rb | 2 +- spec/models/merge_request_spec.rb | 6 ++++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3ca30375f6d..8cefa043113 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,7 @@ v 8.2.0 (unreleased) - Improved performance of sorting milestone issues - Allow users to select the Files view as default project view (Cristian Bica) - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy) + - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index d1c15174aea..58fb946dbc2 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -12,7 +12,7 @@ class Projects::CommitsController < Projects::ApplicationController @limit, @offset = (params[:limit] || 40), (params[:offset] || 0) @commits = @repo.commits(@ref, @path, @limit, @offset) - @note_counts = Note.where(commit_id: @commits.map(&:id)). + @note_counts = project.notes.where(commit_id: @commits.map(&:id)). group(:commit_id).count respond_to do |format| diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 21861a46a84..8d9ad44681d 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -257,7 +257,7 @@ class MergeRequest < ActiveRecord::Base Note.where( "(project_id = :target_project_id AND noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR" + - "(project_id = :source_project_id AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))", + "((project_id = :source_project_id OR project_id = :target_project_id) AND noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids, target_project_id: target_project_id, diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6aaf1c036b0..eed2cbc5412 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -79,6 +79,12 @@ describe MergeRequest do expect(merge_request.commits).not_to be_empty expect(merge_request.mr_and_commit_notes.count).to eq(2) end + + it "should include notes for commits from target project as well" do + create(:note, commit_id: merge_request.commits.first.id, noteable_type: 'Commit', project: merge_request.target_project) + expect(merge_request.commits).not_to be_empty + expect(merge_request.mr_and_commit_notes.count).to eq(3) + end end describe '#is_being_reassigned?' do -- cgit v1.2.1 From 64a270a6455d474926aaa8be281c88fca072a113 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 22 Oct 2015 13:53:12 -0700 Subject: Fix cloning Wiki repositories via HTTP Cloning a project Wiki over HTTP would end up cloning the main repository since the .wiki extension was being stripped. Closes #3106 --- CHANGELOG | 1 + lib/gitlab/backend/grack_auth.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fa630f354c4..6d2e55dbbd5 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.2.0 (unreleased) + - Fix cloning Wiki repositories via HTTP (Stan Hu) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 6830a916bcb..85a2d1a93a7 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -193,12 +193,19 @@ module Grack end def render_grack_auth_ok + repo_path = + if @request.path_info =~ /^([\w\.\/-]+)\.wiki\.git/ + ProjectWiki.new(project).repository.path_to_repo + else + project.repository.path_to_repo + end + [ 200, { "Content-Type" => "application/json" }, [JSON.dump({ 'GL_ID' => Gitlab::ShellEnv.gl_id(@user), - 'RepoPath' => project.repository.path_to_repo, + 'RepoPath' => repo_path, })] ] end -- cgit v1.2.1 From 69e5e260b0bd28cd48983494b2765e1cef0d3c38 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 22 Oct 2015 14:16:37 -0700 Subject: Add spec for cloning Wiki over HTTP --- spec/lib/gitlab/backend/grack_auth_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index 37c527221a0..dfa0e10318a 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -50,6 +50,22 @@ describe Grack::Auth do end end + context "when the Wiki for a project exists" do + before do + @wiki = ProjectWiki.new(project) + env["PATH_INFO"] = "#{@wiki.repository.path_with_namespace}.git/info/refs" + project.update_attribute(:visibility_level, Project::PUBLIC) + end + + it "responds with the right project" do + response = auth.call(env) + json_body = ActiveSupport::JSON.decode(response[2][0]) + + expect(response.first).to eq(200) + expect(json_body['RepoPath']).to include(@wiki.repository.path_with_namespace) + end + end + context "when the project exists" do before do env["PATH_INFO"] = project.path_with_namespace + ".git" -- cgit v1.2.1 From c5146ca1517412b0389c483f18c13072e2abc4a3 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Fri, 23 Oct 2015 03:00:08 +0200 Subject: Fix merge error --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 596a47938d6..3fa7d2bb1cd 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -68,7 +68,7 @@ module ApplicationHelper end end - def avatar_icon(user_email = nil, size = nil, scale = 2) + def avatar_icon(user_or_email = nil, size = nil, scale = 2) if user_or_email.is_a?(User) user = user_or_email else -- cgit v1.2.1 From 3d613fe1e87a4e9837239b34f5fdf88063ea98f9 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 22 Oct 2015 17:16:51 +0800 Subject: Fix API::APIHelpers -> API::Helpers; Rails Autoload find file to require is use , APIHelpers -> api_helpers.rb, not helpers.rb; --- lib/api/api.rb | 2 +- lib/api/helpers.rb | 2 +- lib/ci/api/api.rb | 2 +- spec/requests/api/api_helpers_spec.rb | 26 +++++++++++++------------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/api/api.rb b/lib/api/api.rb index afc0402f9e1..40671e2517c 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -25,7 +25,7 @@ module API format :json content_type :txt, "text/plain" - helpers APIHelpers + helpers Helpers mount Groups mount GroupMembers diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 549b1f9e9a7..652bdf9b278 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -1,5 +1,5 @@ module API - module APIHelpers + module Helpers PRIVATE_TOKEN_HEADER = "HTTP_PRIVATE_TOKEN" PRIVATE_TOKEN_PARAM = :private_token SUDO_HEADER ="HTTP_SUDO" diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 218d8c3adcc..0a4cbf69b63 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -26,7 +26,7 @@ module Ci format :json helpers Helpers - helpers ::API::APIHelpers + helpers ::API::Helpers mount Builds mount Commits diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb index 4048c297013..0c19094ec54 100644 --- a/spec/requests/api/api_helpers_spec.rb +++ b/spec/requests/api/api_helpers_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe API, api: true do - include API::APIHelpers + include API::Helpers include ApiHelpers let(:user) { create(:user) } let(:admin) { create(:admin) } @@ -13,25 +13,25 @@ describe API, api: true do def set_env(token_usr, identifier) clear_env clear_param - env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = token_usr.private_token - env[API::APIHelpers::SUDO_HEADER] = identifier + env[API::Helpers::PRIVATE_TOKEN_HEADER] = token_usr.private_token + env[API::Helpers::SUDO_HEADER] = identifier end def set_param(token_usr, identifier) clear_env clear_param - params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = token_usr.private_token - params[API::APIHelpers::SUDO_PARAM] = identifier + params[API::Helpers::PRIVATE_TOKEN_PARAM] = token_usr.private_token + params[API::Helpers::SUDO_PARAM] = identifier end def clear_env - env.delete(API::APIHelpers::PRIVATE_TOKEN_HEADER) - env.delete(API::APIHelpers::SUDO_HEADER) + env.delete(API::Helpers::PRIVATE_TOKEN_HEADER) + env.delete(API::Helpers::SUDO_HEADER) end def clear_param - params.delete(API::APIHelpers::PRIVATE_TOKEN_PARAM) - params.delete(API::APIHelpers::SUDO_PARAM) + params.delete(API::Helpers::PRIVATE_TOKEN_PARAM) + params.delete(API::Helpers::SUDO_PARAM) end def error!(message, status) @@ -40,22 +40,22 @@ describe API, api: true do describe ".current_user" do it "should return nil for an invalid token" do - env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token' + env[API::Helpers::PRIVATE_TOKEN_HEADER] = 'invalid token' allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ false } expect(current_user).to be_nil end it "should return nil for a user without access" do - env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token + env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token allow(Gitlab::UserAccess).to receive(:allowed?).and_return(false) expect(current_user).to be_nil end it "should leave user as is when sudo not specified" do - env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = user.private_token + env[API::Helpers::PRIVATE_TOKEN_HEADER] = user.private_token expect(current_user).to eq(user) clear_env - params[API::APIHelpers::PRIVATE_TOKEN_PARAM] = user.private_token + params[API::Helpers::PRIVATE_TOKEN_PARAM] = user.private_token expect(current_user).to eq(user) end -- cgit v1.2.1 From 127836dd541ce0ecd4976d002d97b3e9e57f4947 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 11:40:57 +0200 Subject: Fix small CI UI regressions --- CHANGELOG | 2 ++ app/assets/javascripts/ci/build.coffee | 2 +- app/controllers/projects/builds_controller.rb | 9 +++++---- app/models/commit_status.rb | 1 - app/views/ci/lints/_create.html.haml | 7 ++++++- app/views/projects/builds/show.html.haml | 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fa630f354c4..e81b5f74b77 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,8 @@ v 8.1.0 - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) - Speed up load times of issue detail pages by roughly 1.5x - Require CI jobs to be named + - Fix CI rendering regressions + - Allow developer to manage builds - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg) - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu) - Make diff file view easier to use on mobile screens (Stan Hu) diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/ci/build.coffee index 93385b32a13..44d5ddb7d95 100644 --- a/app/assets/javascripts/ci/build.coffee +++ b/app/assets/javascripts/ci/build.coffee @@ -31,7 +31,7 @@ class CiBuild $('#build-trace code').html build.trace_html $('#build-trace code').append '' @checkAutoscroll() - else + else if build.status != build_status Turbolinks.visit build_url , 4000 diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 816012762ce..ad0adc17866 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -9,16 +9,17 @@ class Projects::BuildsController < Projects::ApplicationController def index @scope = params[:scope] @all_builds = project.ci_builds + @builds = @all_builds.order('created_at DESC') @builds = case @scope when 'all' - @all_builds + @builds when 'finished' - @all_builds.finished + @builds.finished else - @all_builds.running_or_pending + @builds.running_or_pending.reverse_order end - @builds = @builds.order('created_at DESC').page(params[:page]).per(30) + @builds = @builds.page(params[:page]).per(30) end def cancel_all diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 8188ba3a28e..0b73ab6d2eb 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -20,7 +20,6 @@ class CommitStatus < ActiveRecord::Base 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 event :run do diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml index f45cd05aec0..809fc52dbdf 100644 --- a/app/views/ci/lints/_create.html.haml +++ b/app/views/ci/lints/_create.html.haml @@ -17,7 +17,7 @@ %td #{stage.capitalize} Job - #{build[:name]} %td %pre - = simple_format build[:script] + = simple_format build[:commands] %br %b Tag list: @@ -28,6 +28,11 @@ %br %b Refs except: = build[:except] && build[:except].join(", ") + %br + %b When: + = build[:when] + - if build[:allow_failure] + %b Allowed to fail -else %p diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 3a8172dc8e6..e3d8d734913 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -155,7 +155,7 @@ - if @builds.present? .build-widget - %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}: + %h4.title #{pluralize(@builds.count(:id), "other build")} for #{@build.short_sha}: %table.table.builds - @builds.each_with_index do |build, i| %tr.build -- cgit v1.2.1 From 3adfee1c8724d56e051da21e18d83435e8b6ba31 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 11:41:22 +0200 Subject: Allow developer to manage builds --- app/controllers/ci/application_controller.rb | 8 -------- app/controllers/projects/builds_controller.rb | 8 +++++++- app/controllers/projects/commit_controller.rb | 11 ++++++++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 9be470660e6..848f2b4e314 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -8,14 +8,6 @@ module Ci private - def authenticate_public_page! - unless project.public - authenticate_user! - - return access_denied! unless can?(current_user, :read_project, gl_project) - end - end - def authenticate_token! unless project.valid_token?(params[:token]) return head(403) diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index ad0adc17866..7d72e0b951b 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -2,7 +2,7 @@ class Projects::BuildsController < Projects::ApplicationController before_action :ci_project before_action :build, except: [:index, :cancel_all] - before_action :authorize_admin_project!, except: [:index, :show, :status] + before_action :authorize_manage_builds!, except: [:index, :show, :status] layout "project" @@ -74,4 +74,10 @@ class Projects::BuildsController < Projects::ApplicationController def build_path(build) namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) end + + def authorize_manage_builds! + unless can?(current_user, :manage_builds, project) + return page_404 + end + end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 7886f3c6deb..878c3a66e7d 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -4,7 +4,8 @@ class Projects::CommitController < Projects::ApplicationController # Authorize before_action :require_non_empty_project - before_action :authorize_download_code! + before_action :authorize_download_code!, except: [:cancel_builds] + before_action :authorize_manage_builds!, only: [:cancel_builds] before_action :commit def show @@ -55,4 +56,12 @@ class Projects::CommitController < Projects::ApplicationController def commit @commit ||= @project.commit(params[:id]) end + + private + + def authorize_manage_builds! + unless can?(current_user, :manage_builds, project) + return page_404 + end + end end -- cgit v1.2.1 From f230b42f8442f1e8d29bec7c5f84f73e02c9c845 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 11:41:50 +0200 Subject: On CI Admin page show only projects that are present in GitLab --- app/models/ci/project.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index eb65c773570..4e806ca1a68 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -99,6 +99,7 @@ module Ci def ordered_by_last_commit_date last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)" joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). + joins(:gl_project). order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") end end -- cgit v1.2.1 From 2afb2d3c6788d14039c64dcc2b1ee290c48a0de4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 12:41:17 +0200 Subject: Fix broken Runners admin page --- app/controllers/ci/admin/runners_controller.rb | 1 + app/views/ci/admin/runners/show.html.haml | 30 +++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb index 110954a612d..0cafad27418 100644 --- a/app/controllers/ci/admin/runners_controller.rb +++ b/app/controllers/ci/admin/runners_controller.rb @@ -17,6 +17,7 @@ module Ci @projects = @projects.where(gitlab_id: @gl_projects.select(:id)) end @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? + @projects = @projects.joins(:gl_project) @projects = @projects.page(params[:page]).per(30) end diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 92787b2e6ac..02b53612663 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -53,13 +53,14 @@ %th - @runner.runner_projects.each do |runner_project| - project = runner_project.project - %tr.alert-info - %td - %strong - = project.name - %td - .pull-right - = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + - if project.gl_project + %tr.alert-info + %td + %strong + = project.name + %td + .pull-right + = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' %table.table %thead @@ -103,21 +104,26 @@ %th Finished at - @builds.each do |build| + - gl_project = build.gl_project %tr.build %td.id - - gl_project = build.project.gl_project - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do + - if gl_project + = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do + = build.id + - else = build.id %td.status = ci_status_with_icon(build.status) %td.status - = build.project.name + - if gl_project + = gl_project.name_with_namespace %td.build-link - = link_to ci_status_path(build.commit) do - %strong #{build.commit.short_sha} + - if gl_project + = link_to ci_status_path(build.commit) do + %strong #{build.commit.short_sha} %td.timestamp - if build.finished_at -- cgit v1.2.1 From 03ea0c74af8bfe6600eb4a7cc04a5d6cfc677f59 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 13:13:22 +0200 Subject: Use git follow flag for commits page when retrieve history for file or directory Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/models/repository.rb | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c70cc33cce2..19a5876c4f1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.2.0 (unreleased) - Allow users to select the Files view as default project view (Cristian Bica) - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork + - Use git follow flag for commits page when retrieve history for file or directory v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index 88d3d73a40e..a517c7d6982 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -67,13 +67,16 @@ class Repository end def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false) - commits = Gitlab::Git::Commit.where( + options = { repo: raw_repository, ref: ref, path: path, limit: limit, offset: offset, - ) + } + + options[:follow] = true if path.present? + commits = Gitlab::Git::Commit.where(options) commits = Commit.decorate(commits, @project) if commits.present? commits end -- cgit v1.2.1 From 0b25d837b8b97010a5ea65002ca09f1aa7f3e504 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 13:33:44 +0200 Subject: Fail builds that also have empty string --- db/migrate/20151023112551_fail_build_with_empty_name.rb | 5 +++++ db/schema.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151023112551_fail_build_with_empty_name.rb diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb new file mode 100644 index 00000000000..f069bc60ac7 --- /dev/null +++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb @@ -0,0 +1,5 @@ +class FailBuildWithEmptyName < ActiveRecord::Migration + def change + execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'") + end +end diff --git a/db/schema.rb b/db/schema.rb index 0fec00ebf8f..1551956c8bc 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: 20151020173906) do +ActiveRecord::Schema.define(version: 20151023112551) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 5921a1edf3eacc23ed25f39d052ad4db4aac91e2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 13:37:37 +0200 Subject: Fixed nesting for Allowed to fail --- app/views/ci/lints/_create.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/ci/lints/_create.html.haml b/app/views/ci/lints/_create.html.haml index 809fc52dbdf..77f78caa8d8 100644 --- a/app/views/ci/lints/_create.html.haml +++ b/app/views/ci/lints/_create.html.haml @@ -32,7 +32,7 @@ %b When: = build[:when] - if build[:allow_failure] - %b Allowed to fail + %b Allowed to fail -else %p -- cgit v1.2.1 From 9f2e1f504df2fa9ef7d658cbc73bbdba6f897eda Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 14:39:27 +0200 Subject: Refactor git follow option set Signed-off-by: Dmitriy Zaporozhets --- app/models/repository.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index a517c7d6982..9d68d8a6dfd 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -73,9 +73,9 @@ class Repository path: path, limit: limit, offset: offset, + follow: path.present? } - options[:follow] = true if path.present? commits = Gitlab::Git::Commit.where(options) commits = Commit.decorate(commits, @project) if commits.present? commits -- cgit v1.2.1 From 7851a292a1fc7da3cd2d1140cd40f35009a9c082 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 23 Oct 2015 15:00:26 +0200 Subject: Use single_line Markdown pipeline for commit description --- app/views/projects/commits/show.atom.builder | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 7aab98e07c4..a40720ab4a8 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.name commit.author_name xml.email commit.author_email end - xml.summary markdown(commit.description, pipeline: :atom) + xml.summary markdown(commit.description, pipeline: :single_line) end end end -- cgit v1.2.1 From 201641594d66b04d811f99e106466c45ba7fc1e6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Sep 2015 18:09:17 -0400 Subject: Vendor clipboard.js --- vendor/assets/javascripts/clipboard.js | 621 +++++++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 vendor/assets/javascripts/clipboard.js diff --git a/vendor/assets/javascripts/clipboard.js b/vendor/assets/javascripts/clipboard.js new file mode 100644 index 00000000000..1b1f4f0bd63 --- /dev/null +++ b/vendor/assets/javascripts/clipboard.js @@ -0,0 +1,621 @@ +/*! + * clipboard.js v1.4.2 + * https://zenorocha.github.io/clipboard.js + * + * Licensed MIT © Zeno Rocha + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Date: Wed, 30 Sep 2015 18:09:45 -0400 Subject: Add copy_to_clipboard JS --- app/assets/javascripts/copy_to_clipboard.js.coffee | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 app/assets/javascripts/copy_to_clipboard.js.coffee diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee new file mode 100644 index 00000000000..ec4b80cca6f --- /dev/null +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -0,0 +1,21 @@ +#= require clipboard + +$ -> + clipboard = new Clipboard '.js-clipboard-trigger', + text: (trigger) -> + $target = $(trigger.nextElementSibling || trigger.previousElementSibling) + $target.data('clipboard-text') || $target.text().trim() + + clipboard.on 'success', (e) -> + $(e.trigger). + tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). + tooltip('show') + + # Clear the selection and blur the trigger so it loses its border + e.clearSelection() + $(e.trigger).blur() + + # Manually hide the tooltip after 1 second + setTimeout(-> + $(e.trigger).tooltip('hide') + , 1000) -- cgit v1.2.1 From 831deeeac3b0de16f65ae67f5c0a249a95ba3079 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 23 Oct 2015 15:29:53 +0200 Subject: Add styling for cross-project-reference clipboard button --- app/assets/stylesheets/pages/issuable.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index c60aa5c7fe7..abc27a19e32 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -88,3 +88,16 @@ padding: 13px 0; } } + +.cross-project-reference { + text-align: center; + width: 100%; + + .slead { + padding: 5px; + } + + span, button { + background-color: $background-color; + } +} -- cgit v1.2.1 From 187625831f7b501ff6689707d1859b9c7ec4f5e0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 23 Oct 2015 15:31:43 +0200 Subject: Add "Copy to clipboard" buttons Adds buttons to the commit list SHAs and the cross-project reference data on issuables. --- app/helpers/clipboard_helper.rb | 8 ++++++++ app/views/projects/commits/_commit.html.haml | 3 ++- app/views/projects/issues/_discussion.html.haml | 6 ++++-- 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 app/helpers/clipboard_helper.rb diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb new file mode 100644 index 00000000000..9659507bf07 --- /dev/null +++ b/app/helpers/clipboard_helper.rb @@ -0,0 +1,8 @@ +module ClipboardHelper + def clipboard_button(target = nil) + content_tag :button, + icon('clipboard'), + class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + type: :button + end +end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index cddd5aa3a83..e80cc194d29 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -21,7 +21,8 @@ = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do = ci_status_icon(ci_commit)   - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" + = clipboard_button + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id} .notes_count - if note_count > 0 diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index d4a98eca473..c5fd863ae99 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -17,8 +17,10 @@ - @participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) .col-md-3 - %span.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @issue) + .input-group.cross-project-reference + %span.slead.has_tooltip{title: 'Cross-project reference'} + = cross_project_reference(@project, @issue) + = clipboard_button .row %section.col-md-9 -- cgit v1.2.1 From 8010872579dbb28b8f075059c3267356ca793b99 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 23 Oct 2015 15:44:39 +0200 Subject: Add clipboard buttons to each step of "How to merge" --- app/assets/stylesheets/pages/merge_requests.scss | 9 +++++++++ app/views/projects/merge_requests/show/_how_to_merge.html.haml | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index a1a5208c59c..f0b3667acca 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -205,6 +205,15 @@ #modal_merge_info .modal-dialog { width: 600px; + + .btn-clipboard { + @extend .pull-right; + + margin-right: 18px; + margin-top: 5px; + position: absolute; + right: 0; + } } .mr-source-target { diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index f18cf96c17d..98f0357ce4e 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -3,11 +3,12 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3 Check out, review and merge locally + %h3 Check out, review, and merge locally .modal-body %p - %strong Step 1. + %strong Step 1. Fetch and check out the branch for this merge request + = clipboard_button %pre.dark - if @merge_request.for_fork? :preserve @@ -24,6 +25,7 @@ %p %strong Step 3. Merge the branch and fix any conflicts that come up + = clipboard_button %pre.dark - if @merge_request.for_fork? :preserve @@ -36,6 +38,7 @@ %p %strong Step 4. Push the result of the merge to GitLab + = clipboard_button %pre.dark :preserve git push origin #{h @merge_request.target_branch} -- cgit v1.2.1 From 03cfda94225ad3f6d76d39b52e89f714dde48ecd Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 23 Oct 2015 15:59:25 +0200 Subject: Remove unused argument from clipboard_button helper --- app/helpers/clipboard_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb index 9659507bf07..3c1d7569fac 100644 --- a/app/helpers/clipboard_helper.rb +++ b/app/helpers/clipboard_helper.rb @@ -1,5 +1,5 @@ module ClipboardHelper - def clipboard_button(target = nil) + def clipboard_button content_tag :button, icon('clipboard'), class: 'btn btn-xs btn-clipboard js-clipboard-trigger', -- cgit v1.2.1 From fe6ec80e75d35b8ead20e1cf8f797cc3ffee3f9b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 17:01:10 +0200 Subject: Render CI status on merge requests index page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/models/merge_request.rb | 10 ++++++++-- app/views/projects/merge_requests/_merge_request.html.haml | 4 ++++ app/views/projects/merge_requests/widget/_heading.html.haml | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2c4afc00ab6..53da0148ff3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.2.0 (unreleased) - Show "Empty Repository Page" for repository without branches (Artem V. Navrotskiy) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork - Use git follow flag for commits page when retrieve history for file or directory + - Show merge request CI status on merge requests index page v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8d9ad44681d..85f37e49e62 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -159,11 +159,11 @@ class MergeRequest < ActiveRecord::Base def last_commit merge_request_diff ? merge_request_diff.last_commit : compare_commits.last - end + end def first_commit merge_request_diff ? merge_request_diff.first_commit : compare_commits.first - end + end def last_commit_short_sha last_commit.short_id @@ -470,4 +470,10 @@ class MergeRequest < ActiveRecord::Base unlock_mr if locked? end end + + def ci_commit + if last_commit + source_project.ci_commit(last_commit.id) + end + end end diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 25e4e8ba80d..f9409be5cfa 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,3 +1,4 @@ +- ci_commit = merge_request.ci_commit %li{ class: mr_css_classes(merge_request) } .merge-request-title %span.merge-request-title-text @@ -6,6 +7,9 @@ - merge_request.labels.each do |label| = link_to_label(label, project: merge_request.project) .pull-right.light + - if ci_commit + = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do + = ci_status_icon(ci_commit) - if merge_request.merged? %span %i.fa.fa-check diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 10efb811939..a3551516bfe 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,4 +1,4 @@ -- ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha) +- ci_commit = @merge_request.ci_commit - if ci_commit - status = ci_commit.status .mr-widget-heading -- cgit v1.2.1 From 7b06c83cf72d24edab7adcc00f4000830539506e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 17:01:25 +0200 Subject: Add tests for MR index page Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/merge_requests/_merge_request.html.haml | 3 ++- features/project/merge_requests.feature | 6 ++++++ features/steps/project/merge_requests.rb | 13 +++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index f9409be5cfa..a285d5d660e 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -8,7 +8,8 @@ = link_to_label(label, project: merge_request.project) .pull-right.light - if ci_commit - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do + = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", + title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do = ci_status_icon(ci_commit) - if merge_request.merged? %span diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 83055188bac..f423c3ba542 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -10,6 +10,12 @@ Feature: Project Merge Requests Then I should see "Bug NS-04" in merge requests And I should not see "Feature NS-03" in merge requests + Scenario: I should see CI status for merge requests + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + Given "Bug NS-05" has CI status + When I visit project "Shop" merge requests page + Then I should see merge request "Bug NS-05" with CI status + Scenario: I should see rejected merge requests Given I click link "Closed" Then I should see "Feature NS-03" in merge requests diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 875bf6c4676..da34ed758c3 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -338,6 +338,19 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps expect(page).to have_content('diff --git') end + step '"Bug NS-05" has CI status' do + project = merge_request.source_project + project.enable_ci + ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id + create :ci_build, commit: ci_commit + end + + step 'I should see merge request "Bug NS-05" with CI status' do + page.within ".mr-list" do + expect(page).to have_link "Build status: pending" + end + end + def merge_request @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05") end -- cgit v1.2.1 From 696a7084010315078c38507cccabb236bb3d794a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 17:32:52 +0200 Subject: Move rendering CI status to helper Signed-off-by: Dmitriy Zaporozhets --- app/helpers/ci_status_helper.rb | 7 +++++++ app/views/projects/commits/_commit.html.haml | 3 +-- app/views/projects/merge_requests/_merge_request.html.haml | 4 +--- app/views/shared/projects/_project.html.haml | 4 +--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index dbd1e26fa79..ceafe5e8c91 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -42,4 +42,11 @@ module CiStatusHelper icon(icon_name) end + + def render_ci_status(ci_commit) + link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", + title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do + ci_status_icon(ci_commit) + end + end end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index cddd5aa3a83..b7998dd12c1 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -18,8 +18,7 @@ .pull-right - if ci_commit - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}" do - = ci_status_icon(ci_commit) + = render_ci_status(ci_commit)   = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index a285d5d660e..300a3715292 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -8,9 +8,7 @@ = link_to_label(label, project: merge_request.project) .pull-right.light - if ci_commit - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do - = ci_status_icon(ci_commit) + = render_ci_status(ci_commit) - if merge_request.merged? %span %i.fa.fa-check diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index aee839b44e7..c36995b94d7 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -21,9 +21,7 @@ .project-controls - if ci && !project.empty_repo? && project.commit - if ci_commit = project.ci_commit(project.commit.sha) - = link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do - = ci_status_icon(ci_commit) + = render_ci_status(ci_commit)   - if stars %span -- cgit v1.2.1 From 122f02bc39c690b7fef69f57075380ca82c25b46 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 23 Oct 2015 16:51:44 +0200 Subject: Remove the contents of the satellites dir --- config/gitlab.yml.example | 6 ++++-- config/initializers/1_settings.rb | 4 +++- db/migrate/20151023144219_remove_satellites.rb | 17 +++++++++++++++++ db/schema.rb | 2 +- 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20151023144219_remove_satellites.rb diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8b85981497a..d3aef44705b 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -318,10 +318,12 @@ production: &base # ========================== # GitLab Satellites + # + # Note for maintainers: keep the satellites.path setting until GitLab 9.0 at + # least. This setting is fed to 'rm -rf' in + # db/migrate/20151023144219_remove_satellites.rb satellites: - # Relative paths are relative to Rails.root (default: tmp/repo_satellites/) path: /home/git/gitlab-satellites/ - timeout: 30 ## Backup settings backup: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index d5493ca038d..65e9b0dcb50 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -242,9 +242,11 @@ Settings.git['max_size'] ||= 20971520 # 20.megabytes Settings.git['bin_path'] ||= '/usr/bin/git' Settings.git['timeout'] ||= 10 +# Important: keep the satellites.path setting until GitLab 9.0 at +# least. This setting is fed to 'rm -rf' in +# db/migrate/20151023144219_remove_satellites.rb Settings['satellites'] ||= Settingslogic.new({}) Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root) -Settings.satellites['timeout'] ||= 30 # # Extra customization diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb new file mode 100644 index 00000000000..e73f300028a --- /dev/null +++ b/db/migrate/20151023144219_remove_satellites.rb @@ -0,0 +1,17 @@ +require 'fileutils' + +class RemoveSatellites < ActiveRecord::Migration + def up + satellites = Gitlab.config['satellites'] + return if satellites.nil? + + satellites_path = satellites['path'] + return if satellites_path.nil? + + FileUtils.rm_rf(satellites_path) + end + + def down + # Do nothing + end +end diff --git a/db/schema.rb b/db/schema.rb index 1551956c8bc..0fe113325fa 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: 20151023112551) do +ActiveRecord::Schema.define(version: 20151023144219) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 5615f2737cb034d3d8de70e1e50f1ba8ca78c34d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 23 Oct 2015 18:21:43 +0200 Subject: Fix rubocop issues Signed-off-by: Dmitriy Zaporozhets --- app/helpers/ci_status_helper.rb | 6 ++++-- features/steps/project/merge_requests.rb | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index ceafe5e8c91..ed88df5dd86 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -44,8 +44,10 @@ module CiStatusHelper end def render_ci_status(ci_commit) - link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", data: {toggle: 'tooltip', placement: 'left'} do + link_to ci_status_path(ci_commit), + class: "c#{ci_status_color(ci_commit)}", + title: "Build status: #{ci_commit.status}", + data: { toggle: 'tooltip', placement: 'left' } do ci_status_icon(ci_commit) end end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index da34ed758c3..92ec14d0d76 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -345,11 +345,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps create :ci_build, commit: ci_commit end - step 'I should see merge request "Bug NS-05" with CI status' do - page.within ".mr-list" do - expect(page).to have_link "Build status: pending" - end - end + step 'I should see merge request "Bug NS-05" with CI status' do + page.within ".mr-list" do + expect(page).to have_link "Build status: pending" + end + end def merge_request @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05") -- cgit v1.2.1 From b2663d1ca171c62435c292481ff5b45eb4230f4b Mon Sep 17 00:00:00 2001 From: Derek Robati Date: Fri, 23 Oct 2015 12:48:17 -0400 Subject: Added missing period. --- doc/workflow/gitlab_flow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md index f608674faf6..9a24a1e252a 100644 --- a/doc/workflow/gitlab_flow.md +++ b/doc/workflow/gitlab_flow.md @@ -26,7 +26,7 @@ After getting used to these three steps the branching model becomes the challeng Since many organizations new to git have no conventions how to work with it, it can quickly become a mess. The biggest problem they run into is that many long running branches that each contain part of the changes are around. People have a hard time figuring out which branch they should develop on or deploy to production. -Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html) +Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html). We think there is still room for improvement and will detail a set of practices we call GitLab flow. ## Git flow and its problems -- cgit v1.2.1 From 7e441bbb4023ed7531c23b2d2e5f063ccc820c40 Mon Sep 17 00:00:00 2001 From: Jannick Fahlbusch Date: Fri, 23 Oct 2015 23:37:28 +0200 Subject: Add missing character This adds the missing character `a` to the help-text --- app/views/projects/ci_settings/_no_runners.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/ci_settings/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml index 33038c52978..1374e6680f9 100644 --- a/app/views/projects/ci_settings/_no_runners.html.haml +++ b/app/views/projects/ci_settings/_no_runners.html.haml @@ -5,4 +5,4 @@ You can add Specific runner for this project on Runners page - if current_user.admin - or add Shared runner for whole application in admin are. + or add Shared runner for whole application in admin area. -- cgit v1.2.1 From 45399ccc9a90e575ca9b98d2acf7a6a8c526af68 Mon Sep 17 00:00:00 2001 From: Jannick Fahlbusch Date: Sat, 24 Oct 2015 12:37:44 +0200 Subject: Fix some grammatical issues --- app/views/ci/admin/runner_projects/index.html.haml | 2 +- app/views/ci/admin/runners/index.html.haml | 4 ++-- app/views/ci/admin/runners/show.html.haml | 4 ++-- app/views/ci/user_sessions/new.html.haml | 3 +-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml index f049b4f4c4e..6b4e3b2cb38 100644 --- a/app/views/ci/admin/runner_projects/index.html.haml +++ b/app/views/ci/admin/runner_projects/index.html.haml @@ -1,5 +1,5 @@ %p.lead - To register new runner visit #{link_to 'this page ', ci_runners_path} + To register a new runner visit #{link_to 'this page ', ci_runners_path} .row .col-md-8 diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index bb213fbffc4..bacaccfbffa 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -1,5 +1,5 @@ %p.lead - %span To register new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. + %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. %code #{GitlabCi::REGISTRATION_TOKEN} .bs-callout @@ -21,7 +21,7 @@ \- run builds from assigned projects %li %span.label.label-danger paused - \- runner will not receive any new build + \- runner will not receive any new builds .append-bottom-20.clearfix .pull-left diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 02b53612663..1498db46a80 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -13,13 +13,13 @@ - if @runner.shared? .bs-callout.bs-callout-success - %h4 This runner will process build from ALL UNASSIGNED projects + %h4 This runner will process builds from ALL UNASSIGNED projects %p If you want runners to build only specific projects, enable them in the table below. Keep in mind that this is a one way transition. - else .bs-callout.bs-callout-info - %h4 This runner will process build only from ASSIGNED projects + %h4 This runner will process builds only from ASSIGNED projects %p You can't make this a shared runner. %hr = form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml index 308b217ea78..b8d9a1d7089 100644 --- a/app/views/ci/user_sessions/new.html.haml +++ b/app/views/ci/user_sessions/new.html.haml @@ -1,8 +1,7 @@ .login-block %h2 Login using GitLab account %p.light - Make sure you have account on GitLab server + Make sure you have an account on the GitLab server = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink %hr = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' ) - -- cgit v1.2.1 From ab72c12028072e9508ee730e39a68974b2a714f2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 24 Oct 2015 15:58:43 +0200 Subject: Remove "Install schedules" step from Installation guide [ci skip] --- doc/install/installation.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 2e9ac7393e3..36d6ec79fde 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -346,11 +346,6 @@ The `secrets.yml` file stores encryption keys for sessions and secure variables. Backup `secrets.yml` someplace safe, but don't store it in the same place as your database backups. Otherwise your secrets are exposed if one of your backups is compromised. -### Install schedules - - # Setup schedules - sudo -u gitlab_ci -H bundle exec whenever -w RAILS_ENV=production - ### Install Init Script Download the init script (will be `/etc/init.d/gitlab`): -- cgit v1.2.1 From c3d48f97355371d6c8760e05637f666f23c2a76a Mon Sep 17 00:00:00 2001 From: kazubu Date: Mon, 26 Oct 2015 00:02:32 +0900 Subject: Fix: 500 error returned if destroy request without HTTP referer --- CHANGELOG | 1 + app/controllers/projects_controller.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 53da0148ff3..2b69b383603 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.2.0 (unreleased) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page + - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 82119022cf9..743c429b72e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -124,7 +124,7 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute flash[:alert] = "Project '#{@project.name}' was deleted." - if request.referer.include?('/admin') + if request.referer.present? && request.referer.include?('/admin') redirect_to admin_namespaces_projects_path else redirect_to dashboard_projects_path -- cgit v1.2.1 From 0bfb9cbf38c72f801255b910430fdbff6536b73d Mon Sep 17 00:00:00 2001 From: kazubu Date: Mon, 26 Oct 2015 14:58:09 +0900 Subject: modify to use redirect_back_or_default function --- app/controllers/projects_controller.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 743c429b72e..05c7d3de8bc 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -124,11 +124,7 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute flash[:alert] = "Project '#{@project.name}' was deleted." - if request.referer.present? && request.referer.include?('/admin') - redirect_to admin_namespaces_projects_path - else - redirect_to dashboard_projects_path - end + redirect_back_or_default(default: dashboard_projects_path, options: {}) rescue Projects::DestroyService::DestroyError => ex redirect_to edit_project_path(@project), alert: ex.message end -- cgit v1.2.1 From eb3c4255bf1926a1edf02c2a4792feaaf6fe4091 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 26 Oct 2015 10:14:35 +0100 Subject: Bump nprogress-rails to 0.1.6.7 Closes #2866 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 9254ce2ccfa..f72ca7aa539 100644 --- a/Gemfile +++ b/Gemfile @@ -201,7 +201,7 @@ gem 'jquery-atwho-rails', '~> 1.0.0' gem 'jquery-rails', '~> 3.1.3' gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-ui-rails', '~> 4.2.1' -gem 'nprogress-rails', '~> 0.1.2.3' +gem 'nprogress-rails', '~> 0.1.6.7' gem 'raphael-rails', '~> 2.1.2' gem 'request_store', '~> 1.2.0' gem 'select2-rails', '~> 3.5.9' diff --git a/Gemfile.lock b/Gemfile.lock index 53122898b07..7016c59e0df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -406,7 +406,7 @@ GEM newrelic_rpm (3.9.4.245) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) - nprogress-rails (0.1.2.3) + nprogress-rails (0.1.6.7) oauth (0.4.7) oauth2 (1.0.0) faraday (>= 0.8, < 0.10) @@ -854,7 +854,7 @@ DEPENDENCIES nested_form (~> 0.3.2) newrelic-grape newrelic_rpm (~> 3.9.4.245) - nprogress-rails (~> 0.1.2.3) + nprogress-rails (~> 0.1.6.7) oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) -- cgit v1.2.1 From 63c74bb0eee81b96ce675d5875f2bbad04362353 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 26 Oct 2015 10:39:33 +0100 Subject: Bump jquery-atwho-rails to ~> 1.3.2 Closes #2858 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 9254ce2ccfa..39ec04951c0 100644 --- a/Gemfile +++ b/Gemfile @@ -197,7 +197,7 @@ gem 'bootstrap-sass', '~> 3.0' gem 'font-awesome-rails', '~> 4.2' gem 'gitlab_emoji', '~> 0.1' gem 'gon', '~> 5.0.0' -gem 'jquery-atwho-rails', '~> 1.0.0' +gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-rails', '~> 3.1.3' gem 'jquery-scrollto-rails', '~> 1.4.3' gem 'jquery-ui-rails', '~> 4.2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 53122898b07..340eb0fc301 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -354,7 +354,7 @@ GEM ice_nine (0.11.1) inflecto (0.0.2) ipaddress (0.8.0) - jquery-atwho-rails (1.0.1) + jquery-atwho-rails (1.3.2) jquery-rails (3.1.3) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) @@ -840,7 +840,7 @@ DEPENDENCIES hipchat (~> 1.5.0) html-pipeline (~> 1.11.0) httparty (~> 0.13.3) - jquery-atwho-rails (~> 1.0.0) + jquery-atwho-rails (~> 1.3.2) jquery-rails (~> 3.1.3) jquery-scrollto-rails (~> 1.4.3) jquery-turbolinks (~> 2.0.1) -- cgit v1.2.1 From 6db014987d3c9cd4595adad70bb8a11ccacf9545 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 23 Oct 2015 18:15:13 +0200 Subject: Fix specific runner visibility --- CHANGELOG | 1 + app/controllers/projects/runners_controller.rb | 9 ++++----- app/models/ci/runner.rb | 1 + app/models/user.rb | 23 ++++++++++++++--------- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 53da0148ff3..215a8d27743 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.1.0 - Speed up load times of issue detail pages by roughly 1.5x - Require CI jobs to be named - Fix CI rendering regressions + - Fix specific runners visibility - Allow developer to manage builds - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg) - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index deb07a21416..bfbcf2567f3 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -6,11 +6,10 @@ class Projects::RunnersController < Projects::ApplicationController layout 'project_settings' def index - @runners = @ci_project.runners.order('id DESC') - @specific_runners = - Ci::Runner.specific.includes(:runner_projects). - where(Ci::RunnerProject.table_name => { project_id: current_user.authorized_projects } ). - where.not(id: @runners).order("#{Ci::Runner.table_name}.id DESC").page(params[:page]).per(20) + @runners = @ci_project.runners.ordered + @specific_runners = current_user.ci_authorized_runners. + where.not(id: @ci_project.runners). + ordered.page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 1b3669f1b7a..b719ad3c87e 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -36,6 +36,7 @@ module Ci scope :active, ->() { where(active: true) } scope :paused, ->() { where(active: false) } scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } + scope :ordered, ->() { order(id: :desc) } acts_as_taggable diff --git a/app/models/user.rb b/app/models/user.rb index 7e4321d5376..c72beacbf0f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -401,15 +401,17 @@ class User < ActiveRecord::Base end end + def authorized_projects_id + @authorized_projects_id ||= begin + project_ids = personal_projects.pluck(:id) + project_ids.push(*groups_projects.pluck(:id)) + project_ids.push(*projects.pluck(:id).uniq) + end + end # Projects user has access to def authorized_projects - @authorized_projects ||= begin - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.pluck(:id)) - project_ids.push(*projects.pluck(:id).uniq) - Project.where(id: project_ids) - end + @authorized_projects ||= Project.where(id: authorized_projects_id) end def owned_projects @@ -768,11 +770,14 @@ class User < ActiveRecord::Base end def ci_authorized_projects - @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects) + @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id) end def ci_authorized_runners - Ci::Runner.specific.includes(:runner_projects). - where(ci_runner_projects: { project_id: ci_authorized_projects } ) + @ci_authorized_runners ||= begin + runner_ids = Ci::RunnerProject.joins(:project). + where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id) + Ci::Runner.specific.where(id: runner_ids) + end end end -- cgit v1.2.1 From 271ad4e354d10c7ff59f57a1852c5a97e3621273 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 26 Oct 2015 12:23:34 +0100 Subject: Fix CI badge The previous code relied on having on ref stored in commit, however the ref was moved to the build. --- CHANGELOG | 1 + app/models/ci/project_status.rb | 4 ---- app/services/ci/image_for_build_service.rb | 16 +++++++--------- spec/services/ci/image_for_build_service_spec.rb | 5 +++-- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 53da0148ff3..b0b80f4abf8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,6 +25,7 @@ v 8.1.0 - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu) - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) - Speed up load times of issue detail pages by roughly 1.5x + - Fix CI badge - Require CI jobs to be named - Fix CI rendering regressions - Allow developer to manage builds diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb index b66f1212f23..2d35aeac225 100644 --- a/app/models/ci/project_status.rb +++ b/app/models/ci/project_status.rb @@ -27,9 +27,5 @@ module Ci def human_status status end - - def last_commit_for_ref(ref) - commits.where(ref: ref).last - end end end diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb index b95835ba093..b8d24193035 100644 --- a/app/services/ci/image_for_build_service.rb +++ b/app/services/ci/image_for_build_service.rb @@ -1,17 +1,15 @@ module Ci class ImageForBuildService def execute(project, params) - image_name = - if params[:sha] - commit = project.commits.find_by(sha: params[:sha]) - image_for_commit(commit) - elsif params[:ref] - commit = project.last_commit_for_ref(params[:ref]) - image_for_commit(commit) - else - 'build-unknown.svg' + sha = params[:sha] + sha ||= + if params[:ref] + project.gl_project.commit(params[:ref]).try(:sha) end + commit = project.commits.ordered.find_by(sha: sha) + image_name = image_for_commit(commit) + image_path = Rails.root.join('public/ci', image_name) OpenStruct.new( diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index d7242d684c6..cda7d0c4a51 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -4,8 +4,9 @@ module Ci describe ImageForBuildService do let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project, ref: 'master') } + let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) } + let(:commit_sha) { gl_project.commit('master').sha } + let(:commit) { gl_project.ensure_ci_commit(commit_sha) } let(:build) { FactoryGirl.create(:ci_build, commit: commit) } describe :execute do -- cgit v1.2.1 From 0b082b348b517af854265eaff530b2b6d3cfd7b2 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 26 Oct 2015 13:42:09 +0100 Subject: Start putting shared files in "shared" --- CONTRIBUTING.md | 1 + config/gitlab.yml.example | 4 ++++ config/initializers/1_settings.rb | 6 +++++- doc/development/shared_files.md | 33 +++++++++++++++++++++++++++++++++ shared/.gitkeep | 0 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 doc/development/shared_files.md create mode 100644 shared/.gitkeep diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 69abadb151a..9f79ff413a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,6 +83,7 @@ If you can, please submit a merge request with the fix or improvements including 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. +1. If your code creates new files on disk please read the [shared files guidelines](doc/development/shared_files.md). The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases. diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8b85981497a..ad0f76e8d04 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -310,6 +310,10 @@ production: &base # application_name: 'YOUR_APP_NAME', # application_password: 'YOUR_APP_PASSWORD' } } + # Shared file storage settings + shared: + # path: /mnt/gitlab # Default: shared + diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index d5493ca038d..75b6eadf617 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -125,6 +125,9 @@ Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link Settings.omniauth['providers'] ||= [] +Settings['shared'] ||= Settingslogic.new({}) +Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root) + Settings['issues_tracker'] ||= {} # @@ -169,7 +172,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) -Settings.gitlab['repository_downloads_path'] = File.absolute_path(Settings.gitlab['repository_downloads_path'] || 'tmp/repositories', Rails.root) +Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil? Settings.gitlab['restricted_signup_domains'] ||= [] Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] @@ -246,6 +249,7 @@ Settings['satellites'] ||= Settingslogic.new({}) Settings.satellites['path'] = File.expand_path(Settings.satellites['path'] || "tmp/repo_satellites/", Rails.root) Settings.satellites['timeout'] ||= 30 + # # Extra customization # diff --git a/doc/development/shared_files.md b/doc/development/shared_files.md new file mode 100644 index 00000000000..cffe6f79193 --- /dev/null +++ b/doc/development/shared_files.md @@ -0,0 +1,33 @@ +# Shared files + +Historically, GitLab has been storing shared files in many different +directories: `public/uploads`, `builds`, `tmp/repositories`, `tmp/rebase` (EE), +etc. Having so many shared directories makes it difficult to deploy GitLab on +shared storage (e.g. NFS). Working towards GitLab 9.0 we are consolidating +these different directories under the `shared` directory. + +This means that if GitLab will start storing puppies in some future version +then we should put them in `shared/puppies`. Temporary files should be stored +in `shared/tmp`. + +In the GitLab application code you can get the full path to the `shared` +directory with `Gitlab.config.shared.path`. + +## What is not a 'shared file' + +Files that belong to only one process, or on only one server, should not go in +`shared`. Examples include PID files and sockets. + +## Temporary files and shared storage + +Sometimes you create a temporary file on disk with the intention of it becoming +'official'. For example you might be first streaming an upload from a user to +disk in a temporary file so you can perform some checks on it. When the checks +pass, you make the file official. In scenarios like this please follow these +rules: + +- Store the temporary file under `shared/tmp`, i.e. on the same filesystem you + want the official file to be on. +- Use move/rename operations when operating on the file instead of copy + operations where possible, because renaming a file is much faster than + copying it. diff --git a/shared/.gitkeep b/shared/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 5ff830cf32908c0efc156e439a0486cfb40eeadb Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 26 Oct 2015 14:28:31 +0100 Subject: Clarify puppies --- doc/development/shared_files.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/development/shared_files.md b/doc/development/shared_files.md index cffe6f79193..fcd905b54a4 100644 --- a/doc/development/shared_files.md +++ b/doc/development/shared_files.md @@ -7,8 +7,8 @@ shared storage (e.g. NFS). Working towards GitLab 9.0 we are consolidating these different directories under the `shared` directory. This means that if GitLab will start storing puppies in some future version -then we should put them in `shared/puppies`. Temporary files should be stored -in `shared/tmp`. +then we should put them in `shared/puppies`. Temporary puppy files should be +stored in `shared/tmp`. In the GitLab application code you can get the full path to the `shared` directory with `Gitlab.config.shared.path`. -- cgit v1.2.1 From 2f56d89c9e22637cdb9e310c41f724f9ed450ea5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 26 Oct 2015 16:19:07 +0100 Subject: Update CHANGELOG for 8.1.1 [ci skip] --- CHANGELOG | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 24aaa449e65..256cb58fd34 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) - - Fix cloning Wiki repositories via HTTP (Stan Hu) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL @@ -13,11 +12,20 @@ v 8.2.0 (unreleased) - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page +v 8.1.1 + - Fix cloning Wiki repositories via HTTP (Stan Hu) + - Add migration to remove satellites directory + - Fix specific runners visibility + - Fix 500 when editing CI service + - Require CI jobs to be named + - Fix CSS for runner status + - Fix CI badge + - Allow developer to manage builds + v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - - Fix CSS for runner status - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. @@ -25,16 +33,11 @@ v 8.1.0 - Don't show "Add README" link in an empty repository if user doesn't have access to push (Stan Hu) - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) - Speed up load times of issue detail pages by roughly 1.5x - - Fix CI badge - - Require CI jobs to be named - Fix CI rendering regressions - - Fix specific runners visibility - - Allow developer to manage builds - If a merge request is to close an issue, show this on the issue page (Zeger-Jan van de Weg) - Add a system note and update relevant merge requests when a branch is deleted or re-added (Stan Hu) - Make diff file view easier to use on mobile screens (Stan Hu) - Improved performance of finding users by username or Email address - - Fix 500 when editing CI service - Fix bug where merge request comments created by API would not trigger notifications (Stan Hu) - Add support for creating directories from Files page (Stan Hu) - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu) -- cgit v1.2.1 From 01c7769c7529ca6d4036220052d78fc7284262f7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 26 Oct 2015 19:31:15 +0100 Subject: Add projects path index Fixes performance regression introduced by MR1649 --- db/migrate/20151026182941_add_project_path_index.rb | 9 +++++++++ db/schema.rb | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151026182941_add_project_path_index.rb diff --git a/db/migrate/20151026182941_add_project_path_index.rb b/db/migrate/20151026182941_add_project_path_index.rb new file mode 100644 index 00000000000..a62fe199d70 --- /dev/null +++ b/db/migrate/20151026182941_add_project_path_index.rb @@ -0,0 +1,9 @@ +class AddProjectPathIndex < ActiveRecord::Migration + def up + add_index :projects, :path + end + + def down + remove_index :projects, :path + end +end diff --git a/db/schema.rb b/db/schema.rb index 0fe113325fa..4bde9f0b748 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: 20151023144219) do +ActiveRecord::Schema.define(version: 20151026182941) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -624,6 +624,7 @@ ActiveRecord::Schema.define(version: 20151023144219) do add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree + add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree create_table "protected_branches", force: true do |t| -- cgit v1.2.1 From 2bbdeb91ca6b2c1e4fa10dbed34f9fff39e790f4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 27 Oct 2015 15:22:48 +0100 Subject: Reset memoized project repository when path changes --- app/models/project.rb | 1 + app/views/projects/edit.html.haml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..e0b44ee9bea 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -647,6 +647,7 @@ class Project < ActiveRecord::Base gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") send_move_instructions(old_path_with_namespace) reset_events_cache + @repository = nil rescue # Returning false does not rollback after_* transaction but gives # us information about failing some of tasks diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index afbf88b5507..f5bb00b0612 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -24,10 +24,10 @@ .col-sm-10 = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250 - - if @project.repository.exists? && @project.repository.branch_names.any? + - unless @project.empty_repo? .form-group = f.label :default_branch, "Default Branch", class: 'control-label' - .col-sm-10= f.select(:default_branch, @repository.branch_names, {}, {class: 'select2 select-wide'}) + .col-sm-10= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'}) = render 'shared/visibility_level', f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can_change_visibility_level?(@project, current_user), form_model: @project -- cgit v1.2.1 From 28af56dea5a88ffcaceb082cf67c9c1ab021609d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 27 Oct 2015 17:49:34 +0100 Subject: gfm is no longer --- app/views/shared/snippets/_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 0a4a790ec5e..dc98d310a13 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -21,4 +21,4 @@ = render "snippets/actions" .gray-content-block.middle-block %h2.snippet-title - = gfm escape_once(@snippet.title) + = markdown escape_once(@snippet.title), pipeline: :single_line -- cgit v1.2.1 From c9af886df9b83e7f3f9b131f19184546fbeac9de Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 28 Oct 2015 12:33:54 +0100 Subject: Remove deprecated CI events from project settings page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/ci/events_controller.rb | 21 --------------------- app/controllers/ci/runner_projects_controller.rb | 2 -- app/views/ci/events/index.html.haml | 20 -------------------- app/views/layouts/ci/project.html.haml | 11 ----------- app/views/layouts/nav/_project_settings.html.haml | 5 ----- config/routes.rb | 2 -- spec/features/ci/events_spec.rb | 22 ---------------------- 8 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 app/controllers/ci/events_controller.rb delete mode 100644 app/views/ci/events/index.html.haml delete mode 100644 app/views/layouts/ci/project.html.haml delete mode 100644 spec/features/ci/events_spec.rb diff --git a/CHANGELOG b/CHANGELOG index ea8c6fb5c17..b8c23cbef4f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.2.0 (unreleased) - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) + - Remove deprecated CI events from project settings page v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/controllers/ci/events_controller.rb b/app/controllers/ci/events_controller.rb deleted file mode 100644 index 89b784a1e89..00000000000 --- a/app/controllers/ci/events_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Ci - class EventsController < Ci::ApplicationController - EVENTS_PER_PAGE = 50 - - before_action :authenticate_user! - before_action :project - before_action :authorize_manage_project! - - layout 'ci/project' - - def index - @events = project.events.order("created_at DESC").page(params[:page]).per(EVENTS_PER_PAGE) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb index 97f01d40af5..9d555313369 100644 --- a/app/controllers/ci/runner_projects_controller.rb +++ b/app/controllers/ci/runner_projects_controller.rb @@ -4,8 +4,6 @@ module Ci before_action :project before_action :authorize_manage_project! - layout 'ci/project' - def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/views/ci/events/index.html.haml b/app/views/ci/events/index.html.haml deleted file mode 100644 index 9824e85b1af..00000000000 --- a/app/views/ci/events/index.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -%h3.page-title Events - -.table-holder - %table.table - %thead - %tr - %th User ID - %th Description - %th When - - @events.each do |event| - %tr - %td - = event.user_id - %td - = event.description - %td.light - = time_ago_in_words event.updated_at - ago - -= paginate @events diff --git a/app/views/layouts/ci/project.html.haml b/app/views/layouts/ci/project.html.haml deleted file mode 100644 index 15478c3f5bc..00000000000 --- a/app/views/layouts/ci/project.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title @project.name, ci_project_path(@project) - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page', sidebar: 'nav_project' diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 954dbe5d2b9..356ce09c3d7 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -65,8 +65,3 @@ = 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 diff --git a/config/routes.rb b/config/routes.rb index f6812c9280a..0458f538eb6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,8 +23,6 @@ Gitlab::Application.routes.draw do end resources :runner_projects, only: [:create, :destroy] - - resources :events, only: [:index] end resource :user_sessions do diff --git a/spec/features/ci/events_spec.rb b/spec/features/ci/events_spec.rb deleted file mode 100644 index 5b9fd404159..00000000000 --- a/spec/features/ci/events_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -describe "Events" do - let(:user) { create(:user) } - let(:project) { FactoryGirl.create :ci_project } - let(:event) { FactoryGirl.create :ci_admin_event, project: project } - - before do - login_as(user) - project.gl_project.team << [user, :master] - end - - describe "GET /ci/project/:id/events" do - before do - event - visit ci_project_events_path(project) - end - - it { expect(page).to have_content "Events" } - it { expect(page).to have_content event.description } - end -end -- cgit v1.2.1 From 910c2a50ef59b4233f6d3903f5c0e980b5e9ab7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Wed, 28 Oct 2015 12:41:56 +0100 Subject: Fixed the permission doc The guest users was missing "Pull project code" and "Download project". --- doc/permissions/permissions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index 7a6a1958445..8d4c2ceab7d 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -15,8 +15,8 @@ documentation](doc/workflow/add-user/add-user.md). |---------------------------------------|---------|------------|-------------|----------|--------| | Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ | -| Pull project code | | ✓ | ✓ | ✓ | ✓ | -| Download project | | ✓ | ✓ | ✓ | ✓ | +| Pull project code | ✓ | ✓ | ✓ | ✓ | ✓ | +| Download project | ✓ | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ | -- cgit v1.2.1 From aaa93d5add41661b657c3a684e23c4084f6292b5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 28 Oct 2015 13:09:51 +0100 Subject: Use issue editor as cross reference comment author when issue is edited with a new mention. --- CHANGELOG | 1 + app/services/issues/update_service.rb | 2 +- app/services/merge_requests/update_service.rb | 2 +- app/services/notes/update_service.rb | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ea8c6fb5c17..5fe1d957eb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.2.0 (unreleased) - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) + - Use issue editor as cross reference comment author when issue is edited with a new mention. v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 2b5426ad452..551325e4cab 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -35,7 +35,7 @@ module Issues create_title_change_note(issue, issue.previous_changes['title'].first) end - issue.create_new_cross_references! + issue.create_new_cross_references!(current_user) execute_hooks(issue, 'update') end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index ebbe0af803b..61f7d2bbe89 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -59,7 +59,7 @@ module MergeRequests merge_request.mark_as_unchecked end - merge_request.create_new_cross_references! + merge_request.create_new_cross_references!(current_user) execute_hooks(merge_request, 'update') end diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb index 6c2f08e5963..72e2f78008d 100644 --- a/app/services/notes/update_service.rb +++ b/app/services/notes/update_service.rb @@ -4,7 +4,7 @@ module Notes return note unless note.editable? note.update_attributes(params.merge(updated_by: current_user)) - note.create_new_cross_references! + note.create_new_cross_references!(current_user) note.reset_events_cache note -- cgit v1.2.1 From 89bce7fada0d02caf27b13bc638aaaf5956b9c80 Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Mon, 26 Oct 2015 11:34:36 +0100 Subject: Add copy paste text for closing down the Github issue tracker Signed-off-by: Jeroen van Baarsen --- PROCESS.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/PROCESS.md b/PROCESS.md index 9f4b708d2b5..d42168a7231 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -115,3 +115,10 @@ We can only accept a merge request if all the tests are green. I've just restarted the build. When the tests are still not passing after this restart and you're sure that is does not have anything to do with your code changes, please rebase with master to see if that solves the issue. + +### Closing down the issue tracker on GitHub + +We are currently in the process of closing down the issue tracker on GitHub, to +prevent duplication with the [GitLab.com issue tracker][https://gitlab.com/gitlab-org/gitlab-ce/issues]. +Since this is an older issue I'll be closing this for now. If you think this is +still an issue I encourage you to open it on the [GitLab.com issue tracker][https://gitlab.com/gitlab-org/gitlab-ce/issues]. -- cgit v1.2.1 From f56c7d9f8e66c69de6e984e497dd529874b8d638 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 23 Oct 2015 11:13:10 -0700 Subject: Force update refs/merge-requests/X/head upon a push to the source branch of a merge request Closes #3138 --- CHANGELOG | 1 + app/models/repository.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..fa13343ea2b 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.2.0 (unreleased) + - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL diff --git a/app/models/repository.rb b/app/models/repository.rb index a3ba5f4c18a..c9b36bd8170 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -528,7 +528,7 @@ class Repository end def fetch_ref(source_path, source_ref, target_ref) - args = %W(git fetch #{source_path} #{source_ref}:#{target_ref}) + args = %W(git fetch -f #{source_path} #{source_ref}:#{target_ref}) Gitlab::Popen.popen(args, path_to_repo) end -- cgit v1.2.1 From 5a5069969ce8e9184e36abbb7623bf474d5869e9 Mon Sep 17 00:00:00 2001 From: Jonathan Schoeffling Date: Sun, 14 Jun 2015 18:04:20 -0400 Subject: Add support for searching commit log messages Include the log messages of recent commits in project-level search results, providing functionality similar to 'git log --grep'. Update repository model rspec tests to validate the output of Repository#commits_with_log_matching. --- CHANGELOG | 1 + app/controllers/search_controller.rb | 4 ++-- app/models/repository.rb | 6 ++++++ app/views/layouts/_search.html.haml | 2 ++ app/views/search/_category.html.haml | 7 +++++++ app/views/search/results/_commits.html.haml | 32 +++++++++++++++++++++++++++++ lib/gitlab/project_search_results.rb | 12 ++++++++++- spec/models/repository_spec.rb | 11 ++++++++++ 8 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 app/views/search/results/_commits.html.haml diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..3975f236a58 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -208,6 +208,7 @@ v 8.0.0 - Fix references to target project issues in Merge Requests markdown preview and textareas (Francesco Levorato) - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato) - Removed API calls from CE to CI + - Include commit logs in project search v 7.14.3 - No changes diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index eb0408a95e5..9bb42ec86b3 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -23,8 +23,8 @@ class SearchController < ApplicationController @search_results = if @project - unless %w(blobs notes issues merge_requests milestones wiki_blobs). - include?(@scope) + unless %w(blobs notes issues merge_requests milestones wiki_blobs + commits).include?(@scope) @scope = 'blobs' end diff --git a/app/models/repository.rb b/app/models/repository.rb index a3ba5f4c18a..39451f7da7f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,6 +87,12 @@ class Repository commits end + def commits_with_log_matching(query) + list = Gitlab::Git::Commit.where(repo: raw_repository, limit: 1000) + list = Commit.decorate(list, @project) if list.present? + list.select! { |c| c.message.match /#{query}/i } + end + def find_branch(name) branches.find { |branch| branch.name == name } end diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index ceb64ce3157..d1aa8f62463 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -11,6 +11,8 @@ = hidden_field_tag :scope, 'merge_requests' - elsif current_controller?(:wikis) = hidden_field_tag :scope, 'wiki_blobs' + - elsif current_controller?(:commits) + = hidden_field_tag :scope, 'commits' - else = hidden_field_tag :search_code, true diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index d637abfa76b..ef43f727d8b 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -42,6 +42,13 @@ Wiki %span.badge = @search_results.wiki_blobs_count + %li{class: ("active" if @scope == 'commits')} + = link_to search_filter_path(scope: 'commits') do + = icon('history fw') + %span + Commit Logs + %span.badge + = @search_results.commits_count - elsif @show_snippets %li{class: ("active" if @scope == 'snippet_blobs')} diff --git a/app/views/search/results/_commits.html.haml b/app/views/search/results/_commits.html.haml new file mode 100644 index 00000000000..8076174e59d --- /dev/null +++ b/app/views/search/results/_commits.html.haml @@ -0,0 +1,32 @@ +.search-result-row + .commits-row-title + %strong.str-truncated + = link_to commits.title, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id" + + .pull-right + = link_to commits.short_id, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id" + + .notes_count + - if @note_counts + - note_count = @note_counts.fetch(commits.id, 0) + - else + - notes = commits.notes + - note_count = notes.user.count + + - if note_count > 0 + %span.light + %i.fa.fa-comments + = note_count + + - if commits.description? + .commits-row-description + %pre + = preserve(gfm(escape_once(commits.description))) + + .commits-row-info + = commit_author_link(commits, avatar: true, size: 24) + authored + .committed_ago + #{time_ago_with_tooltip(commits.committed_date)}   + = link_to_browse_code(@project, commits) + %br diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 0a2be605af9..3bf98699bcb 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -20,6 +20,8 @@ module Gitlab Kaminari.paginate_array(blobs).page(page).per(per_page) when 'wiki_blobs' Kaminari.paginate_array(wiki_blobs).page(page).per(per_page) + when 'commits' + Kaminari.paginate_array(commits).page(page).per(per_page) else super end @@ -27,7 +29,7 @@ module Gitlab def total_count @total_count ||= issues_count + merge_requests_count + blobs_count + - notes_count + wiki_blobs_count + notes_count + wiki_blobs_count + commits_count end def blobs_count @@ -42,6 +44,10 @@ module Gitlab @wiki_blobs_count ||= wiki_blobs.count end + def commits_count + @commits_count ||= commits.count + end + private def blobs @@ -70,6 +76,10 @@ module Gitlab Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC') end + def commits + project.repository.commits_with_log_matching(query) + end + def limit_project_ids [project.id] end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 05e51532eb8..ceffd5a8617 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,6 +26,17 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end + describe :commits_with_log_matching do + subject { repository.commits_with_log_matching('submodule'). + map{|k| k.id} + } + + it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } + it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } + it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') } + it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + end + describe :blob_at do context 'blank sha' do subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') } -- cgit v1.2.1 From 091aa95dbfec0b062ed706fb55589ce10e5bd9d0 Mon Sep 17 00:00:00 2001 From: Mike Chmielewski Date: Wed, 21 Oct 2015 00:59:52 +0000 Subject: Make subject of spec line up on one line to satisfy rubocop requirements --- spec/models/repository_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index ceffd5a8617..9d5859f1476 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,9 +27,7 @@ describe Repository do end describe :commits_with_log_matching do - subject { repository.commits_with_log_matching('submodule'). - map{|k| k.id} - } + subject { repository.commits_with_log_matching('submodule').map{|k| k.id} } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } @@ -102,4 +100,4 @@ describe Repository do it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end end -end +end \ No newline at end of file -- cgit v1.2.1 From afdb53baecb2ef6d57f8ed957213260825c67805 Mon Sep 17 00:00:00 2001 From: Mike Chmielewski Date: Wed, 21 Oct 2015 01:04:19 +0000 Subject: Added @commits to list of tags. --- lib/tasks/spinach.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index c8881be0954..d5a96fd38f4 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -5,7 +5,7 @@ namespace :spinach do task :project do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets), + %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits), ] run_commands(cmds) end @@ -14,7 +14,7 @@ namespace :spinach do task :other do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets), + %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits), ] run_commands(cmds) end @@ -33,4 +33,4 @@ def run_commands(cmds) cmds.each do |cmd| system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") end -end +end \ No newline at end of file -- cgit v1.2.1 From 8e3a316121ba0814997cc66a8ef00fb97ea0f200 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 20 Oct 2015 21:44:39 -0400 Subject: Moved changelog entry to 8.2 --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3975f236a58..b573d40317e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,9 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) + - Include commit logs in project search + +v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. @@ -208,7 +211,6 @@ v 8.0.0 - Fix references to target project issues in Merge Requests markdown preview and textareas (Francesco Levorato) - Redirect from incorrectly cased group or project path to correct one (Francesco Levorato) - Removed API calls from CE to CI - - Include commit logs in project search v 7.14.3 - No changes -- cgit v1.2.1 From 8e8fb87d4091f05a75929d6daa3bbe6862e2dda1 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Sat, 24 Oct 2015 22:41:51 -0400 Subject: Trailing new lines at ends of files are important. --- spec/models/repository_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 9d5859f1476..a6973ea8fcb 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -100,4 +100,4 @@ describe Repository do it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end end -end \ No newline at end of file +end -- cgit v1.2.1 From b1f4aaa5e753e6e7cdefd84226839123df59b382 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 27 Oct 2015 21:16:56 -0400 Subject: Trying to incorporate suggestions from comments on Merge Request 1661 --- app/models/repository.rb | 9 +++++---- lib/gitlab/project_search_results.rb | 2 +- spec/models/repository_spec.rb | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 39451f7da7f..ed7ed9fd261 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,10 +87,11 @@ class Repository commits end - def commits_with_log_matching(query) - list = Gitlab::Git::Commit.where(repo: raw_repository, limit: 1000) - list = Commit.decorate(list, @project) if list.present? - list.select! { |c| c.message.match /#{query}/i } + def find_commits_with_matching_log(query) + # Limited to 1000 commits for now, could be parameterized? + args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) + + Gitlab::Popen.popen(args, path_to_repo) end def find_branch(name) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 3bf98699bcb..54389f7d662 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,7 @@ module Gitlab end def commits - project.repository.commits_with_log_matching(query) + project.repository.find_commits_with_matching_log(query) end def limit_project_ids diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index a6973ea8fcb..26c92af31ed 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,8 +26,8 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end - describe :commits_with_log_matching do - subject { repository.commits_with_log_matching('submodule').map{|k| k.id} } + describe :find_commits_with_matching_log do + subject { repository.find_commits_with_matching_log('submodule').map{|k| k.id} } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 7b62791afc8e98f8ccd7d85dbae0cf2128883c13 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Tue, 27 Oct 2015 23:24:43 -0400 Subject: Fixed method to use git log via Popen as recommended, and made output match test (and thus system) expectations. --- app/models/repository.rb | 7 ++++++- spec/models/repository_spec.rb | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index ed7ed9fd261..112ad05c188 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -91,7 +91,12 @@ class Repository # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - Gitlab::Popen.popen(args, path_to_repo) + git_log_results = Gitlab::Popen.popen(args, path_to_repo) + + # 1. Get result, which is 1-element array + # 2. Split on lines + # 3. Recreate array, but remove trailing newline characters on each element + git_log_results.first.lines.map{ |l| l.chomp } end def find_branch(name) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 26c92af31ed..17b5f28891b 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,7 +27,7 @@ describe Repository do end describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule').map{|k| k.id} } + subject { repository.find_commits_with_matching_log('submodule') } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From a0d0a0179134c16c84dfa18da9db78b375cd7cd0 Mon Sep 17 00:00:00 2001 From: Michael Chmielewski Date: Wed, 28 Oct 2015 22:12:22 -0400 Subject: Actually converted code to following suggestions. --- app/models/repository.rb | 9 +++------ spec/models/repository_spec.rb | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 112ad05c188..a0f2b3fb765 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -91,12 +91,9 @@ class Repository # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - git_log_results = Gitlab::Popen.popen(args, path_to_repo) - - # 1. Get result, which is 1-element array - # 2. Split on lines - # 3. Recreate array, but remove trailing newline characters on each element - git_log_results.first.lines.map{ |l| l.chomp } + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map{ |l| l.chomp } + commits = git_log_results.map{ |c| commit(c) } + commits end def find_branch(name) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 17b5f28891b..aedbfa04d88 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -27,7 +27,7 @@ describe Repository do end describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule') } + subject { repository.find_commits_with_matching_log('submodule').map{ |k| k.id } } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 7794cc8b9d1e1582c5046f94d5d5cea843c7e95a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Oct 2015 10:25:38 +0100 Subject: Put delete snippet btn after edit btn Signed-off-by: Dmitriy Zaporozhets --- app/views/snippets/_actions.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml index 751fafa8942..1979ae6d5bc 100644 --- a/app/views/snippets/_actions.html.haml +++ b/app/views/snippets/_actions.html.haml @@ -1,11 +1,11 @@ = link_to new_snippet_path, class: 'btn btn-grouped new-snippet-link', title: "New Snippet" do = icon('plus') New Snippet -- if can?(current_user, :admin_personal_snippet, @snippet) - = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do - = icon('trash-o') - Delete - if can?(current_user, :update_personal_snippet, @snippet) = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do = icon('pencil-square-o') Edit +- if can?(current_user, :admin_personal_snippet, @snippet) + = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-remove", title: 'Delete Snippet' do + = icon('trash-o') + Delete -- cgit v1.2.1 From e0e311a19c025435d119d379a98ec28f0704628f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Oct 2015 10:28:41 +0100 Subject: Fix bg for labels page when no labels present Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/labels/index.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 97175f8232b..fb784ee5f4f 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -14,8 +14,8 @@ = render @labels = paginate @labels, theme: 'gitlab' - else - .light-well + .nothing-here-block - if can? current_user, :admin_label, @project - .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels + Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels - else - .nothing-here-block No labels created + No labels created -- cgit v1.2.1 From 3bb626f91cb50bd2eff58681e22db942b7d6a087 Mon Sep 17 00:00:00 2001 From: James Newton Date: Wed, 28 Oct 2015 16:39:23 +0100 Subject: refactor login as to be impersonation with better login/logout Modifies the existing "login as" feature to be called impersonation, as well as keeping track of who is impersonating to revert back to that user without having to log out. --- app/assets/stylesheets/framework/header.scss | 4 ++ app/controllers/admin/application_controller.rb | 6 +++ app/controllers/admin/impersonation_controller.rb | 32 +++++++++++++++ app/controllers/admin/users_controller.rb | 6 --- app/views/admin/users/_head.html.haml | 2 +- app/views/layouts/header/_default.html.haml | 4 ++ config/routes.rb | 4 +- spec/controllers/admin/users_controller_spec.rb | 15 ------- spec/features/admin/admin_users_spec.rb | 50 +++++++++++++++++------ 9 files changed, 88 insertions(+), 35 deletions(-) create mode 100644 app/controllers/admin/impersonation_controller.rb diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 91e6975e269..02ea91602e8 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -118,6 +118,10 @@ header { } } } + + .impersonation i { + color: $red-normal; + } } @mixin collapsed-header { diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb index 56e24386463..9083bfb41cf 100644 --- a/app/controllers/admin/application_controller.rb +++ b/app/controllers/admin/application_controller.rb @@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController def authenticate_admin! return render_404 unless current_user.is_admin? end + + def authorize_impersonator! + if session[:impersonator_id] + User.find_by!(username: session[:impersonator_id]).admin? + end + end end diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb new file mode 100644 index 00000000000..0382402afa6 --- /dev/null +++ b/app/controllers/admin/impersonation_controller.rb @@ -0,0 +1,32 @@ +class Admin::ImpersonationController < Admin::ApplicationController + skip_before_action :authenticate_admin!, only: :destroy + + before_action :user + before_action :authorize_impersonator! + + def create + session[:impersonator_id] = current_user.username + session[:impersonator_return_to] = request.env['HTTP_REFERER'] + + warden.set_user(user, scope: 'user') + + flash[:alert] = "You are impersonating #{user.username}." + + redirect_to root_path + end + + def destroy + redirect = session[:impersonator_return_to] + + warden.set_user(user, scope: 'user') + + session[:impersonator_return_to] = nil + session[:impersonator_id] = nil + + redirect_to redirect || root_path + end + + def user + @user ||= User.find_by!(username: params[:id] || session[:impersonator_id]) + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index c63d0793e31..d7c927d444c 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController end end - def login_as - sign_in(user) - flash[:alert] = "Logged in as #{user.username}" - redirect_to root_path - end - def disable_two_factor user.disable_two_factor! redirect_to admin_user_path(user), diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 4245d0f1eda..8d1cab4137c 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -7,7 +7,7 @@ .pull-right - unless @user == current_user - = link_to 'Log in as this user', login_as_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" + = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index c31b1cbe9a8..13eeb0fe20b 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -13,6 +13,10 @@ %li.visible-sm.visible-xs = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('search') + - if session[:impersonator_id] + %li.impersonation + = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do + = icon('user-secret fw') - if current_user.is_admin? %li = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do diff --git a/config/routes.rb b/config/routes.rb index f6812c9280a..6ebedaefafe 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -212,6 +212,8 @@ Gitlab::Application.routes.draw do resources :keys, only: [:show, :destroy] resources :identities, only: [:index, :edit, :update, :destroy] + delete 'stop_impersonation' => 'impersonation#destroy', on: :collection + member do get :projects get :keys @@ -221,7 +223,7 @@ Gitlab::Application.routes.draw do put :unblock put :unlock put :confirm - post :login_as + post 'impersonate' => 'impersonation#create' patch :disable_two_factor delete 'remove/:email_id', action: 'remove_email', as: 'remove_email' end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index fcbe62cace8..8b7af4d3a0a 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -7,21 +7,6 @@ describe Admin::UsersController do sign_in(admin) end - describe 'POST login_as' do - let(:user) { create(:user) } - - it 'logs admin as another user' do - expect(warden.authenticate(scope: :user)).not_to eq(user) - post :login_as, id: user.username - expect(warden.authenticate(scope: :user)).to eq(user) - end - - it 'redirects user to homepage' do - post :login_as, id: user.username - expect(response).to redirect_to(root_path) - end - end - describe 'DELETE #user with projects' do let(:user) { create(:user) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index c2c7364f6c5..4c756a8e732 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -111,24 +111,50 @@ describe "Admin::Users", feature: true do expect(page).to have_content(@user.name) end - describe 'Login as another user' do - it 'should show login button for other users and check that it works' do - another_user = create(:user) + describe 'Impersonation' do + let(:another_user) { create(:user) } + before { visit admin_user_path(another_user) } - visit admin_user_path(another_user) - - click_link 'Log in as this user' + context 'before impersonating' do + it 'shows impersonate button for other users' do + expect(page).to have_content('Impersonate') + end - expect(page).to have_content("Logged in as #{another_user.username}") + it 'should not show impersonate button for admin itself' do + visit admin_user_path(@user) - page.within '.sidebar-user .username' do - expect(page).to have_content(another_user.username) + expect(page).not_to have_content('Impersonate') end end - it 'should not show login button for admin itself' do - visit admin_user_path(@user) - expect(page).not_to have_content('Log in as this user') + context 'when impersonating' do + before { click_link 'Impersonate' } + + it 'logs in as the user when impersonate is clicked' do + page.within '.sidebar-user .username' do + expect(page).to have_content(another_user.username) + end + end + + it 'sees impersonation log out icon' do + icon = first('.fa.fa-user-secret') + + expect(icon).to_not eql nil + end + + it 'can log out of impersonated user back to original user' do + find(:css, 'li.impersonation a').click + + page.within '.sidebar-user .username' do + expect(page).to have_content(@user.username) + end + end + + it 'is redirected back to the impersonated users page in the admin after stopping' do + find(:css, 'li.impersonation a').click + + expect(current_path).to eql "/admin/users/#{another_user.username}" + end end end -- cgit v1.2.1 From ac006c193c0d5fbc1396b8f748688466fee4a184 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Thu, 29 Oct 2015 10:26:48 +0000 Subject: IE message doesn't need a heading --- doc/install/requirements.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index aa0d03b75bc..5041ead6017 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -109,8 +109,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) - Safari 7+ (known problem: required fields in html5 do not work) - Opera (Latest released version) -- IE 10+ - -### Common UI problems with IE - -If you experience UI issues with Internet Explorer, please make sure that you have the `Compatibility View` mode disabled. \ No newline at end of file +- IE 10+ but please make sure that you have the `Compatibility View` mode disabled. \ No newline at end of file -- cgit v1.2.1 From ae99720a40b8e0700891f5828c1a93bcc7673e04 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Thu, 29 Oct 2015 10:27:21 +0000 Subject: Write out internet explorer --- doc/install/requirements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 5041ead6017..c0ccdd37458 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -109,4 +109,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/)) - Safari 7+ (known problem: required fields in html5 do not work) - Opera (Latest released version) -- IE 10+ but please make sure that you have the `Compatibility View` mode disabled. \ No newline at end of file +- Internet Explorer (IE) 10+ but please make sure that you have the `Compatibility View` mode disabled. \ No newline at end of file -- cgit v1.2.1 From 7fc95d805dd2a4b997bbc3ad24a1a3a7d64ef305 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 20 Oct 2015 16:56:29 +0200 Subject: Added index on services.template This column is queried when creating a new project, without an index this query would lead to a sequence scan. --- db/migrate/20151020145526_add_services_template_index.rb | 5 +++++ db/schema.rb | 1 + 2 files changed, 6 insertions(+) create mode 100644 db/migrate/20151020145526_add_services_template_index.rb diff --git a/db/migrate/20151020145526_add_services_template_index.rb b/db/migrate/20151020145526_add_services_template_index.rb new file mode 100644 index 00000000000..1b04f313565 --- /dev/null +++ b/db/migrate/20151020145526_add_services_template_index.rb @@ -0,0 +1,5 @@ +class AddServicesTemplateIndex < ActiveRecord::Migration + def change + add_index :services, :template + end +end diff --git a/db/schema.rb b/db/schema.rb index 4bde9f0b748..73fc83c3d6b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -667,6 +667,7 @@ ActiveRecord::Schema.define(version: 20151026182941) do add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree + add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: true do |t| t.string "title" -- cgit v1.2.1 From 6369c992a6d9279f4aa38c60c350c966f3926df1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 20 Oct 2015 17:44:15 +0200 Subject: Added benchmark for Projects::CreateService This benchmark currently runs at ~0.6 iterations per second and is unlikely to perform any better any time soon. --- .../services/projects/create_service_spec.rb | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 spec/benchmarks/services/projects/create_service_spec.rb diff --git a/spec/benchmarks/services/projects/create_service_spec.rb b/spec/benchmarks/services/projects/create_service_spec.rb new file mode 100644 index 00000000000..25ed48c34fd --- /dev/null +++ b/spec/benchmarks/services/projects/create_service_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Projects::CreateService, benchmark: true do + describe '#execute' do + let(:user) { create(:user, :admin) } + + let(:group) do + group = create(:group) + + create(:group_member, group: group, user: user) + + group + end + + benchmark_subject do + name = SecureRandom.hex + service = described_class.new(user, + name: name, + path: name, + namespace_id: group.id, + visibility_level: Gitlab::VisibilityLevel::PUBLIC) + + service.execute + end + + it { is_expected.to iterate_per_second(0.5) } + end +end -- cgit v1.2.1 From 0574166a6117fa5eccc21f632b7f5ab528796fd6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 29 Oct 2015 13:00:20 +0000 Subject: Fix grouping of contributors by email in graph. --- app/assets/javascripts/stat_graph_contributors_util.js.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/stat_graph_contributors_util.js.coffee index cfe5508290f..f5584bcfe4b 100644 --- a/app/assets/javascripts/stat_graph_contributors_util.js.coffee +++ b/app/assets/javascripts/stat_graph_contributors_util.js.coffee @@ -6,7 +6,7 @@ window.ContributorsStatGraphUtil = for entry in log @add_date(entry.date, total) unless total[entry.date]? - data = by_author[entry.author_name] #|| by_email[entry.author_email] + data = by_author[entry.author_name] || by_email[entry.author_email] data ?= @add_author(entry, by_author, by_email) @add_date(entry.date, data) unless data[entry.date] @@ -95,5 +95,4 @@ window.ContributorsStatGraphUtil = if date_range is null || date_range[0] <= new Date(date) <= date_range[1] true else - false - + false \ No newline at end of file -- cgit v1.2.1 From bb8b6f38e7fef0afd41088d54d9dda41695ec1c7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 29 Oct 2015 13:02:11 +0000 Subject: Update changelog --- CHANGELOG | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..ff618ea582f 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.2.0 (unreleased) + - Fix grouping of contributors by email in graph. - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL @@ -1902,4 +1903,4 @@ v 0.8.0 - stability - security fixes - increased test coverage - - email notification + - email notification \ No newline at end of file -- cgit v1.2.1 From 29b3ce56ac45afc2c4dcd7055b53d8c06ef9faf2 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 21 Oct 2015 16:19:05 +0200 Subject: Removed extra activity update for new projects When a project is created the last activity timestamp is already set so there's no need for another update. --- app/services/projects/create_service.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index faf1ee008e7..5b84527eccf 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -94,8 +94,6 @@ module Projects @project.team << [current_user, :master, current_user] end - @project.update_column(:last_activity_at, @project.created_at) - if @project.import? @project.import_start end -- cgit v1.2.1 From 41734b630ed86c5aa0814cb331f4f8e94e0bf2a2 Mon Sep 17 00:00:00 2001 From: Gert Goet Date: Thu, 29 Oct 2015 14:13:30 +0100 Subject: Ensure variable is evalled --- app/views/notify/project_was_moved_email.text.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb index d8a23dabf49..b2c5f71e465 100644 --- a/app/views/notify/project_was_moved_email.text.erb +++ b/app/views/notify/project_was_moved_email.text.erb @@ -1,4 +1,4 @@ -Project #{@old_path_with_namespace} was moved to another location +Project <%= @old_path_with_namespace %> was moved to another location The project is now located under <%= namespace_project_url(@project.namespace, @project) %> -- cgit v1.2.1 From c5132e94e1c44884f8f9b399c4def20154b6c0ce Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 29 Oct 2015 14:21:24 +0100 Subject: Switch to gitlab-workhorse --- GITLAB_GIT_HTTP_SERVER_VERSION | 1 - GITLAB_WORKHORSE_VERSION | 1 + doc/update/8.1-to-8.2.md | 187 ++++++++++++++++++++++++++++++ lib/gitlab/backend/grack_auth.rb | 2 +- lib/support/init.d/gitlab | 68 ++++++----- lib/support/init.d/gitlab.default.example | 11 +- lib/support/nginx/gitlab | 22 ++-- lib/support/nginx/gitlab-ssl | 22 ++-- 8 files changed, 249 insertions(+), 65 deletions(-) delete mode 100644 GITLAB_GIT_HTTP_SERVER_VERSION create mode 100644 GITLAB_WORKHORSE_VERSION create mode 100644 doc/update/8.1-to-8.2.md diff --git a/GITLAB_GIT_HTTP_SERVER_VERSION b/GITLAB_GIT_HTTP_SERVER_VERSION deleted file mode 100644 index 0d91a54c7d4..00000000000 --- a/GITLAB_GIT_HTTP_SERVER_VERSION +++ /dev/null @@ -1 +0,0 @@ -0.3.0 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION new file mode 100644 index 00000000000..9e11b32fcaa --- /dev/null +++ b/GITLAB_WORKHORSE_VERSION @@ -0,0 +1 @@ +0.3.1 diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md new file mode 100644 index 00000000000..38e5661b368 --- /dev/null +++ b/doc/update/8.1-to-8.2.md @@ -0,0 +1,187 @@ +# From 8.1 to 8.2 + +**NOTE:** GitLab 8.0 introduced several significant changes related to +installation and configuration which *are not duplicated here*. Be sure you're +already running a working version of 8.0 before proceeding with this guide. + +### 0. Double-check your Git version + +**This notice applies only to /usr/local/bin/git** + +If you compiled Git from source on your GitLab server then please double-check +that you are using a version that protects against CVE-2014-9390. For six +months after this vulnerability became known the GitLab installation guide +still contained instructions that would install the outdated, 'vulnerable' Git +version 2.1.2. + +Run the following command to get your current Git version: + +```sh +/usr/local/bin/git --version +``` + +If you see 'No such file or directory' then you did not install Git according +to the outdated instructions from the GitLab installation guide and you can go +to the next step 'Stop server' below. + +If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4, +v2.2.1 or newer. You can use the [instructions in the GitLab source +installation +guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) +to install a newer version of Git. + +### 1. Stop server + + sudo service gitlab stop + +### 2. Backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Get latest code + +```bash +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +``` + +For GitLab Community Edition: + +```bash +sudo -u git -H git checkout 8-2-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +sudo -u git -H git checkout 8-2-stable-ee +``` + +### 4. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch +sudo -u git -H git checkout v2.6.5 +``` + +### 5. Replace gitlab-git-http-server with gitlab-workhorse + +Install and compile gitlab-workhorse. This requires [Go +1.5](https://golang.org/dl) which should already be on your system +from GitLab 8.1. + +```bash +cd /home/git +sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git +sudo -u git -H git checkout 0.3.1 +sudo -u git -H make +``` + +Update the GitLab init script and 'default' file. + +``` +cd /home/git/gitlab +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +test -e /etc/default/gitlab && \ + sudo sed -i .pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab +``` + +Make sure that you also update your **NGINX configuration** to use +the new gitlab-workhorse.socket file. + +### 6. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment + +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +# Update init.d script +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +### 7. Update configuration files + +#### New configuration options for `gitlab.yml` + +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +git diff origin/8-1-stable:config/gitlab.yml.example origin/8-2-stable:config/gitlab.yml.example +``` + +#### Nginx configuration + +View changes between the previous recommended Nginx configuration and the +current one: + +```sh +# For HTTPS configurations +git diff origin/8-1-stable:lib/support/nginx/gitlab-ssl origin/8-2-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/8-1-stable:lib/support/nginx/gitlab origin/8-2-stable:lib/support/nginx/gitlab +``` + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-git-http-server listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-2-stable/lib/support/init.d/gitlab.default.example#L34 + +### 8. Start application + + sudo service gitlab start + sudo service nginx restart + +### 9. Check application status + +Check if GitLab and its environment are configured correctly: + + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations, the upgrade is complete! + +## Things went south? Revert to previous version (8.0) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 7.14 to 8.0](7.14-to-8.0.md), except for the database migration +(The backup is already migrated to the previous version) + +### 2. Restore from the backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +## Troubleshooting + +### "You appear to have cloned an empty repository." + +See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting). diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 85a2d1a93a7..440ef5a3cb3 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -34,7 +34,7 @@ module Grack auth! if project && authorized_request? - # Tell gitlab-git-http-server the request is OK, and what the GL_ID is + # Tell gitlab-workhorse the request is OK, and what the GL_ID is render_grack_auth_ok elsif @user.nil? && !@ci unauthorized diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index a80e7e77430..f0a6c2b30e9 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -37,10 +37,9 @@ web_server_pid_path="$pid_path/unicorn.pid" sidekiq_pid_path="$pid_path/sidekiq.pid" mail_room_enabled=false mail_room_pid_path="$pid_path/mail_room.pid" -gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid" -gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080" -gitlab_git_http_server_repo_root='/home/git/repositories' -gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log" +gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid" +gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080" +gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" shell_path="/bin/bash" # Read configuration variable file if it is present @@ -76,8 +75,8 @@ check_pids(){ else spid=0 fi - if [ -f "$gitlab_git_http_server_pid_path" ]; then - hpid=$(cat "$gitlab_git_http_server_pid_path") + if [ -f "$gitlab_workhorse_pid_path" ]; then + hpid=$(cat "$gitlab_workhorse_pid_path") else hpid=0 fi @@ -94,7 +93,7 @@ check_pids(){ wait_for_pids(){ # We are sleeping a bit here mostly because sidekiq is slow at writing it's pid i=0; - while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_git_http_server_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do + while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; }; do sleep 0.1; i=$((i+1)) if [ $((i%10)) = 0 ]; then @@ -131,9 +130,9 @@ check_status(){ fi if [ $hpid -ne 0 ]; then kill -0 "$hpid" 2>/dev/null - gitlab_git_http_server_status="$?" + gitlab_workhorse_status="$?" else - gitlab_git_http_server_status="-1" + gitlab_workhorse_status="-1" fi if [ "$mail_room_enabled" = true ]; then if [ $mpid -ne 0 ]; then @@ -143,7 +142,7 @@ check_status(){ mail_room_status="-1" fi fi - if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_git_http_server_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then + if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; }; then gitlab_status=0 else # http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html @@ -171,9 +170,9 @@ check_stale_pids(){ exit 1 fi fi - if [ "$hpid" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ]; then - echo "Removing stale gitlab-git-http-server pid. This is most likely caused by gitlab-git-http-server crashing the last time it ran." - if ! rm "$gitlab_git_http_server_pid_path"; then + if [ "$hpid" != "0" ] && [ "$gitlab_workhorse_status" != "0" ]; then + echo "Removing stale gitlab-workhorse pid. This is most likely caused by gitlab-workhorse crashing the last time it ran." + if ! rm "$gitlab_workhorse_pid_path"; then echo "Unable to remove stale pid, exiting" exit 1 fi @@ -190,7 +189,7 @@ check_stale_pids(){ ## If no parts of the service is running, bail out. exit_if_not_running(){ check_stale_pids - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then echo "GitLab is not running." exit fi @@ -206,8 +205,8 @@ start_gitlab() { if [ "$sidekiq_status" != "0" ]; then echo "Starting GitLab Sidekiq" fi - if [ "$gitlab_git_http_server_status" != "0" ]; then - echo "Starting gitlab-git-http-server" + if [ "$gitlab_workhorse_status" != "0" ]; then + echo "Starting gitlab-workhorse" fi if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" != "0" ]; then echo "Starting GitLab MailRoom" @@ -230,15 +229,14 @@ start_gitlab() { RAILS_ENV=$RAILS_ENV bin/background_jobs start & fi - if [ "$gitlab_git_http_server_status" = "0" ]; then - echo "The gitlab-git-http-server is already running with pid $spid, not restarting" + if [ "$gitlab_workhorse_status" = "0" ]; then + echo "The gitlab-workhorse is already running with pid $spid, not restarting" else - # No need to remove a socket, gitlab-git-http-server does this itself - $app_root/bin/daemon_with_pidfile $gitlab_git_http_server_pid_path \ - $app_root/../gitlab-git-http-server/gitlab-git-http-server \ - $gitlab_git_http_server_options \ - $gitlab_git_http_server_repo_root \ - >> $gitlab_git_http_server_log 2>&1 & + # No need to remove a socket, gitlab-workhorse does this itself + $app_root/bin/daemon_with_pidfile $gitlab_workhorse_pid_path \ + $app_root/../gitlab-workhorse/gitlab-workhorse \ + $gitlab_workhorse_options \ + >> $gitlab_workhorse_log 2>&1 & fi if [ "$mail_room_enabled" = true ]; then @@ -268,9 +266,9 @@ stop_gitlab() { echo "Shutting down GitLab Sidekiq" RAILS_ENV=$RAILS_ENV bin/background_jobs stop fi - if [ "$gitlab_git_http_server_status" = "0" ]; then - echo "Shutting down gitlab-git-http-server" - kill -- $(cat $gitlab_git_http_server_pid_path) + if [ "$gitlab_workhorse_status" = "0" ]; then + echo "Shutting down gitlab-workhorse" + kill -- $(cat $gitlab_workhorse_pid_path) fi if [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; then echo "Shutting down GitLab MailRoom" @@ -278,11 +276,11 @@ stop_gitlab() { fi # If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script. - while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do + while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; do sleep 1 check_status printf "." - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then printf "\n" break fi @@ -292,7 +290,7 @@ stop_gitlab() { # Cleaning up unused pids rm "$web_server_pid_path" 2>/dev/null # rm "$sidekiq_pid_path" 2>/dev/null # Sidekiq seems to be cleaning up it's own pid. - rm -f "$gitlab_git_http_server_pid_path" + rm -f "$gitlab_workhorse_pid_path" if [ "$mail_room_enabled" = true ]; then rm "$mail_room_pid_path" 2>/dev/null fi @@ -303,7 +301,7 @@ stop_gitlab() { ## Prints the status of GitLab and it's components. print_status() { check_status - if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_git_http_server_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then + if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; }; then echo "GitLab is not running." return fi @@ -317,10 +315,10 @@ print_status() { else printf "The GitLab Sidekiq job dispatcher is \033[31mnot running\033[0m.\n" fi - if [ "$gitlab_git_http_server_status" = "0" ]; then - echo "The gitlab-git-http-server with pid $hpid is running." + if [ "$gitlab_workhorse_status" = "0" ]; then + echo "The gitlab-workhorse with pid $hpid is running." else - printf "The gitlab-git-http-server is \033[31mnot running\033[0m.\n" + printf "The gitlab-workhorse is \033[31mnot running\033[0m.\n" fi if [ "$mail_room_enabled" = true ]; then if [ "$mail_room_status" = "0" ]; then @@ -360,7 +358,7 @@ reload_gitlab(){ ## Restarts Sidekiq and Unicorn. restart_gitlab(){ check_status - if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_git_http_server" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then + if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; }; then stop_gitlab fi start_gitlab diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example index aab5acaa72c..79ae8e0ae55 100755 --- a/lib/support/init.d/gitlab.default.example +++ b/lib/support/init.d/gitlab.default.example @@ -30,15 +30,14 @@ web_server_pid_path="$pid_path/unicorn.pid" # The default is "$pid_path/sidekiq.pid" sidekiq_pid_path="$pid_path/sidekiq.pid" -gitlab_git_http_server_pid_path="$pid_path/gitlab-git-http-server.pid" -# The -listenXxx settings determine where gitlab-git-http-server +gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid" +# The -listenXxx settings determine where gitlab-workhorse # listens for connections from NGINX. To listen on localhost:8181, write # '-listenNetwork tcp -listenAddr localhost:8181'. -# The -authBackend setting tells gitlab-git-http-server where it can reach +# The -authBackend setting tells gitlab-workhorse where it can reach # Unicorn. -gitlab_git_http_server_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-git-http-server.socket -authBackend http://127.0.0.1:8080" -gitlab_git_http_server_repo_root="/home/git/repositories" -gitlab_git_http_server_log="$app_root/log/gitlab-git-http-server.log" +gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080" +gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log" # mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled. # This is required for the Reply by email feature. diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 1e55c5a0486..e767027dc29 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -38,8 +38,8 @@ upstream gitlab { server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; } -upstream gitlab-git-http-server { - server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0; +upstream gitlab-workhorse { + server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } ## Normal HTTP host @@ -114,24 +114,24 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } - location @gitlab-git-http-server { + location @gitlab-workhorse { ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. # gzip off; @@ -147,7 +147,7 @@ server { # The following settings only work with NGINX 1.7.11 or newer # - # # Pass chunked request bodies to gitlab-git-http-server as-is + # # Pass chunked request bodies to gitlab-workhorse as-is # proxy_request_buffering off; # proxy_http_version 1.1; @@ -156,7 +156,7 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://gitlab-git-http-server; + proxy_pass http://gitlab-workhorse; } ## Enable gzip compression as per rails guide: diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 08641bbcc17..4d31e31f8d5 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -42,8 +42,8 @@ upstream gitlab { server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; } -upstream gitlab-git-http-server { - server unix:/home/git/gitlab/tmp/sockets/gitlab-git-http-server.socket fail_timeout=0; +upstream gitlab-workhorse { + server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } ## Redirects all HTTP traffic to the HTTPS host @@ -161,24 +161,24 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } - location @gitlab-git-http-server { + location @gitlab-workhorse { ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. gzip off; @@ -194,7 +194,7 @@ server { # The following settings only work with NGINX 1.7.11 or newer # - # # Pass chunked request bodies to gitlab-git-http-server as-is + # # Pass chunked request bodies to gitlab-workhorse as-is # proxy_request_buffering off; # proxy_http_version 1.1; @@ -203,7 +203,7 @@ server { proxy_set_header X-Forwarded-Ssl on; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://gitlab-git-http-server; + proxy_pass http://gitlab-workhorse; } ## Enable gzip compression as per rails guide: -- cgit v1.2.1 From b1547021bcceb1f622c42b0967d0aaf80cb14cd1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 29 Oct 2015 14:22:11 +0100 Subject: Fix label destroy js Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/labels/destroy.js.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/labels/destroy.js.haml b/app/views/projects/labels/destroy.js.haml index 1b4c83ab097..d59563b122a 100644 --- a/app/views/projects/labels/destroy.js.haml +++ b/app/views/projects/labels/destroy.js.haml @@ -1,2 +1,2 @@ - if @project.labels.size == 0 - $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000) + $('.labels').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000) -- cgit v1.2.1 From 3be9d2c422b8651498abec3a2ee9bb6a3685f040 Mon Sep 17 00:00:00 2001 From: Ben Ford Date: Mon, 19 Oct 2015 14:52:46 -0700 Subject: Add ability to create directories in the editor Simply type a name with a `/` directory separator and new directories will be created. This does not do the fancy UI work that github.com does, but it will get the job done. I could not find tests for file creation, so I didn't add a test for this slight behaviour modification. I did test directory traversals though, using both absolute paths like `/tmp/foo.txt` and relative paths like `../../foo.txt`. Neither case escaped the repository, though attempting to traverse with a relative path resulted in a 500 error that did not affect application stability upon reload. --- CHANGELOG | 3 +++ app/assets/stylesheets/pages/editor.scss | 2 +- app/controllers/projects/blob_controller.rb | 2 +- app/services/files/create_dir_service.rb | 11 +++++++++++ app/services/files/create_service.rb | 11 ++++++++--- features/project/source/browse_files.feature | 10 ++++++++++ features/steps/project/source/browse_files.rb | 15 +++++++++++++++ lib/gitlab/regex.rb | 17 +++++++++++++++++ 8 files changed, 66 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..200e105db4f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ Please view this file on the master branch, on stable branches it's out of date. +development + - Adds ability to create directories using the web editor + v 8.2.0 (unreleased) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index 1d565477dd4..e2c521af91e 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -50,7 +50,7 @@ .editor-file-name { .new-file-name { display: inline-block; - width: 200px; + width: 450px; } .form-control { diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 8cc2f21d887..93738aa1ee5 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -161,7 +161,7 @@ class Projects::BlobController < Projects::ApplicationController if params[:file].present? params[:file_name] = params[:file].original_filename end - File.join(@path, File.basename(params[:file_name])) + File.join(@path, params[:file_name]) else @path end diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb index 71272fb5707..6107254a34e 100644 --- a/app/services/files/create_dir_service.rb +++ b/app/services/files/create_dir_service.rb @@ -5,5 +5,16 @@ module Files def commit repository.commit_dir(current_user, @file_path, @commit_message, @target_branch) end + + def validate + super + + unless @file_path =~ Gitlab::Regex.file_path_regex + raise_error( + 'Your changes could not be committed, because the file path ' + + Gitlab::Regex.file_path_regex_message + ) + end + end end end diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb index c8e3a910bba..2348920cc58 100644 --- a/app/services/files/create_service.rb +++ b/app/services/files/create_service.rb @@ -9,12 +9,17 @@ module Files def validate super - file_name = File.basename(@file_path) + if @file_path =~ Gitlab::Regex.directory_traversal_regex + raise_error( + 'Your changes could not be committed, because the file name ' + + Gitlab::Regex.directory_traversal_regex_message + ) + end - unless file_name =~ Gitlab::Regex.file_name_regex + unless @file_path =~ Gitlab::Regex.file_path_regex raise_error( 'Your changes could not be committed, because the file name ' + - Gitlab::Regex.file_name_regex_message + Gitlab::Regex.file_path_regex_message ) end diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 6b0484b6a38..69aa79f2d24 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -90,6 +90,16 @@ Feature: Project Source Browse Files Then I am on the new file page And I see a commit error message + @javascript + Scenario: I can create file with a directory name + Given I click on "New file" link in repo + And I fill the new file name with a new directory + And I edit code + And I fill the commit message + And I click on "Commit changes" + Then I am redirected to the new file with directory + And I should see its new content + @javascript Scenario: I can edit file 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 1b27500497a..84725b9b585 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -78,6 +78,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps fill_in :file_name, with: 'Spaces Not Allowed' end + step 'I fill the new file name with a new directory' do + fill_in :file_name, with: new_file_name_with_directory + end + step 'I fill the commit message' do fill_in :commit_message, with: 'Not yet a commit message.', visible: true end @@ -238,6 +242,11 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps @project.namespace, @project, 'master/' + new_file_name)) end + step 'I am redirected to the new file with directory' do + expect(current_path).to eq(namespace_project_blob_path( + @project.namespace, @project, 'master/' + new_file_name_with_directory)) + end + step 'I am redirected to the new file on new branch' do expect(current_path).to eq(namespace_project_blob_path( @project.namespace, @project, 'new_branch_name/' + new_file_name)) @@ -335,6 +344,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps 'not_a_file.md' end + # Constant value that is a valid filename with directory and + # not a filename present at root of the seed repository. + def new_file_name_with_directory + 'foo/bar/baz.txt' + end + # Constant value that is a valid directory and # not a directory present at root of the seed repository. def new_dir_name diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 9f1adc860d1..53ab2686b43 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -51,6 +51,23 @@ module Gitlab "can contain only letters, digits, '_', '-' and '.'. " end + def file_path_regex + @file_path_regex ||= /\A[a-zA-Z0-9_\-\.\/]*\z/.freeze + end + + def file_path_regex_message + "can contain only letters, digits, '_', '-' and '.'. Separate directories with a '/'. " + end + + + def directory_traversal_regex + @directory_traversal_regex ||= /\.{2}/.freeze + end + + def directory_traversal_regex_message + "cannot include directory traversal. " + end + def archive_formats_regex # |zip|tar| tar.gz | tar.bz2 | -- cgit v1.2.1 From 6d0337a97a9d21a27bdd96b48ae17d2458b4ad46 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 29 Oct 2015 15:48:45 +0100 Subject: Update changelog item --- CHANGELOG | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 200e105db4f..325583722f5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,5 @@ Please view this file on the master branch, on stable branches it's out of date. -development - - Adds ability to create directories using the web editor - v 8.2.0 (unreleased) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page @@ -32,6 +29,9 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) + - Adds ability to create directories using the web editor (Ben Ford) + +v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. -- cgit v1.2.1 From 6119b872fbb3b9dc19be3b21737dbb8380cf4847 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 29 Oct 2015 16:34:33 +0100 Subject: Add missing "cd" --- doc/update/8.1-to-8.2.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 38e5661b368..d84d13f33e0 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -79,6 +79,7 @@ from GitLab 8.1. ```bash cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git +cd gitlab-workhorse sudo -u git -H git checkout 0.3.1 sudo -u git -H make ``` -- cgit v1.2.1 From bc89c4f8d993028cd903217b6e1b989ca54307b9 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 29 Oct 2015 16:34:50 +0100 Subject: Make sed command GNU-compatible --- doc/update/8.1-to-8.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index d84d13f33e0..3772f624e98 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -90,7 +90,7 @@ Update the GitLab init script and 'default' file. cd /home/git/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab test -e /etc/default/gitlab && \ - sudo sed -i .pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab + sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab ``` Make sure that you also update your **NGINX configuration** to use -- cgit v1.2.1 From de990aa15829d0ab182ad5a55b4c527846c0d39c Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 29 Oct 2015 16:10:27 +0000 Subject: fixed last group owner issue and added test --- app/models/member.rb | 9 +++++---- features/groups.feature | 8 ++++++++ features/steps/groups.rb | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index cae8caa23fb..bc7e70178e1 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -81,11 +81,12 @@ class Member < ActiveRecord::Base member = members.build member.invite_email = user end + if !current_user || current_user.can?(:update_group_member, member) + member.created_by ||= current_user + member.access_level = access_level - member.created_by ||= current_user - member.access_level = access_level - - member.save + member.save + end end end diff --git a/features/groups.feature b/features/groups.feature index db37fa3b375..7777c0298a4 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -59,6 +59,14 @@ Feature: Groups When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" + @javascript + Scenario: Ignore add user to group when is already Owner + Given gitlab user "Mike" + When I visit group "Owned" members page + And I click link "Add members" + When I select "Mike" as "Reporter" + Then I should see "Mike" in team list as "Owner" + @javascript Scenario: Invite user to group When I visit group "Owned" members page diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 69ddfa42c06..5fd4dea5cd6 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -48,6 +48,17 @@ class Spinach::Features::Groups < Spinach::FeatureSteps click_button "Add users to group" end + step 'I select "Mike" as "Master"' do + user = User.find_by(name: "Mike") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Master", from: "access_level" + end + + click_button "Add users to group" + end + step 'I should see "Mike" in team list as "Reporter"' do page.within '.well-list' do expect(page).to have_content('Mike') @@ -55,6 +66,13 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end end + step 'I should see "Mike" in team list as "Owner"' do + page.within '.well-list' do + expect(page).to have_content('Mike') + expect(page).to have_content('Owner') + end + end + step 'I select "sjobs@apple.com" as "Reporter"' do page.within ".users-group-form" do select2("sjobs@apple.com", from: "#user_ids", multiple: true) -- cgit v1.2.1 From c8fe42151291593f0f43509a70235c46fce169a1 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 29 Oct 2015 18:42:29 -0200 Subject: Improve personal snippet access workflow. Fixes #3258 --- CHANGELOG | 1 + app/controllers/snippets_controller.rb | 9 +- app/models/ability.rb | 65 +++++++++++---- spec/controllers/snippets_controller_spec.rb | 118 +++++++++++++++++++++++++++ spec/factories.rb | 12 +++ 5 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 spec/controllers/snippets_controller_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..325a073b5c5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Improve personal snippet access workflow v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 9f9f9a92f11..8498efc89d0 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -1,6 +1,9 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] + # Allow read snippet + before_action :authorize_show_snippet!, only: [:show] + # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] @@ -79,10 +82,14 @@ class SnippetsController < ApplicationController [Snippet::PUBLIC, Snippet::INTERNAL]). find(params[:id]) else - PersonalSnippet.are_public.find(params[:id]) + PersonalSnippet.find(params[:id]) end end + def authorize_show_snippet! + authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) + end + def authorize_update_snippet! return render_404 unless can?(current_user, :update_personal_snippet, @snippet) end diff --git a/app/models/ability.rb b/app/models/ability.rb index b72178fa126..ee2f7b5f58b 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -22,12 +22,17 @@ class Ability # List of possible abilities # for non-authenticated user def not_auth_abilities(user, subject) + return not_auth_personal_snippet_abilities(subject) if subject.kind_of?(PersonalSnippet) + return not_auth_project_abilities(subject) if subject.kind_of?(Project) || subject.respond_to?(:project) + return not_auth_group_abilities(subject) if subject.kind_of?(Group) || subject.respond_to?(:group) + [] + end + + def not_auth_project_abilities(subject) project = if subject.kind_of?(Project) subject - elsif subject.respond_to?(:project) - subject.project else - nil + subject.project end if project && project.public? @@ -47,19 +52,29 @@ class Ability rules - project_disabled_features_rules(project) else - group = if subject.kind_of?(Group) - subject - elsif subject.respond_to?(:group) - subject.group - else - nil - end + [] + end + end - if group && group.public_profile? - [:read_group] - else - [] - end + def not_auth_group_abilities(subject) + group = if subject.kind_of?(Group) + subject + else + subject.group + end + + if group && group.public_profile? + [:read_group] + else + [] + end + end + + def not_auth_personal_snippet_abilities(snippet) + if snippet.public? + [:read_personal_snippet] + else + [] end end @@ -278,7 +293,7 @@ class Ability end end - [:note, :project_snippet, :personal_snippet].each do |name| + [:note, :project_snippet].each do |name| define_method "#{name}_abilities" do |user, subject| rules = [] @@ -298,6 +313,24 @@ class Ability end end + def personal_snippet_abilities(user, snippet) + rules = [] + + if snippet.author == user + rules += [ + :read_personal_snippet, + :update_personal_snippet, + :admin_personal_snippet + ] + end + + if snippet.public? || snippet.internal? + rules.push(:read_snippet) + end + + rules + end + def group_member_abilities(user, subject) rules = [] target_user = subject.user diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb new file mode 100644 index 00000000000..e9b823c523c --- /dev/null +++ b/spec/controllers/snippets_controller_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe SnippetsController do + describe 'GET #show' do + let(:user) { create(:user) } + + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + + it 'responds with status 404' do + get :show, id: other_personal_snippet.to_param + + expect(response.status).to eq(404) + end + end + + context 'when signed in user is the author' do + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :show, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :show, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'renders the snippet' do + get :show, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 404' do + get :show, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + + context 'when not signed in' do + it 'responds with status 404' do + get :show, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 200f18f660d..4bf93adabe2 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -165,6 +165,18 @@ FactoryGirl.define do title content file_name + + trait :public do + visibility_level Gitlab::VisibilityLevel::PUBLIC + end + + trait :internal do + visibility_level Gitlab::VisibilityLevel::INTERNAL + end + + trait :private do + visibility_level Gitlab::VisibilityLevel::PRIVATE + end end factory :snippet do -- cgit v1.2.1 From 49c081b9f38e99bbc11e7132d87773749b5b39d5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 28 Oct 2015 14:43:27 +0100 Subject: Improve performance of User.find_by_any_email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This query used to rely on a JOIN, effectively producing the following SQL: SELECT users.* FROM users LEFT OUTER JOIN emails ON emails.user_id = users.id WHERE (users.email = X OR emails.email = X) LIMIT 1; The use of a JOIN means having to scan over all Emails and users, join them together and then filter out the rows that don't match the criteria (though this step may be taken into account already when joining). In the new setup this query instead uses a sub-query, producing the following SQL: SELECT * FROM users WHERE id IN (select user_id FROM emails WHERE email = X) OR email = X LIMIT 1; This query has the benefit that it: 1. Doesn't have to JOIN any rows 2. Only has to operate on a relatively small set of rows from the "emails" table. Since most users will only have a handful of Emails associated (certainly not hundreds or even thousands) the size of the set returned by the sub-query is small enough that it should not become problematic. Performance of the old versus new version can be measured using the following benchmark: # Save this in ./bench.rb require 'benchmark/ips' email = 'yorick@gitlab.com' def User.find_by_any_email_old(email) user_table = arel_table email_table = Email.arel_table query = user_table. project(user_table[Arel.star]). join(email_table, Arel::Nodes::OuterJoin). on(user_table[:id].eq(email_table[:user_id])). where(user_table[:email].eq(email).or(email_table[:email].eq(email))) find_by_sql(query.to_sql).first end Benchmark.ips do |bench| bench.report 'original' do User.find_by_any_email_old(email) end bench.report 'optimized' do User.find_by_any_email(email) end bench.compare! end Running this locally using "bundle exec rails r bench.rb" produces the following output: Calculating ------------------------------------- original 1.000 i/100ms optimized 93.000 i/100ms ------------------------------------------------- original 11.103 (± 0.0%) i/s - 56.000 optimized 948.713 (± 5.3%) i/s - 4.743k Comparison: optimized: 948.7 i/s original: 11.1 i/s - 85.45x slower In other words, the new setup is 85x faster compared to the old setup, at least when running this benchmark locally. For GitLab.com these improvements result in User.find_by_any_email taking only ~170 ms to run, instead of around 800 ms. While this is "only" an improvement of about 4.5 times (instead of 85x) it's still significantly better than before. Fixes #3242 --- app/models/user.rb | 18 +++--------------- spec/benchmarks/models/user_spec.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index c72beacbf0f..924cb543fab 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -235,21 +235,9 @@ class User < ActiveRecord::Base # Find a User by their primary email or any associated secondary email def find_by_any_email(email) - user_table = arel_table - email_table = Email.arel_table - - # Use ARel to build a query: - query = user_table. - # SELECT "users".* FROM "users" - project(user_table[Arel.star]). - # LEFT OUTER JOIN "emails" - join(email_table, Arel::Nodes::OuterJoin). - # ON "users"."id" = "emails"."user_id" - on(user_table[:id].eq(email_table[:user_id])). - # WHERE ("user"."email" = '' OR "emails"."email" = '') - where(user_table[:email].eq(email).or(email_table[:email].eq(email))) - - find_by_sql(query.to_sql).first + User.reorder(nil). + where('id IN (SELECT user_id FROM emails WHERE email = :email) OR email = :email', email: email). + take end def filter(filter_name) diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb index cc5c3904193..74520aaf3f0 100644 --- a/spec/benchmarks/models/user_spec.rb +++ b/spec/benchmarks/models/user_spec.rb @@ -39,4 +39,30 @@ describe User, benchmark: true do it { is_expected.to iterate_per_second(iterations) } end end + + describe '.find_by_any_email' do + let(:user) { create(:user) } + + describe 'using a user with only a single Email address' do + let(:email) { user.email } + + benchmark_subject { User.find_by_any_email(email) } + + it { is_expected.to iterate_per_second(5000) } + end + + describe 'using a user with multiple Email addresses' do + let(:email) { user.emails.first.email } + + benchmark_subject { User.find_by_any_email(email) } + + before do + 10.times do + user.emails.create(email: FFaker::Internet.email) + end + end + + it { is_expected.to iterate_per_second(5000) } + end + end end -- cgit v1.2.1 From 0a6aaf256900ab47058d03d3b74883420c4643ae Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 28 Oct 2015 14:57:52 +0100 Subject: Changelog entry for User.find_by_any_email --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index cf44a5c7c82..6bec4f606e7 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.2.0 (unreleased) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) + - Improved performance of finding users by one of their Email addresses - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL -- cgit v1.2.1 From 24c8f42278844cf48cdd9871b8285445379623f0 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 29 Oct 2015 12:05:47 +0100 Subject: Use a UNION for User.find_by_any_email This is significantly faster than using a sub-query, at least when run on the GitLab.com production database. The benchmarks are a lot slower now with these changes, most likely due to PostgreSQL choosing a different (and less efficient) plan based on the amount of data present in the test database. Thanks to @dlemstra for suggesting the use of a UNION. --- app/models/user.rb | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 924cb543fab..35f5ab56798 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -235,9 +235,18 @@ class User < ActiveRecord::Base # Find a User by their primary email or any associated secondary email def find_by_any_email(email) - User.reorder(nil). - where('id IN (SELECT user_id FROM emails WHERE email = :email) OR email = :email', email: email). - take + # Arel doesn't allow for chaining operations on union nodes, thus we have + # to write this query by hand. See the following issue for more info: + # https://github.com/rails/arel/issues/98. + sql = '(SELECT * FROM users WHERE email = :email + UNION + SELECT users.* + FROM emails + INNER JOIN users ON users.id = emails.user_id + WHERE emails.email = :email) + LIMIT 1;' + + User.find_by_sql([sql, { email: email }]).first end def filter(filter_name) -- cgit v1.2.1 From bba46623c20de52942c85eb2aba3649e3f4ba296 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 29 Oct 2015 13:33:20 +0100 Subject: Fixed UNION syntax for MySQL MySQL doesn't support the previous syntax. --- app/models/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 35f5ab56798..66db70080b7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -238,9 +238,9 @@ class User < ActiveRecord::Base # Arel doesn't allow for chaining operations on union nodes, thus we have # to write this query by hand. See the following issue for more info: # https://github.com/rails/arel/issues/98. - sql = '(SELECT * FROM users WHERE email = :email + sql = '(SELECT * FROM users WHERE email = :email) UNION - SELECT users.* + (SELECT users.* FROM emails INNER JOIN users ON users.id = emails.user_id WHERE emails.email = :email) -- cgit v1.2.1 From a9df714764d6138bf162acd82b780ca82a21864b Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 29 Oct 2015 17:51:49 +0100 Subject: Use a subquery with IDs only for find_by_any_email This further improves performance of User.find_by_any_email and is roughly twice as fast as the previous UNION setup. Thanks again to @dlemstra for suggesting this. --- app/models/user.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 66db70080b7..67fef1c1e6a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -235,15 +235,13 @@ class User < ActiveRecord::Base # Find a User by their primary email or any associated secondary email def find_by_any_email(email) - # Arel doesn't allow for chaining operations on union nodes, thus we have - # to write this query by hand. See the following issue for more info: - # https://github.com/rails/arel/issues/98. - sql = '(SELECT * FROM users WHERE email = :email) - UNION - (SELECT users.* - FROM emails - INNER JOIN users ON users.id = emails.user_id - WHERE emails.email = :email) + sql = 'SELECT * + FROM users + WHERE id IN ( + SELECT id FROM users WHERE email = :email + UNION + SELECT emails.user_id FROM emails WHERE email = :email + ) LIMIT 1;' User.find_by_sql([sql, { email: email }]).first -- cgit v1.2.1 From 6d3068bec3a926d17f4f2d0da895856489bfcb7a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 29 Oct 2015 17:53:56 +0100 Subject: Adjusted ips/sec for find_by_any_email benchmarks While these benchmarks run at roughly 1500 i/sec setting the threshold to 1000 leaves some room for deviations (e.g. due to different DB setups). --- spec/benchmarks/models/user_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb index 74520aaf3f0..4cdba66939b 100644 --- a/spec/benchmarks/models/user_spec.rb +++ b/spec/benchmarks/models/user_spec.rb @@ -48,7 +48,7 @@ describe User, benchmark: true do benchmark_subject { User.find_by_any_email(email) } - it { is_expected.to iterate_per_second(5000) } + it { is_expected.to iterate_per_second(1000) } end describe 'using a user with multiple Email addresses' do @@ -62,7 +62,7 @@ describe User, benchmark: true do end end - it { is_expected.to iterate_per_second(5000) } + it { is_expected.to iterate_per_second(1000) } end end end -- cgit v1.2.1 From 72ececfab18d4577fbd7e57d44b771576eb204bf Mon Sep 17 00:00:00 2001 From: Thirumal S Date: Fri, 30 Oct 2015 17:27:22 +0530 Subject: form-control class to select box --- app/views/profiles/notifications/_settings.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/notifications/_settings.html.haml b/app/views/profiles/notifications/_settings.html.haml index 2c85d2a9b2b..742c5c4b68d 100644 --- a/app/views/profiles/notifications/_settings.html.haml +++ b/app/views/profiles/notifications/_settings.html.haml @@ -14,4 +14,4 @@ = form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications' do = hidden_field_tag :notification_type, type, id: dom_id(membership, 'notification_type') = hidden_field_tag :notification_id, membership.id, id: dom_id(membership, 'notification_id') - = select_tag :notification_level, options_for_select(Notification.options_with_labels, notification.level), class: 'trigger-submit' + = select_tag :notification_level, options_for_select(Notification.options_with_labels, notification.level), class: 'form-control trigger-submit' -- cgit v1.2.1 From 74416daa660a047d3a7cb00e11e1d775b4ea0937 Mon Sep 17 00:00:00 2001 From: Thirumal S Date: Fri, 30 Oct 2015 19:26:56 +0530 Subject: form group alignment issue fixed in webhook index page --- app/assets/stylesheets/pages/projects.scss | 4 ++++ app/views/projects/hooks/index.html.haml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index f7a22849003..b384e3fae6c 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -511,3 +511,7 @@ pre.light-well { margin-top: -1px; } } + +.form-control-padding-top { + padding-top: 10px; +} diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 85dbfd67862..65e00b38ad4 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -19,7 +19,7 @@ = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' .form-group = f.label :url, "Trigger", class: 'control-label' - .col-sm-10 + .col-sm-10.form-control-padding-top %div = f.check_box :push_events, class: 'pull-left' .prepend-left-20 -- cgit v1.2.1 From 31ea88cfed2580975f621a6aa992ac58405cb275 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sat, 31 Oct 2015 02:27:44 +0900 Subject: Fix deadlink in docs for ci/examples --- doc/ci/examples/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index e0b9fa0e25d..c8122fc63b3 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -1,5 +1,5 @@ # Build script examples -+ [Test and deploy Ruby Application to Heroku](test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy Python Application to Heroku](test-and-deploy-python-application-to-heroku.md) -+ [Test Clojure applications](examples/test-clojure-application.md) ++ [Test and deploy Ruby applications to Heroku](test-and-deploy-ruby-application-to-heroku.md) ++ [Test and deploy Python applications to Heroku](test-and-deploy-python-application-to-heroku.md) ++ [Test Clojure applications](test-clojure-application.md) -- cgit v1.2.1 From 6aa9c21ac0e3f4860f9021718900326ea0575151 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Fri, 30 Oct 2015 19:55:19 +0000 Subject: fix issue with adding members to project (spotted by test) --- app/models/member.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index bc7e70178e1..4651c8fff37 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -73,7 +73,7 @@ class Member < ActiveRecord::Base def add_user(members, user_id, access_level, current_user = nil) user = user_for_id(user_id) - + # `user` can be either a User object or an email to be invited if user.is_a?(User) member = members.find_or_initialize_by(user_id: user.id) @@ -81,13 +81,22 @@ class Member < ActiveRecord::Base member = members.build member.invite_email = user end - if !current_user || current_user.can?(:update_group_member, member) + + project = members.first.respond_to?(:project)? members.first.project : nil + if can_update_member?(current_user, member, project) member.created_by ||= current_user member.access_level = access_level member.save end end + + private + + def can_update_member?(current_user, member, project) + !current_user || current_user.can?(:update_group_member, member) || + (project && current_user.can?(:admin_project_member, project)) + end end def invite? -- cgit v1.2.1 From 31723eb9f0f9490d873a6ecddc897fef3ea1885c Mon Sep 17 00:00:00 2001 From: KON YUICHI Date: Sat, 31 Oct 2015 22:32:06 +0900 Subject: fix deprecated --- app/controllers/projects_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 05c7d3de8bc..00d13a83ce8 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,7 +1,7 @@ class ProjectsController < ApplicationController include ExtractsPath - prepend_before_filter :render_go_import, only: [:show] + prepend_before_action :render_go_import, only: [:show] skip_before_action :authenticate_user!, only: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] -- cgit v1.2.1 From 3b0039f659eab1c29e735ef2a5443e26b33d5d18 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 31 Oct 2015 16:06:06 +0100 Subject: Persist blob editor's value on submit, not on click Prior, the value of the Ace editor was only being persisted if the user physically clicked the submit button, which the "quick submit" behavior doesn't do. Now the value will be properly transferred before any form is submitted. --- app/assets/javascripts/blob/edit_blob.js.coffee | 8 ++++---- app/assets/javascripts/blob/new_blob.js.coffee | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index 050888f9c15..f6bf836f19f 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,10 +11,10 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $(".js-commit-button").click -> - $("#file-content").val editor.getValue() - $(".file-editor form").submit() - return false + # Before a form submission, move the content from the Ace editor into the + # submitted textarea + $('form').submit -> + $("#file-content").val(editor.getValue()) editModePanes = $(".js-edit-mode-pane") editModeLinks = $(".js-edit-mode a") diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index 1f36a53f191..68c5e5195e3 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,10 +11,10 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $(".js-commit-button").click -> - $("#file-content").val editor.getValue() - $(".file-editor form").submit() - return false + # Before a form submission, move the content from the Ace editor into the + # submitted textarea + $('form').submit -> + $("#file-content").val(editor.getValue()) editor: -> return @editor -- cgit v1.2.1 From 98008a2db85a5320d885cd515557d4093a1cba24 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 31 Oct 2015 22:56:24 +0100 Subject: Fix typo in rake task doc --- doc/development/rake_tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index a4a980cf0e0..9f3fd69fc4e 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -9,7 +9,7 @@ bundle exec rake setup ``` The `setup` task is a alias for `gitlab:setup`. -This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and fianlly it calls `db:seed_fu` to seed the database. +This tasks calls `db:setup` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database. Note: `db:setup` calls `db:seed` but this does nothing. ## Run tests -- cgit v1.2.1 From a1d0eca88686faabd9742f92174a0caa76e36cf6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 31 Oct 2015 23:31:21 +0100 Subject: Gilab -> GitLab --- doc/ci/api/README.md | 2 +- doc/ci/api/projects.md | 22 +++++++++++----------- doc/ci/api/runners.md | 4 ++-- doc/install/installation.md | 2 +- doc/operations/unicorn.md | 2 +- doc/ssh/README.md | 7 +++---- .../importing/import_projects_from_gitlab_com.md | 4 ++-- 7 files changed, 21 insertions(+), 22 deletions(-) diff --git a/doc/ci/api/README.md b/doc/ci/api/README.md index 33c5b172e98..cf9710ede57 100644 --- a/doc/ci/api/README.md +++ b/doc/ci/api/README.md @@ -25,7 +25,7 @@ GitLab CI API has 4 authentication methods: Authentication is done by sending the `private-token` of a valid user and the `url` of an -authorized Gitlab instance via a query string along with the API +authorized GitLab instance via a query string along with the API request: GET http://gitlab.example.com/ci/api/v1/projects?private_token=QVy1PB7sTxfy4pqfZM1U&url=http://demo.gitlab.com/ diff --git a/doc/ci/api/projects.md b/doc/ci/api/projects.md index 5585191e826..74a4c64d000 100644 --- a/doc/ci/api/projects.md +++ b/doc/ci/api/projects.md @@ -1,7 +1,7 @@ # Projects API This API is intended to aid in the setup and configuration of -projects on Gitlab CI. +projects on GitLab CI. __Authentication is done by GitLab user token & GitLab url__ @@ -88,23 +88,23 @@ authorized. Parameters: - * `id` (required) - The ID of the Gitlab CI project + * `id` (required) - The ID of the GitLab CI project ### Create Project -Creates a Gitlab CI project using Gitlab project details. +Creates a GitLab CI project using GitLab project details. POST /ci/projects Parameters: * `name` (required) - The name of the project - * `gitlab_id` (required) - The ID of the project on the Gitlab instance + * `gitlab_id` (required) - The ID of the project on the GitLab instance * `default_ref` (optional) - The branch to run on (default to `master`) ### Update Project -Updates a Gitlab CI project using Gitlab project details that the +Updates a GitLab CI project using GitLab project details that the authenticated user has access to. PUT /ci/projects/:id @@ -116,13 +116,13 @@ Parameters: ### Remove Project -Removes a Gitlab CI project that the authenticated user has access to. +Removes a GitLab CI project that the authenticated user has access to. DELETE /ci/projects/:id Parameters: - * `id` (required) - The ID of the Gitlab CI project + * `id` (required) - The ID of the GitLab CI project ### Link Project to Runner @@ -133,8 +133,8 @@ authorized user). Parameters: - * `id` (required) - The ID of the Gitlab CI project - * `runner_id` (required) - The ID of the Gitlab CI runner + * `id` (required) - The ID of the GitLab CI project + * `runner_id` (required) - The ID of the GitLab CI runner ### Remove Project from Runner @@ -145,5 +145,5 @@ via authorized user). Parameters: - * `id` (required) - The ID of the Gitlab CI project - * `runner_id` (required) - The ID of the Gitlab CI runner \ No newline at end of file + * `id` (required) - The ID of the GitLab CI project + * `runner_id` (required) - The ID of the GitLab CI runner \ No newline at end of file diff --git a/doc/ci/api/runners.md b/doc/ci/api/runners.md index e9f88ee066e..c383dc4bcc9 100644 --- a/doc/ci/api/runners.md +++ b/doc/ci/api/runners.md @@ -6,7 +6,7 @@ __Authentication is done by GitLab user token & GitLab url__ -Used to get information about all runners registered on the Gitlab CI +Used to get information about all runners registered on the GitLab CI instance. GET /ci/runners @@ -31,7 +31,7 @@ Returns: __Authentication is done with a Shared runner registration token or a project Specific runner registration token__ -Used to make Gitlab CI aware of available runners. +Used to make GitLab CI aware of available runners. POST /ci/runners/register diff --git a/doc/install/installation.md b/doc/install/installation.md index b48acb655ee..b02e8e8e588 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -489,7 +489,7 @@ See the [omniauth integration document](../integration/omniauth.md) ### Build your projects GitLab can build your projects. To enable that feature you need GitLab Runners to do that for you. -Checkout the [Gitlab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it +Checkout the [GitLab Runner section](https://about.gitlab.com/gitlab-ci/#gitlab-runner) to install it ### Custom Redis Connection diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md index 31b432cd411..3998da01f01 100644 --- a/doc/operations/unicorn.md +++ b/doc/operations/unicorn.md @@ -78,7 +78,7 @@ threshold is a random value between 200 and 250 MB. The master process (PID ``` One other thing that stands out in the log snippet above, taken from -Gitlab.com, is that 'worker 4' was serving requests for only 23 seconds. This +GitLab.com, is that 'worker 4' was serving requests for only 23 seconds. This is a normal value for our current GitLab.com setup and traffic. The high frequency of Unicorn memory restarts on some GitLab sites can be a diff --git a/doc/ssh/README.md b/doc/ssh/README.md index b6b8000af4e..0bdb4070e74 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -15,8 +15,7 @@ Note: It is a best practice to use a password for an SSH key, but it is not required and you can skip creating a password by pressing enter. Note that the password you choose here can't be altered or retrieved. -To generate a new SSH key, use the following command: -```bash +To generate a new SSH key, use the following commandGitLab```bash ssh-keygen -t rsa -C "$your_email" ``` This command will prompt you for a location and filename to store the key @@ -82,7 +81,7 @@ How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Ecli ## Tip: Non-default OpenSSH key file names or locations -If, for whatever reason, you decide to specify a non-default location and filename for your Gitlab SSH key pair, you must configure your SSH client to find your Gitlab SSH private key for connections to your Gitlab server (perhaps gitlab.com). For OpenSSH clients, this is handled in the `~/.ssh/config` file with a stanza similar to the following: +If, for whatever reason, you decide to specify a non-default location and filename for your GitLab SSH key pair, you must configure your SSH client to find your GitLab SSH private key for connections to your GitLab server (perhaps gitlab.com). For OpenSSH clients, this is handled in the `~/.ssh/config` file with a stanza similar to the following: ``` # @@ -97,7 +96,7 @@ User mygitlabusername Another example ``` # -# Our company's internal Gitlab server +# Our company's internal GitLab server # Host my-gitlab.company.com RSAAuthentication yes diff --git a/doc/workflow/importing/import_projects_from_gitlab_com.md b/doc/workflow/importing/import_projects_from_gitlab_com.md index f4c4e955d46..1117db98e7e 100644 --- a/doc/workflow/importing/import_projects_from_gitlab_com.md +++ b/doc/workflow/importing/import_projects_from_gitlab_com.md @@ -2,12 +2,12 @@ You can import your existing GitLab.com projects to your GitLab instance. But keep in mind that it is possible only if GitLab support is enabled on your GitLab instance. -You can read more about Gitlab support [here](http://doc.gitlab.com/ce/integration/gitlab.html) +You can read more about GitLab support [here](http://doc.gitlab.com/ce/integration/gitlab.html) To get to the importer page you need to go to "New project" page. ![New project page](gitlab_importer/new_project_page.png) -Click on the "Import projects from Gitlab.com" link and you will be redirected to GitLab.com +Click on the "Import projects from GitLab.com" link and you will be redirected to GitLab.com for permission to access your projects. After accepting, you'll be automatically redirected to the importer. -- cgit v1.2.1 From 3b717c8a8c1e0f10bc06fd8501ce2423c98490d4 Mon Sep 17 00:00:00 2001 From: Jan-Gerd Tenberge Date: Sun, 1 Nov 2015 02:42:36 +0100 Subject: Fix typo --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3fa7d2bb1cd..3230ff1b004 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -78,7 +78,7 @@ module ApplicationHelper if user user.avatar_url(size) || default_avatar else - gravatar_icon(user_or_email, size, scales) + gravatar_icon(user_or_email, size, scale) end end -- cgit v1.2.1 From c843722de2d778b6ec8fef0656797fd5a8074666 Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Mon, 20 Jul 2015 20:34:19 -0500 Subject: Add graphs showing commits ahead and behind default to branches page --- CHANGELOG | 1 + app/assets/stylesheets/pages/commits.scss | 59 +++++++++++++++++++++++++ app/controllers/projects/branches_controller.rb | 6 +++ app/models/project.rb | 2 + app/models/repository.rb | 42 +++++++++++++++++- app/views/projects/branches/_branch.html.haml | 14 ++++++ 6 files changed, 122 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6bec4f606e7..98702088199 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Add graphs of commits ahead and behind default branch (Jeff Stubler) v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..4a080db7464 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -113,3 +113,62 @@ li.commit { } } } + +.divergence-graph { + padding: 12px 12px 0 0; + float: right; + + .graph-side { + position: relative; + width: 80px; + height: 22px; + padding: 5px 0 13px; + float: left; + + .bar { + position: absolute; + height: 4px; + background-color: #ccc; + } + + .bar-behind { + right: 0; + border-radius: 3px 0 0 3px; + } + + .bar-ahead { + left: 0; + border-radius: 0 3px 3px 0; + } + + .count { + padding-top: 6px; + padding-bottom: 0px; + font-size: 12px; + color: #333; + display: block; + } + + .count-behind { + padding-right: 4px; + text-align: right; + } + + .count-ahead { + padding-left: 4px; + text-align: left; + } + } + + .graph-separator { + position: relative; + width: 1px; + height: 18px; + margin: 5px 0 0; + float: left; + background-color: #ccc; + } +} + + + diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3ac0a75fa70..c3cd7642dd2 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -9,6 +9,12 @@ class Projects::BranchesController < Projects::ApplicationController @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) + + @max_commits = @branches.reduce(0) do + |memo, branch| + diverging_commit_counts = repository.diverging_commit_counts(branch) + [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max + end end def recent diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..e73a856c289 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -714,6 +714,8 @@ class Project < ActiveRecord::Base end def change_head(branch) + # Cached divergent commit counts are based on repository head + repository.expire_branch_cache gitlab_shell.update_repository_head(self.path_with_namespace, branch) reload_default_branch end diff --git a/app/models/repository.rb b/app/models/repository.rb index c9b36bd8170..9b270bc9d18 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -146,10 +146,27 @@ class Repository def size cache.fetch(:size) { raw_repository.size } end + + def diverging_commit_counts(branch) + branch_cache_key = ('diverging_commit_counts_' + branch.name).to_sym + cache.fetch(branch_cache_key) do + number_commits_behind = commits_between(branch.name, root_ref).size + number_commits_ahead = commits_between(root_ref, branch.name).size + + { behind: number_commits_behind, ahead: number_commits_ahead } + end + end def cache_keys - %i(size branch_names tag_names commit_count - readme version contribution_guide changelog license) + %i(size branch_names tag_names commit_count readme + contribution_guide changelog license) + end + + def branch_cache_keys + branches.map do + |branch| + ('diverging_commit_counts_' + branch.name).to_sym + end end def build_cache @@ -158,12 +175,28 @@ class Repository send(key) end end + + branches.each do |branch| + unless cache.exist?(('diverging_commit_counts_' + branch.name).to_sym) + send(:diverging_commit_counts, branch) + end + end end def expire_cache cache_keys.each do |key| cache.expire(key) end + + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + end + end + + def expire_branch_cache + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + end end def rebuild_cache @@ -171,6 +204,11 @@ class Repository cache.expire(key) send(key) end + + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + send(:diverging_commit_counts, branch) + end end def lookup_cache diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index cc0ec9483d2..9ddb10a1c74 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -1,4 +1,7 @@ - commit = @repository.commit(branch.target) +- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0 +- number_commits_behind = @repository.diverging_commit_counts(branch)[:behind] +- number_commits_ahead = @repository.diverging_commit_counts(branch)[:ahead] %li(class="js-branch-#{branch.name}") %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do @@ -29,6 +32,17 @@ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do = icon("trash-o") + - if branch.name != @repository.root_ref + .divergence-graph{ :title => "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } + .graph-side + .bar.bar-behind{ :style => "width: #{number_commits_behind * bar_graph_width_factor}%" } + %span.count.count-behind= number_commits_behind + .graph-separator + .graph-side + .bar.bar-ahead{ :style => "width: #{number_commits_ahead * bar_graph_width_factor}%" } + %span.count.count-ahead= number_commits_ahead + + - if commit = render 'projects/branches/commit', commit: commit, project: @project - else -- cgit v1.2.1 From 1045a72eb7fd16ab09b7e68e1a467ff0f5c9215d Mon Sep 17 00:00:00 2001 From: zheng_b Date: Mon, 2 Nov 2015 07:59:04 +0100 Subject: Go to gitlab installation folder before initialize database --- doc/install/installation.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/install/installation.md b/doc/install/installation.md index b48acb655ee..8ed1eba560a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -329,6 +329,10 @@ GitLab Shell is an SSH access and repository management software developed speci sudo -u git -H make ### Initialize Database and Activate Advanced Features + + # Go to Gitlab installation folder + + cd /home/git/gilab sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production -- cgit v1.2.1 From c03da1caade637298ca96e59cea990b0827539c9 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 2 Nov 2015 14:44:06 +0100 Subject: Extend yml syntax for only and except to support specifying repository path --- CHANGELOG | 1 + app/models/ci/commit.rb | 2 +- doc/ci/yaml/README.md | 14 +- lib/ci/gitlab_ci_yaml_processor.rb | 64 +++--- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 303 ++++++++++++++++++++------- 5 files changed, 273 insertions(+), 111 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6bec4f606e7..eec74e5c5f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.2.0 (unreleased) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page + - Extend yml syntax for only and except to support specifying repository path - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 13437b2483f..e58420d82d4 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -187,7 +187,7 @@ module Ci end def config_processor - @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index ea8f72bc135..0c1d8cfdff6 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -169,7 +169,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. +1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -182,6 +182,18 @@ job: - branches # use special keyword ``` +1. `only` and `except` allow for specify repository path to filter jobs for forks. +The repository path can be used to have jobs executed only for parent repository. + +```yaml +job: + only: + - branches@gitlab-org/gitlab-ce + except: + - master@gitlab-org/gitlab-ce +``` +The above will run `job` for all branches, except master on `gitlab-org/gitlab-ce` repository only. + ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index efcd2faffc7..0f57a4f53ab 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -7,10 +7,11 @@ module Ci ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables] ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when] - attr_reader :before_script, :image, :services, :variables + attr_reader :before_script, :image, :services, :variables, :path - def initialize(config) + def initialize(config, path = nil) @config = YAML.load(config) + @path = path unless @config.is_a? Hash raise ValidationError, "YAML should be a hash" @@ -63,26 +64,6 @@ module Ci end end - def process?(only_params, except_params, ref, tag) - return true if only_params.nil? && except_params.nil? - - if only_params - return true if tag && only_params.include?("tags") - return true if !tag && only_params.include?("branches") - - only_params.find do |pattern| - match_ref?(pattern, ref) - end - else - return false if tag && except_params.include?("tags") - return false if !tag && except_params.include?("branches") - - except_params.each do |pattern| - return false if match_ref?(pattern, ref) - end - end - end - def build_job(name, job) { stage_idx: stages.index(job[:stage]), @@ -101,14 +82,6 @@ module Ci } end - def match_ref?(pattern, ref) - if pattern.first == "/" && pattern.last == "/" - Regexp.new(pattern[1...-1]) =~ ref - else - pattern == ref - end - end - def normalize_script(script) if script.is_a? Array script.join("\n") @@ -208,5 +181,36 @@ module Ci def validate_string(value) value.is_a?(String) || value.is_a?(Symbol) end + + def process?(only_params, except_params, ref, tag) + if only_params.present? + return false unless matching?(only_params, ref, tag) + end + + if except_params.present? + return false if matching?(except_params, ref, tag) + end + + true + end + + def matching?(patterns, ref, tag) + patterns.any? do |pattern| + match_ref?(pattern, ref, tag) + end + end + + def match_ref?(pattern, ref, tag) + pattern, path = pattern.split('@', 2) + return false if path && path != self.path + return true if tag && pattern == 'tags' + return true if !tag && pattern == 'branches' + + if pattern.first == "/" && pattern.last == "/" + Regexp.new(pattern[1...-1]) =~ ref + else + pattern == ref + end + end end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index abdb6b89ac5..a141a459ba3 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -2,7 +2,8 @@ require 'spec_helper' module Ci describe GitlabCiYamlProcessor do - + let(:path) { 'path' } + describe "#builds_for_ref" do let(:type) { 'test' } @@ -12,7 +13,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({ @@ -28,78 +29,222 @@ module Ci when: "on_success" }) end + + describe :only do + it "does not return builds if only has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["deploy"] } + }) - it "does not return builds if only has another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["deploy"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) - end + it "does not return builds if only has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["/^deploy$/"] } + }) - it "does not return builds if only has regexp with another branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["/^deploy$/"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) - end + it "returns builds if only has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", only: ["master"] } + }) - it "returns builds if only has specified this branch" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", only: ["master"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end - expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) - end + it "returns builds if only has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["master", "deploy"] } + }) - it "does not build tags" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", except: ["tags"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end - expect(config_processor.builds_for_stage_and_ref(type, "0-1", true).size).to eq(0) - end + it "returns builds if only has a branches keyword specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches"] } + }) - it "returns builds if only has a list of branches including specified" do - config = YAML.dump({ - before_script: ["pwd"], - rspec: { script: "rspec", type: type, only: ["master", "deploy"] } - }) + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if only has a tags keyword" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["tags"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if only has current repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches@path"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if only has different repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, only: ["branches@fork"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns build only for specified type" do + + config = YAML.dump({ + before_script: ["pwd"], + build: { script: "build", type: "build", only: ["master", "deploy"] }, + rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, + staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }, + }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + end end - it "returns build only for specified type" do + describe :except do + it "returns builds if except has another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["deploy"] } + }) - config = YAML.dump({ - before_script: ["pwd"], - build: { script: "build", type: "build", only: ["master", "deploy"] }, - rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, - staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - }) + config_processor = GitlabCiYamlProcessor.new(config, path) - config_processor = GitlabCiYamlProcessor.new(config) + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end + + it "returns builds if except has regexp with another branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["/^deploy$/"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1) + end - expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + it "does not return builds if except has specified this branch" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", except: ["master"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0) + end + + it "does not return builds if except has a list of branches including specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["master", "deploy"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "does not return builds if except has a branches keyword specified" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if except has a tags keyword" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["tags"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "does not return builds if except has current repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches@path"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0) + end + + it "returns builds if except has different repository path" do + config = YAML.dump({ + before_script: ["pwd"], + rspec: { script: "rspec", type: type, except: ["branches@fork"] } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) + end + + it "returns build except specified type" do + config = YAML.dump({ + before_script: ["pwd"], + build: { script: "build", type: "build", except: ["master", "deploy"] }, + rspec: { script: "rspec", type: type, except: ["master", "deploy", "test@fork"] }, + staging: { script: "deploy", type: "deploy", except: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", except: ["master", "deploy"] }, + production: { script: "deploy", type: "deploy", except: ["master@path", "deploy"] }, + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref(type, "test").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(2) + end end + end describe "Image and service handling" do @@ -111,7 +256,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ @@ -139,7 +284,7 @@ module Ci rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ @@ -172,7 +317,7 @@ module Ci rspec: { script: "rspec" } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) expect(config_processor.variables).to eq(variables) end end @@ -184,7 +329,7 @@ module Ci rspec: { script: "rspec", when: when_state } }) - config_processor = GitlabCiYamlProcessor.new(config) + config_processor = GitlabCiYamlProcessor.new(config, path) builds = config_processor.builds_for_stage_and_ref("test", "master") expect(builds.size).to eq(1) expect(builds.first[:when]).to eq(when_state) @@ -200,154 +345,154 @@ module Ci it "returns errors if tags parameter is invalid" do config = YAML.dump({ rspec: { script: "test", tags: "mysql" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings") end it "returns errors if before_script parameter is invalid" do config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string") end it "returns errors if job name is blank" do config = YAML.dump({ '' => { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") end it "returns errors if job name is non-string" do config = YAML.dump({ 10 => { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string") end it "returns errors if job image parameter is invalid" do config = YAML.dump({ rspec: { script: "test", image: ["test"] } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string") end it "returns errors if services parameter is not an array" do config = YAML.dump({ services: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if services parameter is not an array of strings" do config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings") end it "returns errors if job services parameter is not an array" do config = YAML.dump({ rspec: { script: "test", services: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if job services parameter is not an array of strings" do config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings") end it "returns errors if there are unknown parameters" do config = YAML.dump({ extra: "bundle update" }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do config = YAML.dump({ extra: { services: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end it "returns errors if there is no any jobs defined" do config = YAML.dump({ before_script: ["bundle update"] }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job") end it "returns errors if job allow_failure parameter is not an boolean" do config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean") end it "returns errors if job stage is not a string" do config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a pre-defined stage" do config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") end it "returns errors if stages is not an array" do config = YAML.dump({ types: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if stages is not an array of strings" do config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings") end it "returns errors if variables is not a map" do config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end it "returns errors if variables is not a map of key-valued strings" do config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") end it "returns errors if job when is not on_success, on_failure or always" do config = YAML.dump({ rspec: { script: "test", when: 1 } }) expect do - GitlabCiYamlProcessor.new(config) + GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always") end end -- cgit v1.2.1 From 2dec5ec99042cd8da6c127d4bcfa7f5f84ef94eb Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Wed, 28 Oct 2015 17:39:22 +0100 Subject: Only redirect to homepage url when its not the root url It was possible to create an infi redirect when the user set up the `home_page_url` to redirect to the main URL of the gitlab instance. This fix makes sure this redirect is not possible. Fixes !1020 Signed-off-by: Jeroen van Baarsen --- app/controllers/application_controller.rb | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1b0609e279e..0d182e8eb04 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -59,13 +59,8 @@ class ApplicationController < ActionController::Base end def authenticate_user!(*args) - # If user is not signed-in and tries to access root_path - redirect him to landing page - # Don't redirect to the default URL to prevent endless redirections - if current_application_settings.home_page_url.present? && - current_application_settings.home_page_url.chomp('/') != Gitlab.config.gitlab['url'].chomp('/') - if current_user.nil? && root_path == request.path - redirect_to current_application_settings.home_page_url and return - end + if redirect_to_home_page_url? + redirect_to current_application_settings.home_page_url and return end super(*args) @@ -346,4 +341,17 @@ class ApplicationController < ActionController::Base def git_import_enabled? current_application_settings.import_sources.include?('git') end + + def redirect_to_home_page_url? + # If user is not signed-in and tries to access root_path - redirect him to landing page + # Don't redirect to the default URL to prevent endless redirections + return false unless current_application_settings.home_page_url.present? + + home_page_url = current_application_settings.home_page_url.chomp('/') + root_urls = [Gitlab.config.gitlab['url'].chomp('/'), root_url.chomp('/')] + + return false if root_urls.include?(home_page_url) + + current_user.nil? && root_path == request.path + end end -- cgit v1.2.1 From 1056c9d2d2481ee4fe3a55e0d9acb3691c1a019c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 2 Nov 2015 15:49:54 +0100 Subject: Spelling --- doc/ci/yaml/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 0c1d8cfdff6..d117a2969be 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -192,7 +192,7 @@ job: except: - master@gitlab-org/gitlab-ce ``` -The above will run `job` for all branches, except master on `gitlab-org/gitlab-ce` repository only. +The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. -- cgit v1.2.1 From e032a7288f4103a6d5980da6af4040bc9271bd99 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 2 Nov 2015 16:02:47 +0100 Subject: Spread out runner contacted_at updates --- lib/ci/api/helpers.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index e602cda81d6..7e4986b6af3 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -16,7 +16,9 @@ module Ci end def update_runner_last_contact - if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= UPDATE_RUNNER_EVERY + # Use a random threshold to prevent beating DB updates + contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY) + if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= contacted_at_max_age current_runner.update_attributes(contacted_at: Time.now) end end -- cgit v1.2.1 From 1b8d324762787805fabee72d585ac40e244af4d7 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 2 Nov 2015 14:14:38 +0200 Subject: Add ability to fetch the commit ID of the last commit that actually touched a file --- CHANGELOG | 1 + doc/api/repository_files.md | 3 ++- lib/api/files.rb | 4 +++- spec/requests/api/files_spec.rb | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6bec4f606e7..588909d2578 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - [API] Add ability to fetch the commit ID of the last commit that actually touched a file v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md index 25311b07107..623063f357b 100644 --- a/doc/api/repository_files.md +++ b/doc/api/repository_files.md @@ -23,7 +23,8 @@ Example response: "content": "IyA9PSBTY2hlbWEgSW5mb3...", "ref": "master", "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", - "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50" + "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", + "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d" } ``` diff --git a/lib/api/files.rb b/lib/api/files.rb index 308c84dd135..a7a768f8895 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -43,7 +43,8 @@ module API # "content": "IyA9PSBTY2hlbWEgSW5mb3...", # "ref": "master", # "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", - # "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50" + # "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", + # "last_commit_id": "570e7b2abdd848b95f2f578043fc23bd6f6fd24d", # } # get ":id/repository/files" do @@ -71,6 +72,7 @@ module API ref: ref, blob_id: blob.id, commit_id: commit.id, + last_commit_id: user_project.repository.last_commit_for_path(commit.sha, file_path).id } else not_found! 'File' diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb index 042e6352567..8efa09f75fd 100644 --- a/spec/requests/api/files_spec.rb +++ b/spec/requests/api/files_spec.rb @@ -19,6 +19,7 @@ describe API::API, api: true do expect(response.status).to eq(200) expect(json_response['file_path']).to eq(file_path) expect(json_response['file_name']).to eq('popen.rb') + expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") end -- cgit v1.2.1 From 810c91fe35db6a83c9c517e03d07dc1795922646 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 Nov 2015 16:39:24 +0100 Subject: Refactor search by commits message Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 4 +--- app/assets/stylesheets/pages/commits.scss | 2 ++ app/models/repository.rb | 6 +++--- app/views/search/_category.html.haml | 2 +- app/views/search/results/_commits.html.haml | 32 +---------------------------- lib/gitlab/project_search_results.rb | 2 +- spec/models/repository_spec.rb | 4 ++-- 7 files changed, 11 insertions(+), 41 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b573d40317e..78e0891e822 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Include commit logs in project search v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) @@ -29,9 +30,6 @@ v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - - Include commit logs in project search - -v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..e485487bcfd 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -33,6 +33,8 @@ } li.commit { + list-style: none; + .commit-row-title { font-size: $list-font-size; line-height: 20px; diff --git a/app/models/repository.rb b/app/models/repository.rb index a0f2b3fb765..c6d904339e4 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -87,12 +87,12 @@ class Repository commits end - def find_commits_with_matching_log(query) + def find_commits_by_message(query) # Limited to 1000 commits for now, could be parameterized? args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) - git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map{ |l| l.chomp } - commits = git_log_results.map{ |c| commit(c) } + git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) + commits = git_log_results.map { |c| commit(c) } commits end diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index ef43f727d8b..481451edb23 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -46,7 +46,7 @@ = link_to search_filter_path(scope: 'commits') do = icon('history fw') %span - Commit Logs + Commits %span.badge = @search_results.commits_count diff --git a/app/views/search/results/_commits.html.haml b/app/views/search/results/_commits.html.haml index 8076174e59d..7cff694350f 100644 --- a/app/views/search/results/_commits.html.haml +++ b/app/views/search/results/_commits.html.haml @@ -1,32 +1,2 @@ .search-result-row - .commits-row-title - %strong.str-truncated - = link_to commits.title, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id" - - .pull-right - = link_to commits.short_id, namespace_project_commit_path(@project.namespace, @project, commits.id), class: "commit_short_id" - - .notes_count - - if @note_counts - - note_count = @note_counts.fetch(commits.id, 0) - - else - - notes = commits.notes - - note_count = notes.user.count - - - if note_count > 0 - %span.light - %i.fa.fa-comments - = note_count - - - if commits.description? - .commits-row-description - %pre - = preserve(gfm(escape_once(commits.description))) - - .commits-row-info - = commit_author_link(commits, avatar: true, size: 24) - authored - .committed_ago - #{time_ago_with_tooltip(commits.committed_date)}   - = link_to_browse_code(@project, commits) - %br + = render 'projects/commits/commit', project: @project, commit: commits diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 54389f7d662..c873ef3771e 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,7 @@ module Gitlab end def commits - project.repository.find_commits_with_matching_log(query) + project.repository.find_commits_by_message(query) end def limit_project_ids diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index aedbfa04d88..319fa0a7c8d 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -26,8 +26,8 @@ describe Repository do it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') } end - describe :find_commits_with_matching_log do - subject { repository.find_commits_with_matching_log('submodule').map{ |k| k.id } } + describe :find_commits_by_message do + subject { repository.find_commits_by_message('submodule').map{ |k| k.id } } it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') } -- cgit v1.2.1 From 93672c502010787be9102b25a4f93722526968b9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 2 Nov 2015 14:03:42 -0200 Subject: Use `read` rather than `show` like the ability name --- app/controllers/snippets_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 8498efc89d0..08f2483af33 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -2,7 +2,7 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read snippet - before_action :authorize_show_snippet!, only: [:show] + before_action :authorize_read_snippet!, only: [:show] # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] @@ -86,7 +86,7 @@ class SnippetsController < ApplicationController end end - def authorize_show_snippet! + def authorize_read_snippet! authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet) end -- cgit v1.2.1 From 63cafdb8e5f9481539beaf2b6eac11cf8148daf1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 2 Nov 2015 17:04:28 +0100 Subject: Add changelog entry for contacted_at --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 6bec4f606e7..b67f010c6a0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,9 @@ v 8.2.0 (unreleased) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. +v 8.1.3 + - Spread out runner contacted_at updates + v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) - Add migration to remove satellites directory -- cgit v1.2.1 From 091979bd1b18f53c246415439d05db9aaf078828 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 2 Nov 2015 14:04:45 -0200 Subject: Fix ability name for public or internal personal snippets --- app/models/ability.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index ee2f7b5f58b..06c199bd8d7 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -325,7 +325,7 @@ class Ability end if snippet.public? || snippet.internal? - rules.push(:read_snippet) + rules.push(:read_personal_snippet) end rules -- cgit v1.2.1 From 77f8a1e392b64f51326df8aebdc77e97af07bfed Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 2 Nov 2015 17:27:38 +0100 Subject: Merge when build succeeds --- CHANGELOG | 1 + app/assets/stylesheets/pages/merge_requests.scss | 4 +- .../projects/merge_requests_controller.rb | 35 +++++++++++++++--- app/models/ci/commit.rb | 8 ++++ app/models/commit_status.rb | 33 +++++++++++++++++ app/models/merge_request.rb | 13 +++++++ .../merge_when_build_succeeds_service.rb | 43 ++++++++++++++++++++++ app/services/merge_requests/refresh_service.rb | 8 +++- app/services/system_note_service.rb | 14 +++++++ .../cancel_merge_when_build_succeeds.js.haml | 2 + app/views/projects/merge_requests/merge.js.haml | 6 ++- .../projects/merge_requests/widget/_open.html.haml | 2 + .../merge_requests/widget/open/_accept.html.haml | 18 ++++++--- .../open/_merge_when_build_succeeds.html.haml | 27 ++++++++++++++ config/routes.rb | 1 + ...d_merge_when_build_succeeds_to_merge_request.rb | 7 ++++ db/schema.rb | 17 ++++++--- 17 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 app/services/merge_requests/merge_when_build_succeeds_service.rb create mode 100644 app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml create mode 100644 app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml create mode 100644 db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb diff --git a/CHANGELOG b/CHANGELOG index 0d89fca9fc1..195e53abd8b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Merge when build succeeds (Zeger-Jan van de Weg) v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index f0b3667acca..b5a8c893678 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -19,18 +19,20 @@ .accept-merge-holder { .accept-action { display: inline-block; + float: left; } .accept-control { display: inline-block; + float: left; margin: 0; margin-left: 20px; padding: 5px; + padding-top: 12px; line-height: 20px; &.right { float: right; - padding-top: 12px; a { color: $gl-gray; } diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 16c42386623..2f9b8a25edf 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -2,7 +2,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :module_enabled before_action :merge_request, only: [ :edit, :update, :show, :diffs, :commits, :merge, :merge_check, - :ci_status, :toggle_subscription + :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds ] before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] before_action :validates_merge_request, only: [:show, :diffs, :commits] @@ -149,15 +149,34 @@ class Projects::MergeRequestsController < Projects::ApplicationController render partial: "projects/merge_requests/widget/show.html.haml", layout: false end + def cancel_merge_when_build_succeeds + return access_denied! unless @merge_request.can_be_merged_by?(current_user) + + if @merge_request.merge_when_build_succeeds? + @merge_request.reset_merge_when_build_succeeds + SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user) + end + end + def merge return access_denied! unless @merge_request.can_be_merged_by?(current_user) - if @merge_request.mergeable? - @merge_request.update(merge_error: nil) - MergeWorker.perform_async(@merge_request.id, current_user.id, params) - @status = true + unless @merge_request.mergeable? + @status = :failed + return + end + + @merge_request.update(merge_error: nil) + + if params[:merge_when_build_succeeds] && @merge_request.ci_commit.active? + MergeRequests::MergeWhenBuildSucceedsService.new(@project, + current_user, + merge_params: merge_params) + .execute(@merge_request) + @status = :merge_when_build_succeeds else - @status = false + MergeWorker.perform_async(@merge_request.id, current_user.id, params) + @status = :success end end @@ -282,6 +301,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ) end + def merge_params + params.permit(:should_remove_source_branch, :commit_message) + end + # Make sure merge requests created before 8.0 # have head file in refs/merge-requests/ def ensure_ref_fetched diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 13437b2483f..ebe4bace3b5 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -164,6 +164,14 @@ module Ci status == 'canceled' end + def active? + running? || pending? + end + + def complete? + canceled? || success? || failed? + end + def duration duration_array = latest_statuses.map(&:duration).compact duration_array.reduce(:+).to_i diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 0b73ab6d2eb..b1049fab788 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,3 +1,32 @@ +# == Schema Information +# +# project_id integer +# status string +# finished_at datetime +# trace text +# created_at datetime +# updated_at datetime +# started_at datetime +# runner_id integer +# coverage float +# commit_id integer +# commands text +# job_id integer +# name string +# deploy boolean default: false +# options text +# allow_failure boolean default: false, null: false +# stage string +# trigger_request_id integer +# stage_idx integer +# tag boolean +# ref string +# user_id integer +# type string +# target_url string +# description string +# + class CommitStatus < ActiveRecord::Base self.table_name = 'ci_builds' @@ -46,6 +75,10 @@ class CommitStatus < ActiveRecord::Base build.update_attributes finished_at: Time.now end + after_transition running: :success do |build, transition| + MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build) + end + state :pending, value: 'pending' state :running, value: 'running' state :failed, value: 'failed' diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 85f37e49e62..c5f04dbcf4c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -34,9 +34,12 @@ class MergeRequest < ActiveRecord::Base belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" + belongs_to :merge_user, class_name: "User" has_one :merge_request_diff, dependent: :destroy + serialize :merge_params, Hash + after_create :create_merge_request_diff after_update :update_merge_request_diff @@ -385,6 +388,16 @@ class MergeRequest < ActiveRecord::Base message end + def reset_merge_when_build_succeeds + return unless merge_when_build_succeeds? + + self.merge_when_build_succeeds = false + self.merge_user = nil + self.merge_params = nil + + self.save + end + # Return array of possible target branches # depends on target project of MR def target_branches diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb new file mode 100644 index 00000000000..a4418360b8c --- /dev/null +++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb @@ -0,0 +1,43 @@ +module MergeRequests + class MergeWhenBuildSucceedsService < MergeRequests::BaseService + def execute(merge_request) + merge_request.merge_params.merge!(params[:merge_params]) + + # The service is also called when the merge params are updated. + already_approved = merge_request.merge_when_build_succeeds? + + unless already_approved + merge_request.merge_when_build_succeeds = true + merge_request.merge_user = @current_user + end + + merge_request.save + + unless already_approved + SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user) + end + end + + def trigger(build) + merge_requests = merge_request_from(build) + + merge_requests.each do |merge_request| + next unless merge_request.merge_when_build_succeeds? + + ci_commit = merge_request.ci_commit + if ci_commit && ci_commit.success? && merge_request.mergeable? + MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params) + end + end + end + + private + + def merge_request_from(build) + merge_requests = @project.origin_merge_requests.opened.where(source_branch: build.ref).to_a + merge_requests += @project.fork_merge_requests.opened.where(source_branch: build.ref).to_a + + merge_requests.uniq.select(&:source_project) + end + end +end diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index d68bc79ecc0..335ef32abce 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -8,6 +8,7 @@ module MergeRequests find_new_commits reload_merge_requests + reset_merge_when_build_succeeds # Leave a system note if a branch was deleted/added if branch_added? || branch_removed? @@ -57,7 +58,6 @@ module MergeRequests merge_requests = filter_merge_requests(merge_requests) merge_requests.each do |merge_request| - if merge_request.source_branch == @branch_name || force_push? merge_request.reload_code merge_request.mark_as_unchecked @@ -76,6 +76,12 @@ module MergeRequests end end + def reset_merge_when_build_succeeds + merge_requests_for_source_branch.each do |merge_request| + merge_request.reset_merge_when_build_succeeds + end + end + def find_new_commits if branch_added? @commits = [] diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 708c2f00486..c9846e9f26f 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -130,6 +130,20 @@ class SystemNoteService create_note(noteable: noteable, project: project, author: author, note: body) end + # Called when 'merge when build succeeds' is executed + def self.merge_when_build_succeeds(noteable, project, author) + body = "Approved this request to be merged automatically when the build succeeds" + + create_note(noteable: noteable, project: project, author: author, note: body) + end + + # Called when 'merge when build succeeds' is canceled + def self.cancel_merge_when_build_succeeds(noteable, project, author) + body = "Canceled the automatic merge" + + create_note(noteable: noteable, project: project, author: author, note: body) + end + # Called when the title of a Noteable is changed # # noteable - Noteable object that responds to `title` diff --git a/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml new file mode 100644 index 00000000000..eab5be488b5 --- /dev/null +++ b/app/views/projects/merge_requests/cancel_merge_when_build_succeeds.js.haml @@ -0,0 +1,2 @@ +:plain + $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/accept'))}"); diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml index 33321651e32..89aae17a606 100644 --- a/app/views/projects/merge_requests/merge.js.haml +++ b/app/views/projects/merge_requests/merge.js.haml @@ -1,6 +1,10 @@ -- if @status +- case @status +- when :success :plain merge_request_widget.mergeInProgress(); +- when :merge_when_build_succeeds + :plain + $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}"); - else :plain $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}"); diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 0aad9bb3e88..e0013fb769a 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -13,6 +13,8 @@ = render 'projects/merge_requests/widget/open/conflicts' - elsif @merge_request.work_in_progress? = render 'projects/merge_requests/widget/open/wip' + - elsif @merge_request.merge_when_build_succeeds? + = render 'projects/merge_requests/widget/open/merge_when_build_succeeds' - elsif !@merge_request.can_be_merged_by?(current_user) = render 'projects/merge_requests/widget/open/not_allowed' - elsif @merge_request.can_be_merged? diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 613525437ab..2afc5f81251 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -2,8 +2,15 @@ = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container .accept-action - = f.button class: "btn btn-create accept_merge_request" do - Accept Merge Request + - ci_commit = @merge_request.ci_commit + - if ci_commit && ci_commit.active? + = f.button class: "btn btn-create btn-grouped merge_when_build_succeeds", name: "merge_when_build_succeeds" do + Merge when Build Succeeds + = f.button class: "btn btn-warning btn-grouped accept_merge_request" do + Accept Merge Request Now + - else + = f.button class: "btn btn-create btn-grouped accept_merge_request" do + Accept Merge Request - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork? .accept-control.checkbox = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do @@ -19,7 +26,8 @@ rows: 14, hint: true :coffeescript - $('.accept-mr-form').on 'ajax:before', -> - btn = $('.accept_merge_request') - btn.disable() + $('.accept_merge_request').on 'click', -> + btn = $(this) btn.html(" Merge in progress") + $('.accept-mr-form').on 'ajax:sen', -> + $(".accept-mr-form :input").disable() diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml new file mode 100644 index 00000000000..7e5385cf8b9 --- /dev/null +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -0,0 +1,27 @@ +%h4 + Approved by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} + to be merged automatically when + #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds +%div + - if @merge_request.merge_params["should_remove_source_branch"] + = succeed '.' do + The changes will be merged into + %span.label-branch= @merge_request.target_branch + The source branch will be removed. + - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) + - remove_source_branch_button = true + %p + = succeed '.' do + The changes will be merged into + %span.label-branch= @merge_request.target_branch + The source branch will not be removed. + +- if remove_source_branch_button || @merge_request.can_be_merged_by?(current_user) + .clearfix.prepend-top-10 + - if remove_source_branch_button + = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do + = icon('times') + Remove Source Branch When Merged + - if @merge_request.can_be_merged_by?(current_user) + = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request), remote: true, method: :delete, class: "btn btn-grouped btn-warning btn-sm" do + Cancel Automatic Merge diff --git a/config/routes.rb b/config/routes.rb index 0458f538eb6..917c3d3f1ed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -556,6 +556,7 @@ Gitlab::Application.routes.draw do get :diffs get :commits post :merge + delete :merge, action: :cancel_merge_when_build_succeeds get :merge_check get :ci_status post :toggle_subscription diff --git a/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb new file mode 100644 index 00000000000..ceb52f0c222 --- /dev/null +++ b/db/migrate/20151028152939_add_merge_when_build_succeeds_to_merge_request.rb @@ -0,0 +1,7 @@ +class AddMergeWhenBuildSucceedsToMergeRequest < ActiveRecord::Migration + def change + add_column :merge_requests, :merge_params, :text + add_column :merge_requests, :merge_when_build_succeeds, :boolean, default: false, null: false + add_column :merge_requests, :merge_user_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 4bde9f0b748..825d95565be 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: 20151026182941) do +ActiveRecord::Schema.define(version: 20151028152939) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -418,6 +418,7 @@ ActiveRecord::Schema.define(version: 20151026182941) do end add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree + add_index "labels", ["title"], name: "index_labels_on_title", using: :btree create_table "members", force: true do |t| t.integer "access_level", null: false @@ -453,9 +454,9 @@ ActiveRecord::Schema.define(version: 20151026182941) do add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: true do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" t.string "title" @@ -464,13 +465,16 @@ ActiveRecord::Schema.define(version: 20151026182941) do t.integer "milestone_id" t.string "state" t.string "merge_status" - t.integer "target_project_id", null: false + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" t.string "merge_error" + t.text "merge_params" + t.boolean "merge_when_build_succeeds", default: false, null: false + t.integer "merge_user_id" end add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree @@ -499,6 +503,7 @@ ActiveRecord::Schema.define(version: 20151026182941) do add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree + add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: true do |t| t.string "name", null: false -- cgit v1.2.1 From 63b234706d46f75c0c0f93bdfdc576e328981eb5 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 2 Nov 2015 20:02:51 +0100 Subject: MRs author can cancel automatic merge --- app/controllers/projects/merge_requests_controller.rb | 4 +++- .../merge_requests/widget/open/_merge_when_build_succeeds.html.haml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 2f9b8a25edf..d58dab2d666 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -150,7 +150,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def cancel_merge_when_build_succeeds - return access_denied! unless @merge_request.can_be_merged_by?(current_user) + unless @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user + return access_denied! + end if @merge_request.merge_when_build_succeeds? @merge_request.reset_merge_when_build_succeeds diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index 7e5385cf8b9..f3894334968 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -14,7 +14,7 @@ = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch - The source branch will not be removed. + The source branch won't be removed. - if remove_source_branch_button || @merge_request.can_be_merged_by?(current_user) .clearfix.prepend-top-10 @@ -22,6 +22,6 @@ = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do = icon('times') Remove Source Branch When Merged - - if @merge_request.can_be_merged_by?(current_user) + - if @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request), remote: true, method: :delete, class: "btn btn-grouped btn-warning btn-sm" do Cancel Automatic Merge -- cgit v1.2.1 From 60c5b52ee906639e8bc0408a4a63d50862580fc8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 2 Nov 2015 15:04:43 -0500 Subject: Explicitly require backup/files --- lib/backup/builds.rb | 2 ++ lib/backup/uploads.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/backup/builds.rb b/lib/backup/builds.rb index 800f30c2144..635967f4bd4 100644 --- a/lib/backup/builds.rb +++ b/lib/backup/builds.rb @@ -1,3 +1,5 @@ +require 'backup/files' + module Backup class Builds < Files def initialize diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb index 0a0ec564ba4..9261f77f3c9 100644 --- a/lib/backup/uploads.rb +++ b/lib/backup/uploads.rb @@ -1,3 +1,5 @@ +require 'backup/files' + module Backup class Uploads < Files -- cgit v1.2.1 From f61aa9acdc54306f22bbf4209bdae75b35d7df0c Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Tue, 3 Nov 2015 14:01:27 +0900 Subject: Update links in CI docs after GitLab 8.1 --- doc/ci/examples/README.md | 6 +++--- doc/ci/examples/test-and-deploy-python-application-to-heroku.md | 2 +- doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md | 4 ++-- doc/ci/examples/test-clojure-application.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md index c8122fc63b3..1cf41aea391 100644 --- a/doc/ci/examples/README.md +++ b/doc/ci/examples/README.md @@ -1,5 +1,5 @@ # Build script examples -+ [Test and deploy Ruby applications to Heroku](test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy Python applications to Heroku](test-and-deploy-python-application-to-heroku.md) -+ [Test Clojure applications](test-clojure-application.md) ++ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md) ++ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md) ++ [Test a Clojure application](test-clojure-application.md) diff --git a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md index 036b03dd6b9..a236da53fe9 100644 --- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md @@ -1,7 +1,7 @@ ## Test and Deploy a python application This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application. -You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://ci.gitlab.com/projects/4080). +You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://gitlab.com/ayufan/python-getting-started/builds?scope=all). ### Configure project This is what the `.gitlab-ci.yml` file looks like for this project: diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md index d2a872f1934..e52e1547461 100644 --- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md +++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md @@ -1,7 +1,7 @@ ## Test and Deploy a ruby application This example will guide you how to run tests in your Ruby application and deploy it automatiacally as Heroku application. -You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://ci.gitlab.com/projects/4050). +You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all). ### Configure project This is what the `.gitlab-ci.yml` file looks like for this project: @@ -64,4 +64,4 @@ gitlab-ci-multi-runner register \ With the command above, you create a runner that uses [ruby:2.1](https://registry.hub.docker.com/u/library/ruby/) image and uses [postgres](https://registry.hub.docker.com/u/library/postgres/) database. -To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. \ No newline at end of file +To access PostgreSQL database you need to connect to `host: postgres` as user `postgres` without password. diff --git a/doc/ci/examples/test-clojure-application.md b/doc/ci/examples/test-clojure-application.md index eaee94a10f1..56b746ce025 100644 --- a/doc/ci/examples/test-clojure-application.md +++ b/doc/ci/examples/test-clojure-application.md @@ -1,8 +1,8 @@ -## Test Clojure applications +## Test a Clojure application This example will guide you how to run tests in your Clojure application. -You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://ci.gitlab.com/projects/6306). +You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=all). ### Configure project -- cgit v1.2.1 From d0398514b8b8eda7d03ba89b250554ff60a29ed4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 Nov 2015 15:36:04 +0100 Subject: Add 'New file' link to dropdown on project page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/projects/buttons/_dropdown.html.haml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 588909d2578..c2bf3bf559b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.2.0 (unreleased) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. - [API] Add ability to fetch the commit ID of the last commit that actually touched a file + - Add "New file" link to dropdown on project page v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 4580c912692..bed2b16249e 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -20,6 +20,10 @@ New snippet - if can?(current_user, :push_code, @project) %li.divider + %li + = link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do + = icon('file fw') + New file %li = link_to new_namespace_project_branch_path(@project.namespace, @project) do = icon('code-fork fw') -- cgit v1.2.1 From 28f6fba97cb9753c43d109ef4f43439413d4eb69 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 10:16:09 +0100 Subject: Fix commits search for empty repository Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/project_search_results.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index c873ef3771e..70de6a74e76 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -77,7 +77,11 @@ module Gitlab end def commits - project.repository.find_commits_by_message(query) + if project.empty_repo? || query.blank? + [] + else + project.repository.find_commits_by_message(query).compact + end end def limit_project_ids -- cgit v1.2.1 From fe34b745e71a3e4ebb18d77a186d24de23b50863 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 3 Nov 2015 11:05:54 +0100 Subject: Fix tests --- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index a141a459ba3..9963f76f993 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -123,18 +123,16 @@ module Ci config = YAML.dump({ before_script: ["pwd"], - build: { script: "build", type: "build", only: ["master", "deploy"] }, - rspec: { script: "rspec", type: type, only: ["master", "deploy"] }, + rspec: { script: "rspec", type: "test", only: ["master", "deploy"] }, staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", only: ["master", "deploy"] }, production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] }, }) - config_processor = GitlabCiYamlProcessor.new(config, path) + config_processor = GitlabCiYamlProcessor.new(config, 'fork') - expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) - expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1) expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1) end end @@ -230,18 +228,16 @@ module Ci it "returns build except specified type" do config = YAML.dump({ before_script: ["pwd"], - build: { script: "build", type: "build", except: ["master", "deploy"] }, - rspec: { script: "rspec", type: type, except: ["master", "deploy", "test@fork"] }, - staging: { script: "deploy", type: "deploy", except: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", except: ["master", "deploy"] }, - production: { script: "deploy", type: "deploy", except: ["master@path", "deploy"] }, + rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] }, + staging: { script: "deploy", type: "deploy", except: ["master"] }, + production: { script: "deploy", type: "deploy", except: ["master@fork"] }, }) - config_processor = GitlabCiYamlProcessor.new(config, path) + config_processor = GitlabCiYamlProcessor.new(config, 'fork') - expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0) - expect(config_processor.builds_for_stage_and_ref(type, "test").size).to eq(1) - expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(2) + expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2) + expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0) + expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0) end end -- cgit v1.2.1 From 9479496f7584336788fc61121c096dfbcb3f6e4a Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 2 Nov 2015 16:56:44 +0200 Subject: Add added, modified and removed properties to commit object in webhook --- CHANGELOG | 1 + doc/web_hooks/web_hooks.md | 5 ++++- lib/gitlab/push_data_builder.rb | 33 +++++++++++++++++++++++++++++-- spec/lib/gitlab/push_data_builder_spec.rb | 6 ++++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 588909d2578..78f72ccc1e5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.2.0 (unreleased) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. - [API] Add ability to fetch the commit ID of the last commit that actually touched a file + - Add "added", "modified" and "removed" properties to commit object in webhook v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index ef99a69f60a..7d838187a26 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -69,7 +69,10 @@ X-Gitlab-Event: Push Hook } } ], - "total_commits_count": 4 + "total_commits_count": 4, + "added": ["CHANGELOG"], + "modified": ["app/controller/application.rb"], + "removed": [] } ``` diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index d010ade704e..fa068d50763 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -18,7 +18,10 @@ module Gitlab # homepage: String, # }, # commits: Array, - # total_commits_count: Fixnum + # total_commits_count: Fixnum, + # added: ["CHANGELOG"], + # modified: [], + # removed: ["tmp/file.txt"] # } # def build(project, user, oldrev, newrev, ref, commits = [], message = nil) @@ -33,6 +36,8 @@ module Gitlab commit_attrs = commits_limited.map(&:hook_attrs) type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" + + repo_changes = repo_changes(project, newrev, oldrev) # Hash to be passed as post_receive_data data = { object_kind: type, @@ -55,7 +60,10 @@ module Gitlab visibility_level: project.visibility_level }, commits: commit_attrs, - total_commits_count: commits_count + total_commits_count: commits_count, + added: repo_changes[:added], + modified: repo_changes[:modified], + removed: repo_changes[:removed] } data @@ -86,6 +94,27 @@ module Gitlab newrev end end + + def repo_changes(project, newrev, oldrev) + changes = { added: [], modified: [], removed: [] } + compare_result = CompareService.new. + execute(project, newrev, project, oldrev) + + if compare_result + compare_result.diffs.each do |diff| + case true + when diff.deleted_file + changes[:removed] << diff.old_path + when diff.renamed_file, diff.new_file + changes[:added] << diff.new_path + else + changes[:modified] << diff.new_path + end + end + end + + changes + end end end end diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index 1b8ba7b4d43..02710742625 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -17,6 +17,9 @@ describe 'Gitlab::PushDataBuilder' do it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) } it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) } it { expect(data[:total_commits_count]).to eq(3) } + it { expect(data[:added]).to eq(["gitlab-grack"]) } + it { expect(data[:modified]).to eq([".gitmodules", "files/ruby/popen.rb", "files/ruby/regex.rb"]) } + it { expect(data[:removed]).to eq([]) } end describe :build do @@ -35,5 +38,8 @@ describe 'Gitlab::PushDataBuilder' do it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } it { expect(data[:commits]).to be_empty } it { expect(data[:total_commits_count]).to be_zero } + it { expect(data[:added]).to eq([]) } + it { expect(data[:modified]).to eq([]) } + it { expect(data[:removed]).to eq([]) } end end -- cgit v1.2.1 From 8a22b5bf544bd258b5cacca78b9f241849418d16 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 2 Nov 2015 16:59:00 +0100 Subject: Remove inflector rule that makes commits uncountable Signed-off-by: Dmitriy Zaporozhets --- config/initializers/inflections.rb | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 5d46ece1e1b..9e8b0131f8f 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -8,24 +8,3 @@ # inflect.irregular 'person', 'people' # inflect.uncountable %w( fish sheep ) # end - -# Mark "commits" as uncountable. -# -# Without this change, the routes -# -# resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/} -# resources :commits, only: [:show], constraints: {id: /.+/} -# -# would generate identical route helper methods (`project_commit_path`), resulting -# in one of them not getting a helper method at all. -# -# After this change, the helper methods are: -# -# project_commit_path(@project, @project.commit) -# # => "/gitlabhq/commit/bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a -# -# project_commits_path(@project, 'stable/README.md') -# # => "/gitlabhq/commits/stable/README.md" -ActiveSupport::Inflector.inflections do |inflect| - inflect.uncountable %w(commits) -end -- cgit v1.2.1 From 0df65909eff560f2d313c4034ed21a65d643a0c3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 3 Nov 2015 11:47:23 +0100 Subject: Added benchmark for User.all This benchmark exists to test if ordering has any noticeable impact in the test environment. --- spec/benchmarks/models/user_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/benchmarks/models/user_spec.rb b/spec/benchmarks/models/user_spec.rb index 4cdba66939b..1be7a8d3ed9 100644 --- a/spec/benchmarks/models/user_spec.rb +++ b/spec/benchmarks/models/user_spec.rb @@ -1,6 +1,16 @@ require 'spec_helper' describe User, benchmark: true do + describe '.all' do + before do + 10.times { create(:user) } + end + + benchmark_subject { User.all.to_a } + + it { is_expected.to iterate_per_second(500) } + end + describe '.by_login' do before do %w{Alice Bob Eve}.each do |name| -- cgit v1.2.1 From 732f5380af5d688a152de9c5b4bb14ffb935dd2a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 3 Nov 2015 11:54:43 +0100 Subject: Only sort by IDs by default Sorting by both "created_at" and "id" in descending order is not needed as simply sorting by "id" in descending order will already sort rows from new to old. Depending on the query and data involved sorting twice can also introduce significant overhead. --- app/models/concerns/sortable.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 0ad2654867d..913c747a1c3 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -8,12 +8,12 @@ module Sortable included do # By default all models should be ordered # by created_at field starting from newest - default_scope { order(created_at: :desc, id: :desc) } + default_scope { order(id: :desc) } - scope :order_created_desc, -> { reorder(created_at: :desc, id: :desc) } - scope :order_created_asc, -> { reorder(created_at: :asc, id: :asc) } - scope :order_updated_desc, -> { reorder(updated_at: :desc, id: :desc) } - scope :order_updated_asc, -> { reorder(updated_at: :asc, id: :asc) } + scope :order_created_desc, -> { reorder(created_at: :desc) } + scope :order_created_asc, -> { reorder(created_at: :asc) } + scope :order_updated_desc, -> { reorder(updated_at: :desc) } + scope :order_updated_asc, -> { reorder(updated_at: :asc) } scope :order_name_asc, -> { reorder(name: :asc) } scope :order_name_desc, -> { reorder(name: :desc) } end -- cgit v1.2.1 From a2f8f9ad3da777869ba702ebf3c5b85b18991c28 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 3 Nov 2015 11:56:04 +0100 Subject: Fixed User sorting specs The descriptions were not accurate and one particular spec seemingly expected the wrong User row to be returned. --- spec/models/user_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index c71cfb3ebe3..49e0bfdd2ec 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -663,24 +663,24 @@ describe User do @user1 = create :user, created_at: Date.today - 1, last_sign_in_at: Date.today - 1, name: 'Omega' end - it "sorts users as recently_signed_in" do + it "sorts users by the recent sign-in time" do expect(User.sort('recent_sign_in').first).to eq(@user) end - it "sorts users as late_signed_in" do + it "sorts users by the oldest sign-in time" do expect(User.sort('oldest_sign_in').first).to eq(@user1) end - it "sorts users as recently_created" do + it "sorts users in descending order by their creation time" do expect(User.sort('created_desc').first).to eq(@user) end - it "sorts users as late_created" do + it "sorts users in ascending order by their creation time" do expect(User.sort('created_asc').first).to eq(@user1) end - it "sorts users by name when nil is passed" do - expect(User.sort(nil).first).to eq(@user) + it "sorts users by id in descending order when nil is passed" do + expect(User.sort(nil).first).to eq(@user1) end end -- cgit v1.2.1 From d23ffc832c465c873ad5a70117a57bf32f0b4735 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 12:00:27 +0100 Subject: Fix code that depends on incorrect inflector behavior Signed-off-by: Dmitriy Zaporozhets --- app/views/search/results/_commit.html.haml | 2 ++ app/views/search/results/_commits.html.haml | 2 -- spec/mailers/notify_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 app/views/search/results/_commit.html.haml delete mode 100644 app/views/search/results/_commits.html.haml diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml new file mode 100644 index 00000000000..4e6c3965dc6 --- /dev/null +++ b/app/views/search/results/_commit.html.haml @@ -0,0 +1,2 @@ +.search-result-row + = render 'projects/commits/commit', project: @project, commit: commit diff --git a/app/views/search/results/_commits.html.haml b/app/views/search/results/_commits.html.haml deleted file mode 100644 index 7cff694350f..00000000000 --- a/app/views/search/results/_commits.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -.search-result-row - = render 'projects/commits/commit', project: @project, commit: commits diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index cb67ec95d57..47863d54579 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -468,7 +468,7 @@ describe Notify do subject { Notify.note_commit_email(recipient.id, note.id) } it_behaves_like 'a note email' - it_behaves_like 'an answer to an existing thread', 'commits' + it_behaves_like 'an answer to an existing thread', 'commit' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ -- cgit v1.2.1 From 1b14bc59570a625365fef232f8c57919f76b3e2a Mon Sep 17 00:00:00 2001 From: James Lopez Date: Tue, 3 Nov 2015 11:11:56 +0000 Subject: refactored permissions and added update_project_member ability logic. Also refactored owner methods to a concern. --- app/models/ability.rb | 18 ++++++++++++++++++ app/models/concerns/has_owners.rb | 31 +++++++++++++++++++++++++++++++ app/models/group.rb | 22 ++-------------------- app/models/member.rb | 8 ++++---- app/models/project.rb | 2 ++ 5 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 app/models/concerns/has_owners.rb diff --git a/app/models/ability.rb b/app/models/ability.rb index b72178fa126..5beead0b75d 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -15,6 +15,7 @@ class Ability when "Group" then group_abilities(user, subject) when "Namespace" then namespace_abilities(user, subject) when "GroupMember" then group_member_abilities(user, subject) + when "ProjectMember" then project_member_abilities(user, subject) else [] end.concat(global_abilities(user)) end @@ -316,6 +317,23 @@ class Ability rules end + def project_member_abilities(user, subject) + rules = [] + target_user = subject.user + project = subject.project + can_manage = project_abilities(user, project).include?(:admin_project_member) + + if can_manage && (user != target_user) + rules << :update_project_member + rules << :destroy_project_member + end + + if !project.last_owner?(user) && (can_manage || (user == target_user)) + rules << :destroy_project_member + end + rules + end + def abilities @abilities ||= begin abilities = Six.new diff --git a/app/models/concerns/has_owners.rb b/app/models/concerns/has_owners.rb new file mode 100644 index 00000000000..735b2071721 --- /dev/null +++ b/app/models/concerns/has_owners.rb @@ -0,0 +1,31 @@ +# == Owners concern +# +# Contains owners functionality for groups +# +module HasOwners + extend ActiveSupport::Concern + + def owners + @owners ||= my_members.owners.includes(:user).map(&:user) + end + + def my_members + raise NotImplementedError, "Expected my_members to be defined in #{self.class.name}" + end + + def add_owner(user, current_user = nil) + add_user(user, Gitlab::Access::OWNER, current_user) + end + + def has_owner?(user) + owners.include?(user) + end + + def has_master?(user) + members.masters.where(user_id: user).any? + end + + def last_owner?(user) + has_owner?(user) && owners.size == 1 + end +end diff --git a/app/models/group.rb b/app/models/group.rb index 465c22d23ac..c9806f6fd6f 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -19,8 +19,10 @@ require 'file_size_validator' class Group < Namespace include Gitlab::ConfigHelper include Referable + include HasOwners has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' + alias_method :my_members, :group_members has_many :users, through: :group_members validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } @@ -63,10 +65,6 @@ class Group < Namespace end end - def owners - @owners ||= group_members.owners.includes(:user).map(&:user) - end - def add_users(user_ids, access_level, current_user = nil) user_ids.each do |user_id| Member.add_user(self.group_members, user_id, access_level, current_user) @@ -93,22 +91,6 @@ class Group < Namespace add_user(user, Gitlab::Access::MASTER, current_user) end - def add_owner(user, current_user = nil) - add_user(user, Gitlab::Access::OWNER, current_user) - end - - def has_owner?(user) - owners.include?(user) - end - - def has_master?(user) - members.masters.where(user_id: user).any? - end - - def last_owner?(user) - has_owner?(user) && owners.size == 1 - end - def members group_members end diff --git a/app/models/member.rb b/app/models/member.rb index 4651c8fff37..c565ee6bbce 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -82,8 +82,7 @@ class Member < ActiveRecord::Base member.invite_email = user end - project = members.first.respond_to?(:project)? members.first.project : nil - if can_update_member?(current_user, member, project) + if can_update_member?(current_user, member) member.created_by ||= current_user member.access_level = access_level @@ -93,9 +92,10 @@ class Member < ActiveRecord::Base private - def can_update_member?(current_user, member, project) + def can_update_member?(current_user, member) !current_user || current_user.can?(:update_group_member, member) || - (project && current_user.can?(:admin_project_member, project)) + (member.respond_to?(:project) && + current_user.can?(:update_project_member, member)) end end diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..79b7a6457d7 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -41,6 +41,7 @@ class Project < ActiveRecord::Base include Sortable include AfterCommitQueue include CaseSensitivity + include HasOwners extend Gitlab::ConfigHelper extend Enumerize @@ -114,6 +115,7 @@ class Project < ActiveRecord::Base has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' has_many :protected_branches, dependent: :destroy has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember' + alias_method :my_members, :project_members has_many :users, through: :project_members has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys, through: :deploy_keys_projects -- cgit v1.2.1 From 8d2758e02d634fd8518893f39dcc3359284e890f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 21 Oct 2015 11:29:47 +0200 Subject: Cleanup stuck CI builds daily --- CHANGELOG | 1 + app/workers/stuck_ci_builds_worker.rb | 18 ++++++++++++ spec/workers/stuck_ci_builds_worker_spec.rb | 44 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 app/workers/stuck_ci_builds_worker.rb create mode 100644 spec/workers/stuck_ci_builds_worker_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 08c85072633..e4f235fc3de 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ v 8.1.0 - Fix duplicate repositories in GitHub import page (Stan Hu) - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - Adds ability to create directories using the web editor (Ben Ford) + - Cleanup stuck CI builds v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb new file mode 100644 index 00000000000..ad02a3b16d9 --- /dev/null +++ b/app/workers/stuck_ci_builds_worker.rb @@ -0,0 +1,18 @@ +class StuckCiBuildsWorker + include Sidekiq::Worker + include Sidetiq::Schedulable + + BUILD_STUCK_TIMEOUT = 1.day + + recurrence { daily } + + def perform + Rails.logger.info 'Cleaning stuck builds' + + builds = Ci::Build.running_or_pending.where('updated_at < ?', BUILD_STUCK_TIMEOUT.ago) + builds.find_each(batch_size: 50).each do |build| + Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}" + build.drop + end + end +end diff --git a/spec/workers/stuck_ci_builds_worker_spec.rb b/spec/workers/stuck_ci_builds_worker_spec.rb new file mode 100644 index 00000000000..f9d87d97014 --- /dev/null +++ b/spec/workers/stuck_ci_builds_worker_spec.rb @@ -0,0 +1,44 @@ +require "spec_helper" + +describe StuckCiBuildsWorker do + let!(:build) { create :ci_build } + + subject do + build.reload + build.status + end + + %w(pending running).each do |status| + context "#{status} build" do + before do + build.update!(status: status) + end + + it 'gets dropped if it was updated over 2 days ago' do + build.update!(updated_at: 2.day.ago) + StuckCiBuildsWorker.new.perform + is_expected.to eq('failed') + end + + it "is still #{status}" do + build.update!(updated_at: 1.minute.ago) + StuckCiBuildsWorker.new.perform + is_expected.to eq(status) + end + end + end + + %w(success failed canceled).each do |status| + context "#{status} build" do + before do + build.update!(status: status) + end + + it "is still #{status}" do + build.update!(updated_at: 2.day.ago) + StuckCiBuildsWorker.new.perform + is_expected.to eq(status) + end + end + end +end -- cgit v1.2.1 From e3aed9121948c8124ddf128af3841986d69dafd5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 13:22:11 +0100 Subject: Better name for up-level links Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/layouts/nav/_group.html.haml | 4 ++-- app/views/layouts/nav/_group_settings.html.haml | 4 ++-- app/views/layouts/nav/_profile.html.haml | 4 ++-- app/views/layouts/nav/_project.html.haml | 8 ++++---- app/views/layouts/nav/_project_settings.html.haml | 4 ++-- features/steps/groups.rb | 2 +- features/steps/project/project.rb | 4 ++-- features/steps/shared/project_tab.rb | 2 +- 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 08c85072633..f178db46263 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ v 8.2.0 (unreleased) - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Add "New file" link to dropdown on project page - Include commit logs in project search + - Rename "Back to" links to "Go to" because its not always a case it point to place user come from v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index eb35af22b93..319352876b4 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index 8075fe32fbc..c8411521f36 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to group_path(@group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to group + Go to group %li.separate-item diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 5a47b8e6db2..0f3a793e30b 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 53a913fe8f3..20db2866d1f 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,16 +1,16 @@ %ul.nav.nav-sidebar - if @project.group = nav_link do - = link_to group_path(@project.group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to group + Go to group - else = nav_link do - = link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to dashboard + Go to dashboard %li.separate-item diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 356ce09c3d7..a59939ccd31 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -1,9 +1,9 @@ %ul.nav.nav-sidebar = nav_link do - = link_to project_path(@project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do + = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do = icon('caret-square-o-left fw') %span - Back to project + Go to project %li.separate-item diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 69ddfa42c06..70388c18fcf 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -6,7 +6,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include Select2Helper step 'I should see back to dashboard button' do - expect(page).to have_content 'Back to dashboard' + expect(page).to have_content 'Go to dashboard' end step 'gitlab user "Mike"' do diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index d76891d5bde..9ca7c8ebbc7 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -124,11 +124,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps end step 'I should see back to dashboard button' do - expect(page).to have_content 'Back to dashboard' + expect(page).to have_content 'Go to dashboard' end step 'I should see back to group button' do - expect(page).to have_content 'Back to group' + expect(page).to have_content 'Go to group' end step 'I click notifications drop down button' do diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb index c67e5e4a06a..33ff7084e30 100644 --- a/features/steps/shared/project_tab.rb +++ b/features/steps/shared/project_tab.rb @@ -46,7 +46,7 @@ module SharedProjectTab step 'the active main tab should be Settings' do page.within '.nav-sidebar' do - expect(page).to have_content('Back to project') + expect(page).to have_content('Go to project') end end -- cgit v1.2.1 From ab129f7b235527d28068a3fd6ca1fc6eed5ebac5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 14:00:41 +0100 Subject: Improve profile page UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/profile.scss | 22 ++++++++++++++++++++++ app/views/users/show.html.haml | 27 ++++++++++++++++----------- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index b7391e5303b..1d6ca0dfc13 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -53,3 +53,25 @@ float: right; font-size: 12px; } + +.profile-link-holder { + display: inline; + + &:after { + content: "\00B7"; + padding: 0px 6px; + font-weight: bold; + } + + &:last-child { + &:after { + content: ""; + padding: 0; + } + } + + a { + color: $blue-dark; + text-decoration: none; + } +} diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 4ea4a1f92c2..3766d8cd673 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -24,22 +24,27 @@ .cover-desc - unless @user.public_email.blank? - = link_to @user.public_email, "mailto:#{@user.public_email}" + .profile-link-holder + = link_to @user.public_email, "mailto:#{@user.public_email}" - unless @user.skype.blank? - · - = link_to "Skype", "skype:#{@user.skype}" + .profile-link-holder + = link_to "skype:#{@user.skype}", title: "Skype" do + = icon('skype') - unless @user.linkedin.blank? - · - = link_to "LinkedIn", "http://www.linkedin.com/in/#{@user.linkedin}" + .profile-link-holder + = link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do + = icon('linkedin-square') - unless @user.twitter.blank? - · - = link_to "Twitter", "http://www.twitter.com/#{@user.twitter}" + .profile-link-holder + = link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do + = icon('twitter-square') - unless @user.website_url.blank? - · - = link_to @user.short_website_url, @user.full_website_url + .profile-link-holder + = link_to @user.short_website_url, @user.full_website_url - unless @user.location.blank? - · - = @user.location + .profile-link-holder + = icon('map-marker') + = @user.location .cover-controls -- cgit v1.2.1 From 4d7f00fdeae074ae26f2629c78064d6eaa21ace1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 15:32:40 +0100 Subject: Apply new design for user profile page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/assets/javascripts/calendar.js.coffee | 2 +- app/assets/stylesheets/framework/common.scss | 10 +++++ app/assets/stylesheets/pages/profile.scss | 4 ++ app/views/users/_projects.html.haml | 13 ------ app/views/users/show.html.haml | 62 +++++++++++++++++++--------- 6 files changed, 59 insertions(+), 33 deletions(-) delete mode 100644 app/views/users/_projects.html.haml diff --git a/CHANGELOG b/CHANGELOG index ae3613cc330..4c43a377fd9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ v 8.2.0 (unreleased) - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Add "New file" link to dropdown on project page - Include commit logs in project search + - New design for user profile page v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 4c4bc3d66ed..2b1e20d3225 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -25,7 +25,7 @@ class @Calendar 30 ] legendCellPadding: 3 - cellSize: $('.user-calendar').width() / 80 + cellSize: $('.user-calendar').width() / 76 onClick: (date, count) -> formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() $.ajax diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index e1a1793be9c..3d0b71e066e 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -387,6 +387,16 @@ table { } } +.center-middle-menu { + @include nav-menu; + text-align: center; + margin: -$gl-padding; + height: auto; + margin-top: 0; + margin-bottom: 0; + border-bottom: 1px solid $border-color; +} + .dropzone .dz-preview .dz-progress { border-color: $border-color !important; } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 1d6ca0dfc13..bc1ad21305a 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -75,3 +75,7 @@ text-decoration: none; } } + +.cal-heatmap-container { + margin: 0 auto; +} diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml deleted file mode 100644 index a126a858ea8..00000000000 --- a/app/views/users/_projects.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present? - .panel.panel-default.contributed-projects - .panel-heading Projects contributed to - = render 'shared/projects/list', - projects: contributed_projects.sort_by(&:star_count).reverse, - projects_limit: 5, stars: true, avatar: false - -- if local_assigns.has_key?(:projects) && projects.present? - .panel.panel-default - .panel-heading Personal projects - = render 'shared/projects/list', - projects: projects.sort_by(&:star_count).reverse, - projects_limit: 10, stars: true, avatar: false diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 3766d8cd673..e22d93aae84 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -52,7 +52,7 @@ = link_to profile_path, class: 'btn btn-gray' do = icon('pencil') - elsif current_user - .report-abuse + %span.report-abuse - if @user.abuse_report %button.btn.btn-danger{ title: 'Already reported for abuse', data: { toggle: 'tooltip', placement: 'left', container: 'body' }} @@ -61,6 +61,10 @@ = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray', title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do = icon('exclamation-circle') + - if current_user +   + = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do + = icon('rss') .gray-content-block.second-block .user-calendar @@ -69,27 +73,47 @@ .user-calendar-activities -.row.prepend-top-20 - %section.col-md-7 - - if @groups.any? - .prepend-top-20 - %h4 Groups - = render 'groups', groups: @groups - %hr - - %h4 - User Activity - - - if current_user - %span.rss-icon.pull-right - = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do - %strong - %i.fa.fa-rss +%ul.nav.center-middle-menu + %li.active + = link_to "#activity", 'data-toggle' => 'tab' do + Activity + - if @groups.any? + %li + = link_to "#groups", 'data-toggle' => 'tab' do + Groups + - if @contributed_projects.present? + %li + = link_to "#contributed", 'data-toggle' => 'tab' do + Contributed projects + - if @projects.present? + %li + = link_to "#personal", 'data-toggle' => 'tab' do + Personal projects +.tab-content + .tab-pane.active#activity .content_list = spinner - %aside.col-md-5 - = render 'projects', projects: @projects, contributed_projects: @contributed_projects + + - if @groups.any? + .tab-pane#groups + %ul.content-list + - @groups.each do |group| + = render 'shared/groups/group', group: group + + - if @contributed_projects.present? + .tab-pane#contributed + .contributed-projects + = render 'shared/projects/list', + projects: @contributed_projects.sort_by(&:star_count).reverse, + projects_limit: 5, stars: true, avatar: false + + - if @projects.present? + .tab-pane#personal + .personal-projects + = render 'shared/projects/list', + projects: @projects.sort_by(&:star_count).reverse, + projects_limit: 10, stars: true, avatar: false :coffeescript $(".user-calendar").load("#{user_calendar_path}") -- cgit v1.2.1 From 00ac792cfba52dffbbfd1bf304059fcd63d7e688 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 15:44:52 +0100 Subject: Fix clipboard button overflow Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/buttons.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 04024419584..fe56266284b 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -180,3 +180,7 @@ } } } + +.btn-clipboard { + border: none; +} -- cgit v1.2.1 From d2846293d067cdf38d2816768e059d901a960a22 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 3 Nov 2015 16:37:39 +0100 Subject: Move visibility_level check for forked projects to Project model --- app/helpers/visibility_level_helper.rb | 3 +-- app/models/project.rb | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index b52cd23aba2..bc594dc53c1 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -89,7 +89,6 @@ module VisibilityLevelHelper def skip_level?(form_model, level) form_model.is_a?(Project) && - form_model.forked? && - !Gitlab::VisibilityLevel.allowed_fork_levels(form_model.forked_from_project.visibility_level).include?(level) + !form_model.visibility_level_allowed?(level) end end diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..a4c634bdb5c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -783,4 +783,9 @@ class Project < ActiveRecord::Base service.active = true service.save end + + def visibility_level_allowed?(level) + return true unless forked? + Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) + end end -- cgit v1.2.1 From e0edc01d1477d463d2ecf8137c966859d32c1302 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 16:39:09 +0100 Subject: Fix tests Signed-off-by: Dmitriy Zaporozhets --- features/profile/profile.feature | 1 + features/steps/profile/profile.rb | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/features/profile/profile.feature b/features/profile/profile.feature index 27c0bde364e..168d9d30b50 100644 --- a/features/profile/profile.feature +++ b/features/profile/profile.feature @@ -7,6 +7,7 @@ Feature: Profile Given I visit profile page Then I should see my profile info + @javascript Scenario: I can see groups I belong to Given I have group with projects When I visit profile page diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 8cf24705a5e..40b2aa7c357 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -59,7 +59,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I should not see the "Remove avatar" button' do expect(page).not_to have_link("Remove avatar") end - + step 'I should see the gravatar host link' do expect(page).to have_link("gravatar.com") end @@ -159,10 +159,9 @@ class Spinach::Features::Profile < Spinach::FeatureSteps end step 'I should see my user page' do - expect(page).to have_content "User Activity" - - page.within '.navbar-gitlab' do + page.within ".cover-block" do expect(page).to have_content current_user.name + expect(page).to have_content current_user.username end end @@ -176,7 +175,13 @@ class Spinach::Features::Profile < Spinach::FeatureSteps end step 'I should see groups I belong to' do - expect(page).to have_css('.profile-groups-avatars', visible: true) + page.within ".content" do + click_link "Groups" + end + + page.within "#groups" do + expect(page).to have_content @group.name + end end step 'I click on new application button' do -- cgit v1.2.1 From ef6f6f80ec6242790d54a57445fd7e4915b36ed1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 3 Nov 2015 16:42:02 +0100 Subject: Add extra padding between user description and links on profile page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/blocks.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 5949a0fd5ad..8917c53b1f5 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -100,7 +100,7 @@ } .cover-desc { - padding: 0 $gl-padding; + padding: 0 $gl-padding 3px; color: $gl-text-color; } -- cgit v1.2.1 From 9e1db139eb1387fa9658ed68592d93eca61efb6b Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 3 Nov 2015 17:23:19 +0100 Subject: Move level_name resolving to Gitlan::VisibilityLevel --- app/services/base_service.rb | 5 +---- lib/gitlab/visibility_level.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/services/base_service.rb b/app/services/base_service.rb index f00ec7408b6..b48ca67d4d2 100644 --- a/app/services/base_service.rb +++ b/app/services/base_service.rb @@ -39,10 +39,7 @@ class BaseService def deny_visibility_level(model, denied_visibility_level = nil) denied_visibility_level ||= model.visibility_level - level_name = 'Unknown' - Gitlab::VisibilityLevel.options.each do |name, level| - level_name = name if level == denied_visibility_level - end + level_name = Gitlab::VisibilityLevel.level_name(denied_visibility_level) model.errors.add( :visibility_level, diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index 335dc44be19..d0ffe24f827 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -51,6 +51,15 @@ module Gitlab def allowed_fork_levels(origin_level) [PRIVATE, INTERNAL, PUBLIC].select{ |level| level <= origin_level } end + + def level_name(level) + level_name = 'Unknown' + options.each do |name, lvl| + level_name = name if lvl == level + end + + level_name + end end def private? -- cgit v1.2.1 From 700909d56726e23c500a2b55e2e68d14cbe3a161 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 2 Nov 2015 15:33:56 -0500 Subject: Bump stamp to ~> 0.6.0 Closes #2801 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index bb31c147b26..df260633091 100644 --- a/Gemfile +++ b/Gemfile @@ -63,7 +63,7 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' # Format dates and times # based on human-friendly examples -gem "stamp", '~> 0.5.0' +gem "stamp", '~> 0.6.0' # Enumeration fields gem 'enumerize', '~> 0.7.0' diff --git a/Gemfile.lock b/Gemfile.lock index 65abc45ff19..030191fb80c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -689,7 +689,7 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - stamp (0.5.0) + stamp (0.6.0) state_machine (1.2.0) stringex (2.5.2) systemu (2.6.5) @@ -909,7 +909,7 @@ DEPENDENCIES spring-commands-spinach (~> 1.0.0) spring-commands-teaspoon (~> 0.0.2) sprockets (~> 2.12.3) - stamp (~> 0.5.0) + stamp (~> 0.6.0) state_machine (~> 1.2.0) task_list (~> 1.0.2) teaspoon (~> 1.0.0) -- cgit v1.2.1 From 4773d98e835dd14bd73e7bde0d5bcf4754355976 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 3 Nov 2015 17:58:12 +0100 Subject: Add Facebook authentication --- CHANGELOG | 1 + Gemfile | 1 + Gemfile.lock | 3 + app/assets/images/auth_buttons/facebook_64.png | Bin 0 -> 2970 bytes app/helpers/auth_helper.rb | 2 +- config/gitlab.yml.example | 23 +++--- doc/integration/facebook.md | 96 +++++++++++++++++++++++++ doc/integration/facebook_api_keys.png | Bin 0 -> 125921 bytes doc/integration/facebook_app_settings.png | Bin 0 -> 134387 bytes doc/integration/facebook_website_url.png | Bin 0 -> 42292 bytes doc/integration/omniauth.md | 3 +- 11 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 app/assets/images/auth_buttons/facebook_64.png create mode 100644 doc/integration/facebook.md create mode 100644 doc/integration/facebook_api_keys.png create mode 100644 doc/integration/facebook_app_settings.png create mode 100644 doc/integration/facebook_website_url.png diff --git a/CHANGELOG b/CHANGELOG index 16055208db5..0ec6030b130 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.2.0 (unreleased) v 8.1.3 - Spread out runner contacted_at updates - New design for user profile page + - Add Facebook authentication v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/Gemfile b/Gemfile index bb31c147b26..8379e5eea17 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,7 @@ gem 'devise-async', '~> 0.9.0' gem 'doorkeeper', '~> 2.1.3' gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' +gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-google-oauth2', '~> 0.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 65abc45ff19..c9ba4959ddf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -423,6 +423,8 @@ GEM multi_json (~> 1.7) omniauth (~> 1.1) omniauth-oauth (~> 1.0) + omniauth-facebook (3.0.0) + omniauth-oauth2 (~> 1.2) omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) @@ -859,6 +861,7 @@ DEPENDENCIES octokit (~> 3.7.0) omniauth (~> 1.2.2) omniauth-bitbucket (~> 0.0.2) + omniauth-facebook (~> 3.0.0) omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.0) omniauth-google-oauth2 (~> 0.2.0) diff --git a/app/assets/images/auth_buttons/facebook_64.png b/app/assets/images/auth_buttons/facebook_64.png new file mode 100644 index 00000000000..1f1a80d7368 Binary files /dev/null and b/app/assets/images/auth_buttons/facebook_64.png differ diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index cd99a232403..2c81ea1623c 100644 --- a/app/helpers/auth_helper.rb +++ b/app/helpers/auth_helper.rb @@ -1,5 +1,5 @@ module AuthHelper - PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze + PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook).freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze def ldap_enabled? diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index e297f393e3d..20894ebcdc9 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -274,27 +274,28 @@ production: &base # arguments, followed by optional 'args' which can be either a hash or an array. # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html providers: - # - { name: 'google_oauth2', - # label: 'Google', - # app_id: 'YOUR_APP_ID', - # app_secret: 'YOUR_APP_SECRET', - # args: { access_type: 'offline', approval_prompt: '' } } - # - { name: 'twitter', - # app_id: 'YOUR_APP_ID', - # app_secret: 'YOUR_APP_SECRET' } # - { name: 'github', - # label: 'GitHub', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'user:email' } } + # - { name: 'bitbucket', + # app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET' } # - { name: 'gitlab', - # label: 'GitLab.com', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', # args: { scope: 'api' } } - # - { name: 'bitbucket', + # - { name: 'google_oauth2', + # app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET', + # args: { access_type: 'offline', approval_prompt: '' } } + # - { name: 'facebook', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' } + # - { name: 'twitter', + # app_id: 'YOUR_APP_ID', + # app_secret: 'YOUR_APP_SECRET' } + # # - { name: 'saml', # label: 'Our SAML Provider', # args: { diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md new file mode 100644 index 00000000000..5880b5baf76 --- /dev/null +++ b/doc/integration/facebook.md @@ -0,0 +1,96 @@ +# Facebook OAuth2 OmniAuth Provider + +To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use. + +1. Sign in to the [Facebook Developer Platform](https://developers.facebook.com/). + +1. Choose "My Apps" > "Add a New App" + +1. Select the type "Website" + +1. Enter a name for your app. This can be anything. Consider something like "<Organization>'s GitLab" or "<Your Name>'s GitLab" or +something else descriptive. + +1. Choose "Create New Facebook App ID" + +1. Select a Category, for example "Productivity" + +1. Choose "Create App ID" + +1. Enter the address of your GitLab installation at the bottom of the package + + ![Facebook Website URL](facebook_website_url.png) + +1. Choose "Next" + +1. Choose "Skip Quick Start" in the upper right corner + +1. Choose "Settings" in the menu on the left + +1. Fill in a contact email for your app + ![Facebook App Settings](facebook_app_settings.png) + +1. Choose "Save Changes" + +1. Choose "Status & Review" in the menu on the left + +1. Change the switch on the right from No to Yes + +1. Choose "Confirm" when prompted to make the app public + +1. Choose "Dashboard" in the menu on the left + +1. Choose "Show" next to the hidden "App Secret" + +1. You should now see an app key and app secret (see screenshot). Keep this page open as you continue configuration. + + ![Facebook API Keys](facebook_api_keys.png) + +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For installations from source: + + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + "name" => "facebook", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET" + } + ] + ``` + + For installations from source: + + ``` + - { name: 'facebook', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET' } + ``` + +1. Change 'YOUR_APP_ID' to the API key from Facebook page in step 10. + +1. Change 'YOUR_APP_SECRET' to the API secret from the Facebook page in step 10. + +1. Save the configuration file. + +1. Restart GitLab for the changes to take effect. + +On the sign in page there should now be a Facebook icon below the regular sign in form. Click the icon to begin the authentication process. Facebook will ask the user to sign in and authorize the GitLab application. If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/integration/facebook_api_keys.png b/doc/integration/facebook_api_keys.png new file mode 100644 index 00000000000..d6c44ac0f11 Binary files /dev/null and b/doc/integration/facebook_api_keys.png differ diff --git a/doc/integration/facebook_app_settings.png b/doc/integration/facebook_app_settings.png new file mode 100644 index 00000000000..30dd21e198a Binary files /dev/null and b/doc/integration/facebook_app_settings.png differ diff --git a/doc/integration/facebook_website_url.png b/doc/integration/facebook_website_url.png new file mode 100644 index 00000000000..dc3088bb2fa Binary files /dev/null and b/doc/integration/facebook_website_url.png differ diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index c5cecbc2f2d..bd9550c6ddb 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -73,8 +73,9 @@ Now we can choose one or more of the Supported Providers below to continue confi - [Bitbucket](bitbucket.md) - [GitLab.com](gitlab.md) - [Google](google.md) -- [Shibboleth](shibboleth.md) +- [Facebook](facebook.md) - [Twitter](twitter.md) +- [Shibboleth](shibboleth.md) - [SAML](saml.md) - [Crowd](crowd.md) -- cgit v1.2.1 From 98dcad2762930b0dc967ff8e67f9f795228d2342 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 3 Nov 2015 17:58:26 +0100 Subject: Use proper labels for OAuth providers --- lib/gitlab/o_auth/provider.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/o_auth/provider.rb b/lib/gitlab/o_auth/provider.rb index 90c3fe8da33..9ad7a38d505 100644 --- a/lib/gitlab/o_auth/provider.rb +++ b/lib/gitlab/o_auth/provider.rb @@ -1,6 +1,12 @@ module Gitlab module OAuth class Provider + LABELS = { + "github" => "GitHub", + "gitlab" => "GitLab.com", + "google_oauth2" => "Google" + }.freeze + def self.providers Devise.omniauth_providers end @@ -23,8 +29,9 @@ module Gitlab end def self.label_for(name) + name = name.to_s config = config_for(name) - (config && config['label']) || name.to_s.titleize + (config && config['label']) || LABELS[name] || name.titleize end end end -- cgit v1.2.1 From 7cb442eed4f488e378b3f20008ebe6ed3b53d31d Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 3 Nov 2015 18:23:48 +0100 Subject: Fix Project update service When project is updated and it is a fork, then visibility_level should not be less restrictive than in its parent project. --- app/models/project.rb | 2 +- app/services/projects/update_service.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index a4c634bdb5c..7f2dd37a3cc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -786,6 +786,6 @@ class Project < ActiveRecord::Base def visibility_level_allowed?(level) return true unless forked? - Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level) + Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i) end end diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 69bdd045ddf..0a42f3e02aa 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -11,6 +11,15 @@ module Projects end end + unless project.visibility_level_allowed?(new_visibility) + level_name = Gitlab::VisibilityLevel.level_name(new_visibility) + project.errors.add( + :visibility_level, + "#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive" + ) + return false + end + new_branch = params[:default_branch] if project.repository.exists? && new_branch && new_branch != project.default_branch -- cgit v1.2.1 From 05eb9e7884a1a1eb4144f84e7f586d26b011f4f1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 3 Nov 2015 16:16:15 -0500 Subject: Minor reformatting for Facebook integration doc [ci skip] --- doc/integration/facebook.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md index 5880b5baf76..bc1f1673086 100644 --- a/doc/integration/facebook.md +++ b/doc/integration/facebook.md @@ -19,7 +19,7 @@ something else descriptive. 1. Enter the address of your GitLab installation at the bottom of the package - ![Facebook Website URL](facebook_website_url.png) + ![Facebook Website URL](facebook_website_url.png) 1. Choose "Next" @@ -28,6 +28,7 @@ something else descriptive. 1. Choose "Settings" in the menu on the left 1. Fill in a contact email for your app + ![Facebook App Settings](facebook_app_settings.png) 1. Choose "Save Changes" @@ -51,15 +52,15 @@ something else descriptive. For omnibus package: ```sh - sudo editor /etc/gitlab/gitlab.rb + sudo editor /etc/gitlab/gitlab.rb ``` For installations from source: ```sh - cd /home/git/gitlab + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml + sudo -u git -H editor config/gitlab.yml ``` 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. @@ -69,20 +70,20 @@ something else descriptive. For omnibus package: ```ruby - gitlab_rails['omniauth_providers'] = [ - { - "name" => "facebook", - "app_id" => "YOUR_APP_ID", - "app_secret" => "YOUR_APP_SECRET" - } - ] + gitlab_rails['omniauth_providers'] = [ + { + "name" => "facebook", + "app_id" => "YOUR_APP_ID", + "app_secret" => "YOUR_APP_SECRET" + } + ] ``` For installations from source: ``` - - { name: 'facebook', app_id: 'YOUR_APP_ID', - app_secret: 'YOUR_APP_SECRET' } + - { name: 'facebook', app_id: 'YOUR_APP_ID', + app_secret: 'YOUR_APP_SECRET' } ``` 1. Change 'YOUR_APP_ID' to the API key from Facebook page in step 10. -- cgit v1.2.1 From 312375ac7c7d6619740899cd185a8dde1d653955 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 3 Nov 2015 17:10:17 -0500 Subject: Update Shell Commands doc for configurable git binary path --- doc/development/shell_commands.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/doc/development/shell_commands.md b/doc/development/shell_commands.md index 2d1d0fb4154..65cdd74bdb6 100644 --- a/doc/development/shell_commands.md +++ b/doc/development/shell_commands.md @@ -35,6 +35,16 @@ Gitlab::Popen.popen(%W(find /some/path -not -path /some/path -mmin +120 -delete) This coding style could have prevented CVE-2013-4490. +## Always use the configurable git binary path for git commands + +```ruby +# Wrong +system(*%W(git branch -d -- #{branch_name})) + +# Correct +system(*%W(#{Gitlab.config.git.bin_path} branch -d -- #{branch_name})) +``` + ## Bypass the shell by splitting commands into separate tokens When we pass shell commands as a single string to Ruby, Ruby will let `/bin/sh` evaluate the entire string. Essentially, we are asking the shell to evaluate a one-line script. This creates a risk for shell injection attacks. It is better to split the shell command into tokens ourselves. Sometimes we use the scripting capabilities of the shell to change the working directory or set environment variables. All of this can also be achieved securely straight from Ruby @@ -81,9 +91,9 @@ In the GitLab codebase, we avoid the option/argument ambiguity by _always_ using ```ruby # Wrong -system(*%W(git branch -d #{branch_name})) +system(*%W(#{Gitlab.config.git.bin_path} branch -d #{branch_name})) # Correct -system(*%W(git branch -d -- #{branch_name})) +system(*%W(#{Gitlab.config.git.bin_path} branch -d -- #{branch_name})) ``` This coding style could have prevented CVE-2013-4582. @@ -94,9 +104,9 @@ Capturing the output of shell commands with backticks reads nicely, but you are ```ruby # Wrong -logs = `cd #{repo_dir} && git log` +logs = `cd #{repo_dir} && #{Gitlab.config.git.bin_path} log` # Correct -logs, exit_status = Gitlab::Popen.popen(%W(git log), repo_dir) +logs, exit_status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} log), repo_dir) # Wrong user = `whoami` @@ -108,7 +118,7 @@ In other repositories, such as gitlab-shell you can also use `IO.popen`. ```ruby # Safe IO.popen example -logs = IO.popen(%W(git log), chdir: repo_dir) { |p| p.read } +logs = IO.popen(%W(#{Gitlab.config.git.bin_path} log), chdir: repo_dir) { |p| p.read } ``` Note that unlike `Gitlab::Popen.popen`, `IO.popen` does not capture standard error. -- cgit v1.2.1 From d09d62b6b875102b7a334fcf9e689537e1f25013 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 3 Nov 2015 17:10:38 -0500 Subject: Replace all usages of `git` command with configurable binary path Closes #3311 --- app/models/repository.rb | 12 ++++++------ config/initializers/2_app.rb | 6 +++--- lib/backup/repository.rb | 6 +++--- lib/gitlab/force_push_check.rb | 2 +- lib/gitlab/git_ref_validator.rb | 2 +- lib/gitlab/upgrader.rb | 8 ++++---- lib/tasks/gitlab/check.rake | 2 +- lib/tasks/gitlab/shell.rake | 10 +++++----- spec/models/project_wiki_spec.rb | 2 +- spec/requests/api/repositories_spec.rb | 4 ++-- spec/support/test_env.rb | 8 ++++---- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 9266ba27f0a..f8c4cb1387b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -89,7 +89,7 @@ class Repository def find_commits_by_message(query) # Limited to 1000 commits for now, could be parameterized? - args = %W(git log --pretty=%H --max-count 1000 --grep=#{query}) + args = %W(#{Gitlab.config.git.bin_path} log --pretty=%H --max-count 1000 --grep=#{query}) git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) commits = git_log_results.map { |c| commit(c) } @@ -296,7 +296,7 @@ class Repository end def last_commit_for_path(sha, path) - args = %W(git rev-list --max-count=1 #{sha} -- #{path}) + args = %W(#{Gitlab.config.git.bin_path} rev-list --max-count=1 #{sha} -- #{path}) sha = Gitlab::Popen.popen(args, path_to_repo).first.strip commit(sha) end @@ -347,7 +347,7 @@ class Repository end def branch_names_contains(sha) - args = %W(git branch --contains #{sha}) + args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha}) names = Gitlab::Popen.popen(args, path_to_repo).first if names.respond_to?(:split) @@ -364,7 +364,7 @@ class Repository end def tag_names_contains(sha) - args = %W(git tag --contains #{sha}) + args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha}) names = Gitlab::Popen.popen(args, path_to_repo).first if names.respond_to?(:split) @@ -505,7 +505,7 @@ class Repository def search_files(query, ref) offset = 2 - args = %W(git grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) + args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end @@ -537,7 +537,7 @@ class Repository end def fetch_ref(source_path, source_ref, target_ref) - args = %W(git fetch -f #{source_path} #{source_ref}:#{target_ref}) + args = %W(#{Gitlab.config.git.bin_path} fetch -f #{source_path} #{source_ref}:#{target_ref}) Gitlab::Popen.popen(args, path_to_repo) end diff --git a/config/initializers/2_app.rb b/config/initializers/2_app.rb index 688cdf5f4b0..35b150c9929 100644 --- a/config/initializers/2_app.rb +++ b/config/initializers/2_app.rb @@ -1,8 +1,8 @@ module Gitlab - VERSION = File.read(Rails.root.join("VERSION")).strip - REVISION = Gitlab::Popen.popen(%W(git log --pretty=format:%h -n 1)).first.chomp - def self.config Settings end + + VERSION = File.read(Rails.root.join("VERSION")).strip + REVISION = Gitlab::Popen.popen(%W(#{config.git.bin_path} log --pretty=format:%h -n 1)).first.chomp end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 4d70f7883dd..a82a7e1f7bf 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -35,7 +35,7 @@ module Backup if wiki.repository.empty? $progress.puts " [SKIPPED]".cyan else - cmd = %W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all) + cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all) output, status = Gitlab::Popen.popen(cmd) if status.zero? $progress.puts " [DONE]".green @@ -67,7 +67,7 @@ module Backup FileUtils.mkdir_p(path_to_repo(project)) cmd = %W(tar -xf #{path_to_bundle(project)} -C #{path_to_repo(project)}) else - cmd = %W(git init --bare #{path_to_repo(project)}) + cmd = %W(#{Gitlab.config.git.bin_path} init --bare #{path_to_repo(project)}) end if system(*cmd, silent) @@ -87,7 +87,7 @@ module Backup # that was initialized with ProjectWiki.new() and then # try to restore with 'git clone --bare'. FileUtils.rm_rf(path_to_repo(wiki)) - cmd = %W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}) + cmd = %W(#{Gitlab.config.git.bin_path} clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}) if system(*cmd, silent) $progress.puts " [DONE]".green diff --git a/lib/gitlab/force_push_check.rb b/lib/gitlab/force_push_check.rb index fdb6a35c78d..93c6a5bb7f5 100644 --- a/lib/gitlab/force_push_check.rb +++ b/lib/gitlab/force_push_check.rb @@ -7,7 +7,7 @@ module Gitlab if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev) false else - missed_refs, _ = Gitlab::Popen.popen(%W(git --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})) + missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev})) missed_refs.split("\n").size > 0 end end diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb index 39d17def930..4d83d8e72a8 100644 --- a/lib/gitlab/git_ref_validator.rb +++ b/lib/gitlab/git_ref_validator.rb @@ -6,7 +6,7 @@ module Gitlab # Returns true for a valid reference name, false otherwise def validate(ref_name) Gitlab::Utils.system_silent( - %W(git check-ref-format refs/#{ref_name})) + %W(#{Gitlab.config.git.bin_path} check-ref-format refs/#{ref_name})) end end end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index cf040971c6e..f3567f3ef85 100644 --- a/lib/gitlab/upgrader.rb +++ b/lib/gitlab/upgrader.rb @@ -50,15 +50,15 @@ module Gitlab end def fetch_git_tags - remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git)) + remote_tags, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git)) remote_tags.split("\n").grep(/tags\/v#{current_version.major}/) end def update_commands { - "Stash changed files" => %W(git stash), - "Get latest code" => %W(git fetch), - "Switch to new version" => %W(git checkout v#{latest_version}), + "Stash changed files" => %W(#{Gitlab.config.git.bin_path} stash), + "Get latest code" => %W(#{Gitlab.config.git.bin_path} fetch), + "Switch to new version" => %W(#{Gitlab.config.git.bin_path} checkout v#{latest_version}), "Install gems" => %W(bundle), "Migrate DB" => %W(bundle exec rake db:migrate), "Recompile assets" => %W(bundle exec rake assets:clean assets:precompile), diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 2e73f792a9d..a25fac62cfc 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -824,7 +824,7 @@ namespace :gitlab do repo_dirs = Dir.glob(File.join(namespace_dir, '*')) repo_dirs.each do |dir| puts "\nChecking repo at #{dir}" - system(*%w(git fsck), chdir: dir) + system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: dir) end end end diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake index 3c0cc763d17..dd61632e557 100644 --- a/lib/tasks/gitlab/shell.rake +++ b/lib/tasks/gitlab/shell.rake @@ -17,7 +17,7 @@ namespace :gitlab do # Clone if needed unless File.directory?(target_dir) - system(*%W(git clone -- #{args.repo} #{target_dir})) + system(*%W(#{Gitlab.config.git.bin_path} clone -- #{args.repo} #{target_dir})) end # Make sure we're on the right tag @@ -27,7 +27,7 @@ namespace :gitlab do reseted = reset_to_commit(args) unless reseted - system(*%W(git fetch origin)) + system(*%W(#{Gitlab.config.git.bin_path} fetch origin)) reset_to_commit(args) end @@ -128,14 +128,14 @@ namespace :gitlab do end def reset_to_commit(args) - tag, status = Gitlab::Popen.popen(%W(git describe -- #{args.tag})) + tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- #{args.tag})) unless status.zero? - tag, status = Gitlab::Popen.popen(%W(git describe -- origin/#{args.tag})) + tag, status = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} describe -- origin/#{args.tag})) end tag = tag.strip - system(*%W(git reset --hard #{tag})) + system(*%W(#{Gitlab.config.git.bin_path} reset --hard #{tag})) end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index 94802dcfb79..9f6cdeeaa96 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -223,7 +223,7 @@ describe ProjectWiki do def create_temp_repo(path) FileUtils.mkdir_p path - system(*%W(git init --quiet --bare -- #{path})) + system(*%W(#{Gitlab.config.git.bin_path} init --quiet --bare -- #{path})) end def remove_temp_repo(path) diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 1149f7e7989..faf6b77a462 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -36,8 +36,8 @@ describe API::API, api: true do it 'should create a new annotated tag' do # Identity must be set in .gitconfig to create annotated tag. repo_path = project.repository.path_to_repo - system(*%W(git --git-dir=#{repo_path} config user.name #{user.name})) - system(*%W(git --git-dir=#{repo_path} config user.email #{user.email})) + system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name})) + system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email})) post api("/projects/#{project.id}/repository/tags", user), tag_name: 'v7.1.0', diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index d12ba25b71b..787670e9297 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -96,15 +96,15 @@ module TestEnv clone_url = "https://gitlab.com/gitlab-org/#{repo_name}.git" unless File.directory?(repo_path) - system(*%W(git clone -q #{clone_url} #{repo_path})) + system(*%W(#{Gitlab.config.git.bin_path} clone -q #{clone_url} #{repo_path})) end Dir.chdir(repo_path) do branch_sha.each do |branch, sha| # Try to reset without fetching to avoid using the network. - reset = %W(git update-ref refs/heads/#{branch} #{sha}) + reset = %W(#{Gitlab.config.git.bin_path} update-ref refs/heads/#{branch} #{sha}) unless system(*reset) - if system(*%w(git fetch origin)) + if system(*%W(#{Gitlab.config.git.bin_path} fetch origin)) unless system(*reset) raise 'The fetched test seed '\ 'does not contain the required revision.' @@ -117,7 +117,7 @@ module TestEnv end # We must copy bare repositories because we will push to them. - system(git_env, *%W(git clone -q --bare #{repo_path} #{repo_path_bare})) + system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare})) end def copy_repo(project) -- cgit v1.2.1 From 5f94e130d11adf4398058d1c558de6f44b11675a Mon Sep 17 00:00:00 2001 From: mozillazg Date: Wed, 4 Nov 2015 11:41:44 +0800 Subject: update example of regex for pytest-cov --- app/views/projects/ci_settings/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml index d711413c6b9..20bdccc9027 100644 --- a/app/views/projects/ci_settings/_form.html.haml +++ b/app/views/projects/ci_settings/_form.html.haml @@ -102,7 +102,7 @@ %code \(\d+.\d+\%\) covered %li pytest-cov (Python) - - %code \d+\%$ + %code \d+\%\s*$ -- cgit v1.2.1 From 91cbf9db0c5c95381e7d422fd684e623d58fadab Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 26 Oct 2015 07:26:03 +0100 Subject: Add allow_failure field to commit status API Closes #3196 --- CHANGELOG | 1 + doc/api/commits.md | 6 ++++-- lib/api/entities.rb | 2 +- spec/requests/api/commit_status_spec.rb | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 16055208db5..e7238c45662 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses + - Add allow_failure field to commit status API (Stan Hu) - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL diff --git a/doc/api/commits.md b/doc/api/commits.md index 9f72adc6ed9..8e4a0ee1b82 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -22,7 +22,8 @@ Parameters: "author_name": "Dmitriy Zaporozhets", "author_email": "dzaporozhets@sphereconsultinginc.com", "created_at": "2012-09-20T11:50:22+03:00", - "message": "Replace sanitize with escape once" + "message": "Replace sanitize with escape once", + "allow_failure": false }, { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", @@ -31,7 +32,8 @@ Parameters: "author_name": "randx", "author_email": "dmitriy.zaporozhets@gmail.com", "created_at": "2012-09-20T09:06:12+03:00", - "message": "Sanitize for network graph" + "message": "Sanitize for network graph", + "allow_failure": false } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 883a5e14b17..20cadae2291 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -231,7 +231,7 @@ module API class CommitStatus < Grape::Entity expose :id, :sha, :ref, :status, :name, :target_url, :description, - :created_at, :started_at, :finished_at + :created_at, :started_at, :finished_at, :allow_failure expose :author, using: Entities::UserBasic end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index b9e6dfc15a7..a28607bd240 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -18,7 +18,7 @@ describe API::API, api: true do 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') + @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running', allow_failure: true) @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') @@ -30,6 +30,8 @@ describe API::API, api: true do expect(json_response).to be_an Array expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id) + json_response.sort_by!{ |status| status['id'] } + expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false]) end it "should return all commit statuses" do -- cgit v1.2.1 From bdb45360e76a272c275690cb11227a737c56e934 Mon Sep 17 00:00:00 2001 From: Tim Jabs Date: Thu, 8 Oct 2015 18:06:51 +0200 Subject: Changed documentation of converting a MySQL-Database with Gitlab to a Postgresdatatabase. The instructions were missleading. See -> https://gitlab.com/gitlab-org/gitlab-ce/issues/2904 --- doc/update/mysql_to_postgresql.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index a596ea38456..a7de5648c0e 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -60,6 +60,9 @@ sudo -u git -H python mysql-postgresql-converter/db_converter.py gitlabhq_produc sudo -u git -H ed -s db/database.sql < mysql-postgresql-converter/move_drop_indexes.ed # Compress database backup +# Warning: If you have Gitlab 7.12.0 or older skip this step and import the database.sql directly into the backup with: +# sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql +# The compressed databasedump is not supported at 7.12.0 and older. sudo -u git -H gzip db/database.sql # Replace the MySQL dump in TIMESTAMP_gitlab_backup.tar. @@ -71,4 +74,5 @@ sudo -u git -H tar rf TIMESTAMP_gitlab_backup.tar db/database.sql.gz # Done! TIMESTAMP_gitlab_backup.tar can now be restored into a Postgres GitLab # installation. +# See https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/backup_restore.md for more information about backups. ``` -- cgit v1.2.1 From c5d637b2cad3ad760f54d4aaa398847b4e251524 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 4 Nov 2015 14:13:25 +0100 Subject: Improvements to profile page UI * add separator between tabs * show project avatars * fix tooltip offset on user calendar * remove gray hover for tabs Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/calendar.js.coffee | 2 +- app/assets/stylesheets/framework/common.scss | 22 +++++++++++++++++++++- app/assets/stylesheets/framework/mixins.scss | 1 + app/assets/stylesheets/pages/profile.scss | 4 ---- app/views/users/show.html.haml | 6 +++--- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 2b1e20d3225..97621236924 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -25,7 +25,7 @@ class @Calendar 30 ] legendCellPadding: 3 - cellSize: $('.user-calendar').width() / 76 + cellSize: $('.user-calendar').width() / 73 onClick: (date, count) -> formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() $.ajax diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 3d0b71e066e..41287d52f69 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -389,12 +389,32 @@ table { .center-middle-menu { @include nav-menu; + padding: 0; text-align: center; margin: -$gl-padding; - height: auto; margin-top: 0; margin-bottom: 0; + height: 58px; border-bottom: 1px solid $border-color; + + li { + &:after { + content: "|"; + color: $border-gray-light; + } + + &:last-child { + &:after { + content: none; + } + } + + > a { + display: inline-block; + text-transform: uppercase; + font-size: 13px; + } + } } .dropzone .dz-preview .dz-progress { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index fe078d016d7..b9c179f2881 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -137,6 +137,7 @@ &:hover, &:active, &:focus { text-decoration: none; + outline: none; } } diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index bc1ad21305a..1d6ca0dfc13 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -75,7 +75,3 @@ text-decoration: none; } } - -.cal-heatmap-container { - margin: 0 auto; -} diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index e22d93aae84..5a15c6c244a 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -73,7 +73,7 @@ .user-calendar-activities -%ul.nav.center-middle-menu +%ul.center-middle-menu %li.active = link_to "#activity", 'data-toggle' => 'tab' do Activity @@ -106,14 +106,14 @@ .contributed-projects = render 'shared/projects/list', projects: @contributed_projects.sort_by(&:star_count).reverse, - projects_limit: 5, stars: true, avatar: false + projects_limit: 5, stars: true, avatar: true - if @projects.present? .tab-pane#personal .personal-projects = render 'shared/projects/list', projects: @projects.sort_by(&:star_count).reverse, - projects_limit: 10, stars: true, avatar: false + projects_limit: 10, stars: true, avatar: true :coffeescript $(".user-calendar").load("#{user_calendar_path}") -- cgit v1.2.1 From 8508be74d2063bb86e39d1cc8667e8adb9dfe63e Mon Sep 17 00:00:00 2001 From: Kjel Delaey Date: Wed, 4 Nov 2015 15:55:19 +0100 Subject: Style inline event items with titles containing long words properly.. On a tablet in portrait mode with a width of 768px the event-title isn't aligned properly when the title contains a long string. This also happens when resizing your browser viewport on a desktop. Example string: Administrator pushed new branch feature-branch-with-a-very-long-name at Gitlab Org / Gitlab Test In the UI it would look like the example below: ---------- | | | AVATAR | less than a minute ago | | ---------- Administrator pushed new branch feature-branch-with-a-very-long-name at... --- app/assets/stylesheets/pages/events.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index dfb901652bf..d2ca106cc8d 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -11,9 +11,12 @@ color: #7f8fa4; &.event-inline { + padding-left: $gl-padding + $gl-avatar-size; + .avatar { position: relative; top: -2px; + margin-left: -$gl-avatar-size; } .event-title { @@ -155,6 +158,10 @@ @media (max-width: $screen-xs-max) { .event-item { + &.event-inline { + padding-left: $gl-padding; + } + .event-title { white-space: normal; overflow: visible; -- cgit v1.2.1 From 8b0906c86d10ff02865e99400f2c0e3bdd1ac6fe Mon Sep 17 00:00:00 2001 From: Kjel Delaey Date: Wed, 4 Nov 2015 16:22:27 +0100 Subject: Apply the same rules to block event items.. Try to avoid alignment issues as well. --- app/assets/stylesheets/pages/events.scss | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index d2ca106cc8d..02401c7c73a 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -4,19 +4,16 @@ */ .event-item { font-size: $gl-font-size; - padding: $gl-padding; + padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size); margin-left: -$gl-padding; margin-right: -$gl-padding; border-bottom: 1px solid $table-border-color; color: #7f8fa4; &.event-inline { - padding-left: $gl-padding + $gl-avatar-size; - .avatar { position: relative; top: -2px; - margin-left: -$gl-avatar-size; } .event-title { @@ -33,6 +30,7 @@ } .avatar { + margin-left: -$gl-avatar-size; margin-right: 15px; } @@ -46,9 +44,6 @@ } .event-body { - margin-left: 63px; - margin-right: 80px; - .event-note { margin-top: 5px; word-wrap: break-word; @@ -158,9 +153,7 @@ @media (max-width: $screen-xs-max) { .event-item { - &.event-inline { - padding-left: $gl-padding; - } + padding-left: $gl-padding; .event-title { white-space: normal; -- cgit v1.2.1 From 3f2955de3bf08ef1ec378d323fb2360547a4ca16 Mon Sep 17 00:00:00 2001 From: Kjel Delaey Date: Wed, 4 Nov 2015 16:23:39 +0100 Subject: Combine selectors because the same style is being applied --- app/assets/stylesheets/pages/events.scss | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 02401c7c73a..f8d1afda73e 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -16,10 +16,7 @@ top: -2px; } - .event-title { - line-height: 44px; - } - + .event-title, .event-item-timestamp { line-height: 44px; } -- cgit v1.2.1 From 530c9519d4d0760848daf5026f93fbcbff348656 Mon Sep 17 00:00:00 2001 From: Kjel Delaey Date: Wed, 4 Nov 2015 16:40:22 +0100 Subject: Make sure that multi-commit rows inside event bodies are aligned properly.. Prevent the second, third, ... rows from having a different indentation than the first commit row. Adding the extra "15px padding" to the event item prevents this. Having a 15px margin on the avatar only doesn't prevent this from happening. --- app/assets/stylesheets/pages/events.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index f8d1afda73e..c150b4ac19f 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -4,7 +4,7 @@ */ .event-item { font-size: $gl-font-size; - padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size); + padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size + 15px); margin-left: -$gl-padding; margin-right: -$gl-padding; border-bottom: 1px solid $table-border-color; @@ -27,8 +27,7 @@ } .avatar { - margin-left: -$gl-avatar-size; - margin-right: 15px; + margin-left: -($gl-avatar-size + 15px); } .event-title { -- cgit v1.2.1 From 07821839d0616c06180388b91e3131bc008522a0 Mon Sep 17 00:00:00 2001 From: mutec Date: Wed, 4 Nov 2015 17:49:23 +0100 Subject: Update installation.md --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 599d2541f2c..e31448263c7 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -332,7 +332,7 @@ GitLab Shell is an SSH access and repository management software developed speci # Go to Gitlab installation folder - cd /home/git/gilab + cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production -- cgit v1.2.1 From 95da91a66de5820a44989aa708338c24177cd10c Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Wed, 4 Nov 2015 17:07:21 +0000 Subject: Use single spaces --- doc_styleguide.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc_styleguide.md b/doc_styleguide.md index 656bb1d17ff..cceb449a854 100644 --- a/doc_styleguide.md +++ b/doc_styleguide.md @@ -15,6 +15,8 @@ For subtitles, use '##', '###' and so on. - Do not duplicate information. - Be brief and clear. - Whenever it applies, add documents in alphabetical order. +- Write in US English +- Use [single spaces](http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html) instead of double spaces. ## Images -- cgit v1.2.1 From dfccc28e38b6298eff8eeedf1ba91a93c0182353 Mon Sep 17 00:00:00 2001 From: Vincent Lequertier Date: Wed, 4 Nov 2015 19:04:20 +0100 Subject: It makes more sense like this --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 599d2541f2c..5a09cf7c965 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -253,8 +253,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da nproc # Enable cluster mode if you expect to have a high load instance - # Ex. change amount of workers to 3 for 2GB RAM server # Set the number of workers to at least the number of cores + # Ex. change amount of workers to 3 for 2GB RAM server sudo -u git -H editor config/unicorn.rb # Copy the example Rack attack config -- cgit v1.2.1 From 3bc012db77e1b59986362d8de0660b97a15c9d1f Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 4 Nov 2015 22:13:40 +0100 Subject: Fix GitlabV::isibilityLevel::level_name method --- lib/gitlab/visibility_level.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb index d0ffe24f827..3160a3c7582 100644 --- a/lib/gitlab/visibility_level.rb +++ b/lib/gitlab/visibility_level.rb @@ -55,7 +55,7 @@ module Gitlab def level_name(level) level_name = 'Unknown' options.each do |name, lvl| - level_name = name if lvl == level + level_name = name if lvl == level.to_i end level_name -- cgit v1.2.1 From 89ecba5e6cec6632c2f8e3baef6604b1b8ea0d45 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 4 Nov 2015 23:40:43 +0100 Subject: Update forks visibility_level after parent project visibility_level change --- app/models/project.rb | 6 ++++- app/services/projects/update_service.rb | 48 ++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 7f2dd37a3cc..f287e59b6df 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -731,7 +731,11 @@ class Project < ActiveRecord::Base end def forks_count - ForkedProjectLink.where(forked_from_project_id: self.id).count + forks.count + end + + def forks + ForkedProjectLink.where(forked_from_project_id: self.id) end def find_label(name) diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 0a42f3e02aa..0d2665f298d 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -3,21 +3,17 @@ module Projects def execute # check that user is allowed to set specified visibility_level new_visibility = params[:visibility_level] - if new_visibility && new_visibility.to_i != project.visibility_level - unless can?(current_user, :change_visibility_level, project) && - Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) - deny_visibility_level(project, new_visibility) - return project + if new_visibility + if new_visibility.to_i != project.visibility_level + unless can?(current_user, :change_visibility_level, project) && + Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility) + deny_visibility_level(project, new_visibility) + return project + end end - end - unless project.visibility_level_allowed?(new_visibility) - level_name = Gitlab::VisibilityLevel.level_name(new_visibility) - project.errors.add( - :visibility_level, - "#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive" - ) - return false + return false unless visibility_level_allowed?(new_visibility) + update_forks_visibility_level(new_visibility) end new_branch = params[:default_branch] @@ -32,5 +28,31 @@ module Projects end end end + + private + + def visibility_level_allowed?(level) + return true if project.visibility_level_allowed?(level) + + level_name = Gitlab::VisibilityLevel.level_name(level) + project.errors.add( + :visibility_level, + "#{level_name} could not be set as visibility level of this project - parent project settings are more restrictive" + ) + + false + end + + def update_forks_visibility_level(new_level) + project.forks.each do |forked_link| + forked_project = forked_link.forked_to_project + fork_level = forked_project.visibility_level + + if fork_level > new_level.to_i + forked_project.visibility_level = new_level.to_i + forked_project.save! + end + end + end end end -- cgit v1.2.1 From b6f89c3490f8c31607073c9761de3ebb30ae244e Mon Sep 17 00:00:00 2001 From: Kjel Delaey Date: Thu, 5 Nov 2015 09:56:51 +0100 Subject: Add right margin to event-body.. Otherwise text will flow under the "timeago" element. The original value was 80px (see commit 8b0906c8), but a value of 174px makes more sense. (see event-title `calc` function). --- app/assets/stylesheets/pages/events.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index c150b4ac19f..282aaf2219b 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -40,6 +40,8 @@ } .event-body { + margin-right: 174px; + .event-note { margin-top: 5px; word-wrap: break-word; -- cgit v1.2.1 From 33b8f002636ad6171637108b53732c74d90b14ad Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 10:51:12 +0100 Subject: Add edit/update tag actions for future release notes Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/tags_controller.rb | 8 ++++++++ app/views/projects/tags/_tag.html.haml | 2 ++ app/views/projects/tags/edit.html.haml | 0 config/routes.rb | 2 +- 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 app/views/projects/tags/edit.html.haml diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index f565fbbbbc3..a30c284c41f 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -10,6 +10,14 @@ class Projects::TagsController < Projects::ApplicationController @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) end + def edit + # TODO: implement + end + + def update + # TODO: implement + end + def create result = CreateTagService.new(@project, current_user). execute(params[:tag_name], params[:ref], params[:message]) diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 2ca295fc5f3..887f3ab075b 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -9,6 +9,8 @@   = strip_gpg_signature(tag.message) .controls + = link_to edit_namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do + = icon("pencil") - if can? current_user, :download_code, @project = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs' - if can?(current_user, :admin_project, @project) diff --git a/app/views/projects/tags/edit.html.haml b/app/views/projects/tags/edit.html.haml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/config/routes.rb b/config/routes.rb index 0458f538eb6..8dc167cbbde 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -569,7 +569,7 @@ Gitlab::Application.routes.draw do end resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } - resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } + resources :tags, constraints: { id: Gitlab::Regex.git_reference_regex } resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] -- cgit v1.2.1 From 1c4d1c3bd69a6f9ec43cce4ab59de4ba47f73229 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 11:03:02 +0100 Subject: Add release model Signed-off-by: Dmitriy Zaporozhets --- app/models/release.rb | 2 ++ db/migrate/20151105094515_create_releases.rb | 14 ++++++++++++++ db/schema.rb | 13 ++++++++++++- spec/factories/releases.rb | 9 +++++++++ spec/models/release_spec.rb | 5 +++++ 5 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 app/models/release.rb create mode 100644 db/migrate/20151105094515_create_releases.rb create mode 100644 spec/factories/releases.rb create mode 100644 spec/models/release_spec.rb diff --git a/app/models/release.rb b/app/models/release.rb new file mode 100644 index 00000000000..1dc9ce6dd4f --- /dev/null +++ b/app/models/release.rb @@ -0,0 +1,2 @@ +class Release < ActiveRecord::Base +end diff --git a/db/migrate/20151105094515_create_releases.rb b/db/migrate/20151105094515_create_releases.rb new file mode 100644 index 00000000000..fe4608c6662 --- /dev/null +++ b/db/migrate/20151105094515_create_releases.rb @@ -0,0 +1,14 @@ +class CreateReleases < ActiveRecord::Migration + def change + create_table :releases do |t| + t.string :tag + t.text :description + t.integer :project_id + + t.timestamps + end + + add_index :releases, :project_id + add_index :releases, [:project_id, :tag] + end +end diff --git a/db/schema.rb b/db/schema.rb index 73fc83c3d6b..34449024478 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: 20151026182941) do +ActiveRecord::Schema.define(version: 20151105094515) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -637,6 +637,17 @@ ActiveRecord::Schema.define(version: 20151026182941) do add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree + create_table "releases", force: true do |t| + t.string "tag" + t.text "description" + t.integer "project_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree + add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree + create_table "sent_notifications", force: true do |t| t.integer "project_id" t.integer "noteable_id" diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb new file mode 100644 index 00000000000..067d8138e41 --- /dev/null +++ b/spec/factories/releases.rb @@ -0,0 +1,9 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :release do + tag "MyString" + description "MyText" + project_id 1 + end +end diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb new file mode 100644 index 00000000000..e533734ba0d --- /dev/null +++ b/spec/models/release_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Release, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end -- cgit v1.2.1 From ba67af79a9ec0d37d08e51af034dd6c21170713c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 11:16:41 +0100 Subject: More release related logic to separate resource Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/releases_controller.rb | 32 +++++++++++++++++++++++++ app/controllers/projects/tags_controller.rb | 8 ------- app/models/project.rb | 3 ++- app/views/projects/releases/edit.html.haml | 6 +++++ app/views/projects/releases/show.html.haml | 1 + app/views/projects/tags/_tag.html.haml | 2 +- app/views/projects/tags/edit.html.haml | 0 config/routes.rb | 5 +++- 8 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 app/controllers/projects/releases_controller.rb create mode 100644 app/views/projects/releases/edit.html.haml create mode 100644 app/views/projects/releases/show.html.haml delete mode 100644 app/views/projects/tags/edit.html.haml diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb new file mode 100644 index 00000000000..877cc0f3674 --- /dev/null +++ b/app/controllers/projects/releases_controller.rb @@ -0,0 +1,32 @@ +class Projects::ReleasesController < Projects::ApplicationController + # Authorize + before_action :require_non_empty_project + before_action :authorize_download_code! + before_action :authorize_push_code! + before_action :tag + before_action :release + + def show + end + + def edit + end + + def update + description = params[:release][:description] + release.update_attributes(description: description) + release.save + + redirect_to namespace_project_tag_release_path(@project.namespace, @project, @tag.name) + end + + private + + def tag + @tag ||= @repository.find_tag(params[:tag_id]) + end + + def release + @release ||= @project.releases.find_or_initialize_by(tag: @tag.name) + end +end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index a30c284c41f..f565fbbbbc3 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -10,14 +10,6 @@ class Projects::TagsController < Projects::ApplicationController @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) end - def edit - # TODO: implement - end - - def update - # TODO: implement - end - def create result = CreateTagService.new(@project, current_user). execute(params[:tag_name], params[:ref], params[:message]) diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..cd3de01a95f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -121,6 +121,7 @@ class Project < ActiveRecord::Base has_many :starrers, through: :users_star_projects, source: :user has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' + has_many :releases, dependent: :destroy has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id @@ -247,7 +248,7 @@ class Project < ActiveRecord::Base joins(:namespace). iwhere('namespaces.path' => namespace_path) - projects.where('projects.path' => project_path).take || + projects.where('projects.path' => project_path).take || projects.iwhere('projects.path' => project_path).take end diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml new file mode 100644 index 00000000000..3b4a5e72238 --- /dev/null +++ b/app/views/projects/releases/edit.html.haml @@ -0,0 +1,6 @@ += form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form' }) do |f| + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do + = render 'projects/zen', f: f, attr: :description, classes: 'js-quick-submit' + = render 'projects/notes/hints' + .error-alert + = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/releases/show.html.haml b/app/views/projects/releases/show.html.haml new file mode 100644 index 00000000000..bd831500086 --- /dev/null +++ b/app/views/projects/releases/show.html.haml @@ -0,0 +1 @@ += debug @release diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 887f3ab075b..526dd3f580b 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -9,7 +9,7 @@   = strip_gpg_signature(tag.message) .controls - = link_to edit_namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do = icon("pencil") - if can? current_user, :download_code, @project = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs' diff --git a/app/views/projects/tags/edit.html.haml b/app/views/projects/tags/edit.html.haml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/config/routes.rb b/config/routes.rb index 8dc167cbbde..5836da47eb3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -569,7 +569,10 @@ Gitlab::Application.routes.draw do end resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } - resources :tags, constraints: { id: Gitlab::Regex.git_reference_regex } + resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do + resource :release + end + resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] -- cgit v1.2.1 From a4d75e3aec2e721231bc1e01a2e5e87aefe15113 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 12:15:25 +0100 Subject: Add ability to edit and show release notes Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/dispatcher.js.coffee | 3 +++ app/assets/stylesheets/framework/common.scss | 1 + app/controllers/projects/releases_controller.rb | 1 + app/views/layouts/nav/_project.html.haml | 2 +- app/views/projects/commits/_head.html.haml | 2 +- app/views/projects/releases/edit.html.haml | 24 ++++++++++++----- app/views/projects/releases/show.html.haml | 36 ++++++++++++++++++++++++- 7 files changed, 60 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 5bf0b302179..030826be74d 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -39,6 +39,9 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() new DropzoneInput($('.merge-request-form')) new IssuableForm($('.merge-request-form')) + when 'projects:releases:edit' + new ZenMode() + new DropzoneInput($('.release-form')) when 'projects:merge_requests:show' new Diff() shortcut_handler = new ShortcutsIssuable() diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 41287d52f69..ddbacd7fd41 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -16,6 +16,7 @@ .append-bottom-10 { margin-bottom:10px } .append-bottom-15 { margin-bottom:15px } .append-bottom-20 { margin-bottom:20px } +.append-bottom-default { margin-bottom: $gl-padding; } .inline { display: inline-block } .center { text-align: center } diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 877cc0f3674..7d1a011cc0a 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -7,6 +7,7 @@ class Projects::ReleasesController < Projects::ApplicationController before_action :release def show + @commit = @repository.commit(@tag.target) end def edit diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 20db2866d1f..2b91d7721f9 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 releases)) do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do = icon('history fw') %span diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index a849bf84698..f11a41cfd7b 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -12,7 +12,7 @@ Branches %span.badge.js-totalbranch-count= @repository.branches.size - = nav_link(controller: :tags) do + = nav_link(controller: [:tags, :releases]) do = link_to namespace_project_tags_path(@project.namespace, @project) do Tags %span.badge.js-totaltags-count= @repository.tags.length diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 3b4a5e72238..612e4696226 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -1,6 +1,18 @@ -= form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form' }) do |f| - = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :description, classes: 'js-quick-submit' - = render 'projects/notes/hints' - .error-alert - = f.submit 'Save changes', class: 'btn btn-save' += render "projects/commits/header_title" += render "projects/commits/head" + +.gray-content-block + .oneline + Release notes for #{@tag.name} + +.prepend-top-default + = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f| + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do + = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit' + = render 'projects/notes/hints' + .error-alert + .prepend-top-default + = f.submit 'Save changes', class: 'btn btn-save' + - if @release.persisted? + = link_to "Cancel", namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" + diff --git a/app/views/projects/releases/show.html.haml b/app/views/projects/releases/show.html.haml index bd831500086..606510f132f 100644 --- a/app/views/projects/releases/show.html.haml +++ b/app/views/projects/releases/show.html.haml @@ -1 +1,35 @@ -= debug @release +- page_title @release.tag, "Releases" += render "projects/commits/header_title" += render "projects/commits/head" + +.gray-content-block + .pull-right + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn' do + = icon("pencil") + .oneline Release notes for #{@tag.name} + +.append-bottom-default.prepend-top-default + - if @release.description.present? + .description + .wiki + = preserve do + = markdown @release.description + - else + This tag has no release notes yet. Press edit button to add one + +.gray-content-block.middle-block.clearfix + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + Browse code + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + Commits + - if can? current_user, :download_code, @project + = render 'projects/repositories/download_archive', ref: @tag.name, btn_class: 'btn-grouped' + -#- if can?(current_user, :admin_project, @project) + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do + %i.fa.fa-trash-o + +.gray-content-block.second-block + - if @commit + = render 'projects/commits/commit', commit: @commit, project: @project + - else + Cant find HEAD commit for this tag -- cgit v1.2.1 From 6051c28fc03b4d9928ee2f2855f210845f9c0579 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 5 Nov 2015 12:38:00 +0200 Subject: Allow groups to appear in the search results if the group owner allows it --- CHANGELOG | 1 + app/controllers/groups_controller.rb | 6 +-- app/finders/groups_finder.rb | 57 ++++++++++++------------ app/helpers/search_helper.rb | 2 +- app/models/group.rb | 2 +- app/views/groups/edit.html.haml | 9 ++++ db/migrate/20151103001141_add_public_to_group.rb | 5 +++ db/schema.rb | 9 ++-- spec/finders/group_finder_spec.rb | 15 +++++++ spec/helpers/search_helper_spec.rb | 5 +++ spec/models/group_spec.rb | 19 ++++++++ 11 files changed, 93 insertions(+), 37 deletions(-) create mode 100644 db/migrate/20151103001141_add_public_to_group.rb create mode 100644 spec/finders/group_finder_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0ec6030b130..3a75f50e9a2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.2.0 (unreleased) - Include commit logs in project search - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from + - Allow groups to appear in the search results if the group owner allows it v 8.1.3 - Spread out runner contacted_at updates diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 40fb15a5b36..fb4eb094f27 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -4,12 +4,12 @@ class GroupsController < Groups::ApplicationController before_action :group, except: [:new, :create] # Authorize - before_action :authorize_read_group!, except: [:show, :new, :create] + before_action :authorize_read_group!, except: [:show, :new, :create, :autocomplete] before_action :authorize_admin_group!, only: [:edit, :update, :destroy, :projects] before_action :authorize_create_group!, only: [:new, :create] # Load group projects - before_action :load_projects, except: [:new, :create, :projects, :edit, :update] + before_action :load_projects, except: [:new, :create, :projects, :edit, :update, :autocomplete] before_action :event_filter, only: :show layout :determine_layout @@ -133,7 +133,7 @@ class GroupsController < Groups::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar) + params.require(:group).permit(:name, :description, :path, :avatar, :public) end def load_events diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index d3597ef0901..b5f3176461c 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -6,33 +6,34 @@ class GroupsFinder private def all_groups(current_user) - if current_user - if current_user.authorized_groups.any? - # User has access to groups - # - # Return only: - # groups with public projects - # groups with internal projects - # groups with joined projects - # - group_ids = Project.public_and_internal_only.pluck(:namespace_id) + - current_user.authorized_groups.pluck(:id) - Group.where(id: group_ids) - else - # User has no group membership - # - # Return only: - # groups with public projects - # groups with internal projects - # - Group.where(id: Project.public_and_internal_only.pluck(:namespace_id)) - end - else - # Not authenticated - # - # Return only: - # groups with public projects - Group.where(id: Project.public_only.pluck(:namespace_id)) - end + group_ids = if current_user + if current_user.authorized_groups.any? + # User has access to groups + # + # Return only: + # groups with public projects + # groups with internal projects + # groups with joined projects + # + Project.public_and_internal_only.pluck(:namespace_id) + + current_user.authorized_groups.pluck(:id) + else + # User has no group membership + # + # Return only: + # groups with public projects + # groups with internal projects + # + Project.public_and_internal_only.pluck(:namespace_id) + end + else + # Not authenticated + # + # Return only: + # groups with public projects + Project.public_only.pluck(:namespace_id) + end + + Group.where("public IS TRUE OR id IN(?)", group_ids) end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index c31a556ff7b..a6ee6880247 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -70,7 +70,7 @@ module SearchHelper # Autocomplete results for the current user's groups def groups_autocomplete(term, limit = 5) - current_user.authorized_groups.search(term).limit(limit).map do |group| + GroupsFinder.new.execute(current_user).search(term).limit(limit).map do |group| { label: "group: #{search_result_sanitize(group.name)}", url: group_path(group) diff --git a/app/models/group.rb b/app/models/group.rb index 465c22d23ac..34904af3b5b 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -120,7 +120,7 @@ class Group < Namespace end def public_profile? - projects.public_only.any? + self.public || projects.public_only.any? end def post_create_hook diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index ae8fc9f85f0..57308a661c0 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -25,6 +25,15 @@ %hr = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + .form-group + %hr + = f.label :public, class: 'control-label' do + Public + .col-sm-10 + .checkbox + = f.check_box :public + %span.descr Make this group public (even if there is no any public project inside this group) + .form-actions = f.submit 'Save group', class: "btn btn-save" diff --git a/db/migrate/20151103001141_add_public_to_group.rb b/db/migrate/20151103001141_add_public_to_group.rb new file mode 100644 index 00000000000..635346300c2 --- /dev/null +++ b/db/migrate/20151103001141_add_public_to_group.rb @@ -0,0 +1,5 @@ +class AddPublicToGroup < ActiveRecord::Migration + def change + add_column :namespaces, :public, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 73fc83c3d6b..17d445a8baa 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: 20151026182941) do +ActiveRecord::Schema.define(version: 20151103001141) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -501,14 +501,15 @@ ActiveRecord::Schema.define(version: 20151026182941) do add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree create_table "namespaces", force: true do |t| - t.string "name", null: false - t.string "path", null: false + t.string "name", null: false + t.string "path", null: false t.integer "owner_id" t.datetime "created_at" t.datetime "updated_at" t.string "type" - t.string "description", default: "", null: false + t.string "description", default: "", null: false t.string "avatar" + t.boolean "public", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree diff --git a/spec/finders/group_finder_spec.rb b/spec/finders/group_finder_spec.rb new file mode 100644 index 00000000000..78dc027837c --- /dev/null +++ b/spec/finders/group_finder_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe GroupsFinder do + let(:user) { create :user } + let!(:group) { create :group } + let!(:public_group) { create :group, public: true } + + describe :execute do + it 'finds public group' do + groups = GroupsFinder.new.execute(user) + expect(groups.size).to eq(1) + expect(groups.first).to eq(public_group) + end + end +end diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index b327f4f911a..ebe9c29d91c 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -42,6 +42,11 @@ describe SearchHelper do expect(search_autocomplete_opts(project.name).size).to eq(1) end + it "includes the public group" do + group = create(:group, public: true) + expect(search_autocomplete_opts(group.name).size).to eq(1) + end + context "with a current project" do before { @project = create(:project) } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 80638fc8db2..0f23e81ace9 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -84,4 +84,23 @@ describe Group do expect(group.avatar_type).to eq(["only images allowed"]) end end + + describe "public_profile?" do + it "returns true for public group" do + group = create(:group, public: true) + expect(group.public_profile?).to be_truthy + end + + it "returns true for non-public group with public project" do + group = create(:group) + create(:project, :public, group: group) + expect(group.public_profile?).to be_truthy + end + + it "returns false for non-public group with no public projects" do + group = create(:group) + create(:project, group: group) + expect(group.public_profile?).to be_falsy + end + end end -- cgit v1.2.1 From 850bb21b12b21fe0cf943278bc8cadad85d48dc5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 13:49:34 +0100 Subject: Create show page for tag and render release notes there and on index page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/callout.scss | 6 ++-- app/assets/stylesheets/pages/commits.scss | 7 ++++ app/controllers/projects/releases_controller.rb | 6 +--- app/controllers/projects/tags_controller.rb | 7 ++++ app/views/projects/branches/_commit.html.haml | 4 +-- app/views/projects/releases/edit.html.haml | 3 +- app/views/projects/releases/show.html.haml | 35 -------------------- app/views/projects/tags/_tag.html.haml | 11 ++++++- app/views/projects/tags/show.html.haml | 44 +++++++++++++++++++++++++ config/routes.rb | 4 +-- 10 files changed, 77 insertions(+), 50 deletions(-) delete mode 100644 app/views/projects/releases/show.html.haml create mode 100644 app/views/projects/tags/show.html.haml diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index f1699d21c9b..f3ce4e3c219 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -9,9 +9,9 @@ .bs-callout { margin: 20px 0; padding: 20px; - border-left: 3px solid #eee; - color: #666; - background: #f9f9f9; + border-left: 3px solid $border-color; + color: $text-color; + background: $background-color; } .bs-callout h4 { margin-top: 0; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index e485487bcfd..c9dfcff6290 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -115,3 +115,10 @@ li.commit { } } } + +.branch-commit { + color: $gl-gray; + .commit-id, .commit-row-message { + color: $gl-gray; + } +} diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 7d1a011cc0a..f69a4bc729e 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -6,10 +6,6 @@ class Projects::ReleasesController < Projects::ApplicationController before_action :tag before_action :release - def show - @commit = @repository.commit(@tag.target) - end - def edit end @@ -18,7 +14,7 @@ class Projects::ReleasesController < Projects::ApplicationController release.update_attributes(description: description) release.save - redirect_to namespace_project_tag_release_path(@project.namespace, @project, @tag.name) + redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) end private diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index f565fbbbbc3..dfc8dbe01c5 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -8,6 +8,13 @@ class Projects::TagsController < Projects::ApplicationController def index sorted = VersionSorter.rsort(@repository.tag_names) @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) + @releases = project.releases.where(tag: @tags) + end + + def show + @tag = @repository.find_tag(params[:id]) + @release = @project.releases.find_or_initialize_by(tag: @tag.name) + @commit = @repository.commit(@tag.target) end def create diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index 68326e65d85..22d77dda938 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,5 +1,5 @@ -.branch-commit.light - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" +.branch-commit + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id" · %span.str-truncated = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 612e4696226..fb841b77a25 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -13,6 +13,5 @@ .error-alert .prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' - - if @release.persisted? - = link_to "Cancel", namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" + = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/projects/releases/show.html.haml b/app/views/projects/releases/show.html.haml deleted file mode 100644 index 606510f132f..00000000000 --- a/app/views/projects/releases/show.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -- page_title @release.tag, "Releases" -= render "projects/commits/header_title" -= render "projects/commits/head" - -.gray-content-block - .pull-right - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn' do - = icon("pencil") - .oneline Release notes for #{@tag.name} - -.append-bottom-default.prepend-top-default - - if @release.description.present? - .description - .wiki - = preserve do - = markdown @release.description - - else - This tag has no release notes yet. Press edit button to add one - -.gray-content-block.middle-block.clearfix - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do - Browse code - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do - Commits - - if can? current_user, :download_code, @project - = render 'projects/repositories/download_archive', ref: @tag.name, btn_class: 'btn-grouped' - -#- if can?(current_user, :admin_project, @project) - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do - %i.fa.fa-trash-o - -.gray-content-block.second-block - - if @commit - = render 'projects/commits/commit', commit: @commit, project: @project - - else - Cant find HEAD commit for this tag diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 526dd3f580b..5d203903f8b 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -1,13 +1,17 @@ - commit = @repository.commit(tag.target) +- release = @releases.find { |release| release.tag == tag.name } %li %div - = link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do + = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do %strong %i.fa.fa-tag = tag.name - if tag.message.present?   = strip_gpg_signature(tag.message) + - if release + %span.label.label-success release + .controls = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do = icon("pencil") @@ -22,3 +26,8 @@ - else %p Cant find HEAD commit for this tag + - if release && release.description.present? + .description.prepend-top-default + .wiki + = preserve do + = markdown release.description diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml new file mode 100644 index 00000000000..d2f23b96a8c --- /dev/null +++ b/app/views/projects/tags/show.html.haml @@ -0,0 +1,44 @@ +- page_title @tag.name, "Tags" += render "projects/commits/header_title" += render "projects/commits/head" + +.gray-content-block + .pull-right + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn' do + = icon("pencil") + - if @tag.message.present? + .title + %strong= @tag.name + = strip_gpg_signature(@tag.message) + - else + .oneline + .title + %strong= @tag.name + +.append-bottom-default.prepend-top-default + - if @release.description.present? + .description + .wiki + = preserve do + = markdown @release.description + - else + This tag has no release notes yet. Press edit button to add one + +.gray-content-block.middle-block.clearfix + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + Browse code + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + Commits + - if can? current_user, :download_code, @project + = render 'projects/repositories/download_archive', ref: @tag.name, btn_class: 'btn-grouped' + - if can?(current_user, :admin_project, @project) + .pull-right + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do + %i.fa.fa-trash-o + +.gray-content-block.second-block + - if @commit + = render 'projects/commits/commit', commit: @commit, project: @project + - else + Cant find HEAD commit for this tag + diff --git a/config/routes.rb b/config/routes.rb index 5836da47eb3..c7aa95118e0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -569,8 +569,8 @@ Gitlab::Application.routes.draw do end resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } - resources :tags, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do - resource :release + resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do + resource :release, only: [:edit, :update] end resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } -- cgit v1.2.1 From 312cf11b61e6bbee8283dfb267516e6b42454431 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 14:03:48 +0100 Subject: Add release description to new tag form Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/dispatcher.js.coffee | 3 +++ app/controllers/projects/tags_controller.rb | 7 +++++++ app/views/projects/tags/new.html.haml | 19 ++++++++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 030826be74d..951173af5d5 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -39,6 +39,9 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() new DropzoneInput($('.merge-request-form')) new IssuableForm($('.merge-request-form')) + when 'projects:tags:new' + new ZenMode() + new DropzoneInput($('.tag-form')) when 'projects:releases:edit' new ZenMode() new DropzoneInput($('.release-form')) diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index dfc8dbe01c5..c4a3e3dca94 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -23,6 +23,13 @@ class Projects::TagsController < Projects::ApplicationController if result[:status] == :success @tag = result[:tag] + + if params[:release_description] + release = @project.releases.find_or_initialize_by(tag: @tag.name) + release.update_attributes(description: params[:release_description]) + release.save + end + redirect_to namespace_project_tags_path(@project.namespace, @project) else @error = result[:message] diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 9f5c1be125c..9b224ff89b0 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -8,7 +8,7 @@ %h3.page-title %i.fa.fa-code-fork New tag -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' .col-sm-10 @@ -23,6 +23,23 @@ .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' .light (Optional) Entering a message will create an annotated tag. + %hr + .form-group + = label_tag :release_description, 'Release description', class: 'control-label' + .col-sm-10 + = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do + .zennable + %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") + .zen-backdrop + = text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: '' + %a.zen-enter-link(tabindex="-1" href="#") + = icon('expand') + Edit in fullscreen + %a.zen-leave-link(href="#") + = icon('compress') + + = render 'projects/notes/hints' + .help-block You can add release description to your tag. It will be stored in GitLab database and displayed on tags page .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' -- cgit v1.2.1 From 26677fbe213069a3820f9f20d528bd560d447bea Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 14:07:55 +0100 Subject: After tag is created - redirect to tag page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/tags_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index c4a3e3dca94..055f328677f 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -30,7 +30,7 @@ class Projects::TagsController < Projects::ApplicationController release.save end - redirect_to namespace_project_tags_path(@project.namespace, @project) + redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) else @error = result[:message] render action: 'new' -- cgit v1.2.1 From ba68facf8d744f6de49b40a2e9923e6555c92cd7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 3 Nov 2015 11:44:07 +0100 Subject: CI details cleanup - Add page titles to CI settings. - Fix CI admin navigation. - Remove duplicated scope. - Use monospace font for commit sha. - Add page title and header title to build page. - Proper authorization for cancel/retry builds. - Use gitlab pagination theme for builds and group members. - Don't paginate builds widget on build page. - Add badges to commit page Changes/Builds tabs. - Add "Builds" to commit Builds tab page title. - Add and use Ci::Build#retryable? method. - Add CI::Build#retried? method. - Allow all failed commit builds to be retried. - Proper authorization for cancel/retry all builds. - Remove unused param. - Use time_ago_with_tooltip where appropriate. - Tweak builds index text - Remove duplication between builds/build and commit_statuses/commit_status. - Use POST rather than GET for canceling and retrying builds. - Remove redundant URL helpers. - Add build ID to build page. - Link branch name on build page. - Move commit/:sha/ci to commit/:sha/builds. --- app/controllers/projects/builds_controller.rb | 10 +-- app/controllers/projects/commit_controller.rb | 38 ++++++++---- app/helpers/builds_helper.rb | 13 ---- app/helpers/ci/gitlab_helper.rb | 19 ------ app/helpers/ci_status_helper.rb | 2 +- app/models/ci/build.rb | 10 ++- app/models/commit_status.rb | 4 +- app/models/project_services/ci/hip_chat_message.rb | 2 +- app/models/project_services/ci/slack_message.rb | 2 +- app/models/project_services/gitlab_ci_service.rb | 2 +- app/views/ci/notify/build_fail_email.html.haml | 4 +- app/views/ci/notify/build_success_email.html.haml | 4 +- app/views/dashboard/groups/index.html.haml | 2 +- app/views/layouts/ci/_nav_admin.html.haml | 18 +++--- app/views/projects/_last_commit.html.haml | 2 +- app/views/projects/builds/_build.html.haml | 53 ---------------- app/views/projects/builds/_header_title.html.haml | 1 + app/views/projects/builds/index.html.haml | 12 ++-- app/views/projects/builds/show.html.haml | 39 ++++++------ app/views/projects/ci_services/edit.html.haml | 1 + app/views/projects/ci_services/index.html.haml | 1 + app/views/projects/ci_settings/edit.html.haml | 1 + app/views/projects/ci_web_hooks/index.html.haml | 1 + app/views/projects/commit/_ci_menu.html.haml | 6 +- app/views/projects/commit/_commit_box.html.haml | 4 +- app/views/projects/commit/builds.html.haml | 72 ++++++++++++++++++++++ app/views/projects/commit/ci.html.haml | 69 --------------------- .../commit_statuses/_commit_status.html.haml | 31 +++++++--- app/views/projects/runners/edit.html.haml | 2 + app/views/projects/runners/index.html.haml | 1 + app/views/projects/runners/show.html.haml | 21 ++++--- app/views/projects/triggers/index.html.haml | 1 + app/views/projects/variables/show.html.haml | 1 + config/routes.rb | 9 +-- spec/features/builds_spec.rb | 14 +++-- spec/features/commits_spec.rb | 2 +- .../project_services/gitlab_ci_service_spec.rb | 2 +- 37 files changed, 226 insertions(+), 250 deletions(-) delete mode 100644 app/helpers/builds_helper.rb delete mode 100644 app/views/projects/builds/_build.html.haml create mode 100644 app/views/projects/builds/_header_title.html.haml create mode 100644 app/views/projects/commit/builds.html.haml delete mode 100644 app/views/projects/commit/ci.html.haml diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 7d72e0b951b..953f30e7c03 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -30,7 +30,7 @@ class Projects::BuildsController < Projects::ApplicationController def show @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC') - @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20) + @builds = @builds.where("id not in (?)", @build.id) @commit = @build.commit respond_to do |format| @@ -42,17 +42,13 @@ class Projects::BuildsController < Projects::ApplicationController end def retry - if @build.commands.blank? + unless @build.retryable? 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 + redirect_to build_path(build) end def status diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 878c3a66e7d..deefdd76667 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -7,14 +7,14 @@ class Projects::CommitController < Projects::ApplicationController before_action :authorize_download_code!, except: [:cancel_builds] before_action :authorize_manage_builds!, only: [:cancel_builds] before_action :commit + before_action :authorize_manage_builds!, only: [:cancel_builds, :retry_builds] + before_action :define_show_vars, only: [:show, :builds] def show return git_not_found! unless @commit @line_notes = commit.notes.inline - @diffs = @commit.diffs @note = @project.build_commit_note(commit) - @notes_count = commit.notes.count @notes = commit.notes.not_inline.fresh @noteable = @commit @comments_allowed = @reply_allowed = true @@ -23,8 +23,6 @@ class Projects::CommitController < Projects::ApplicationController commit_id: @commit.id } - @ci_commit = project.ci_commit(commit.sha) - respond_to do |format| format.html format.diff { render text: @commit.to_diff } @@ -32,20 +30,25 @@ class Projects::CommitController < Projects::ApplicationController end end - def ci - @ci_commit = @project.ci_commit(@commit.sha) - @builds = @ci_commit.builds if @ci_commit - @notes_count = @commit.notes.count + def builds @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) + ci_commit.builds.running_or_pending.each(&:cancel) - redirect_to ci_namespace_project_commit_path(project.namespace, project, commit.sha) + redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha) end + def retry_builds + ci_commit.builds.latest.failed.each do |build| + if build.retryable? + Ci::Build.retry(build) + end + end + + redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha) + end def branches @branches = @project.repository.branch_names_contains(commit.id) @@ -53,11 +56,22 @@ class Projects::CommitController < Projects::ApplicationController render layout: false end + private + def commit @commit ||= @project.commit(params[:id]) end - private + def ci_commit + @ci_commit ||= project.ci_commit(commit.sha) + end + + def define_show_vars + @diffs = commit.diffs + @notes_count = commit.notes.count + + @builds = ci_commit.builds if ci_commit + end def authorize_manage_builds! unless can?(current_user, :manage_builds, project) diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb deleted file mode 100644 index 1b5a2c31d74..00000000000 --- a/app/helpers/builds_helper.rb +++ /dev/null @@ -1,13 +0,0 @@ -module BuildsHelper - def build_ref_link build - gitlab_ref_link build.project, build.ref - end - - def build_commit_link build - gitlab_commit_link build.project, build.short_sha - end - - def build_url(build) - namespace_project_build_path(build.gl_project, build.project, build) - end -end diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb index baddbc806f2..e34c8be1dfc 100644 --- a/app/helpers/ci/gitlab_helper.rb +++ b/app/helpers/ci/gitlab_helper.rb @@ -4,25 +4,6 @@ module Ci { :"data-no-turbolink" => "data-no-turbolink" } end - def gitlab_ref_link project, ref - gitlab_url = project.gitlab_url.dup - gitlab_url << "/commits/#{ref}" - link_to ref, gitlab_url, no_turbolink - end - - def gitlab_compare_link project, before, after - gitlab_url = project.gitlab_url.dup - gitlab_url << "/compare/#{before}...#{after}" - - link_to "#{before}...#{after}", gitlab_url, no_turbolink - end - - def gitlab_commit_link project, sha - gitlab_url = project.gitlab_url.dup - gitlab_url << "/commit/#{sha}" - link_to Ci::Commit.truncate_sha(sha), gitlab_url, no_turbolink - end - def yaml_web_editor_link(project) commits = project.commits diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index ed88df5dd86..0ecf77bb45e 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -1,7 +1,7 @@ module CiStatusHelper def ci_status_path(ci_commit) project = ci_commit.gl_project - ci_namespace_project_commit_path(project.namespace, project, ci_commit.sha) + builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha) end def ci_status_icon(ci_commit) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index b19e2ac1363..7f185ae7cc3 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -106,6 +106,14 @@ module Ci failed? && allow_failure? end + def retryable? + commands.present? + end + + def retried? + !self.commit.latest_builds_for_ref(self.ref).include?(self) + end + def trace_html html = Ci::Ansi2html::convert(trace) if trace.present? html || '' @@ -222,7 +230,7 @@ module Ci end def retry_url - if commands.present? + if retryable? Gitlab::Application.routes.url_helpers. retry_namespace_project_build_path(gl_project.namespace, gl_project, self) end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 0b73ab6d2eb..7d54d83974a 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -15,8 +15,8 @@ class CommitStatus < ActiveRecord::Base scope :pending, -> { where(status: 'pending') } scope :success, -> { where(status: 'success') } scope :failed, -> { where(status: 'failed') } - scope :running_or_pending, -> { where(status:[:running, :pending]) } - scope :finished, -> { where(status:[:success, :failed, :canceled]) } + scope :running_or_pending, -> { where(status: [:running, :pending]) } + scope :finished, -> { where(status: [:success, :failed, :canceled]) } 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) } diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb index cbf325cc525..d89466b689f 100644 --- a/app/models/project_services/ci/hip_chat_message.rb +++ b/app/models/project_services/ci/hip_chat_message.rb @@ -11,7 +11,7 @@ module Ci def to_s lines = Array.new lines.push("#{project.name} - ") - lines.push("Commit ##{commit.id}
") + lines.push("Commit ##{commit.id}
") lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}
") lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).") lines.join('') diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb index dc050a3fc59..1a6ff8e34c9 100644 --- a/app/models/project_services/ci/slack_message.rb +++ b/app/models/project_services/ci/slack_message.rb @@ -45,7 +45,7 @@ module Ci def attachment_message out = "<#{ci_project_url(project)}|#{project_name}>: " - out << "Commit <#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> " + out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> " out << "(<#{commit_sha_link}|#{commit.short_sha}>) " out << "of <#{commit_ref_link}|#{commit.ref}> " out << "by #{commit.git_author_name} " if commit.git_author_name diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 4dcd16ede3a..095d04e0df4 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -71,7 +71,7 @@ class GitlabCiService < CiService def build_page(sha, ref) if project.gitlab_ci_project.present? - ci_namespace_project_commit_url(project.namespace, project, sha) + builds_namespace_project_commit_url(project.namespace, project, sha) end end diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml index 69689a75022..cefb75040e9 100644 --- a/app/views/ci/notify/build_fail_email.html.haml +++ b/app/views/ci/notify/build_fail_email.html.haml @@ -7,7 +7,7 @@ = @project.name %p - Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -16,4 +16,4 @@ Message: #{@build.commit.git_commit_message} %p - Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml index 4e3015a356b..617b88f7345 100644 --- a/app/views/ci/notify/build_success_email.html.haml +++ b/app/views/ci/notify/build_success_email.html.haml @@ -8,7 +8,7 @@ = @project.name %p - Commit link: #{gitlab_commit_link(@project, @build.commit.short_sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -17,4 +17,4 @@ Message: #{@build.commit.git_commit_message} %p - Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index c249f5cacec..f3f3f58111e 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -16,4 +16,4 @@ - group = group_member.group = render 'shared/groups/group', group: group, group_member: group_member -= paginate @group_members += paginate @group_members, theme: 'gitlab' diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml index af2545a22d8..dcda04a4638 100644 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ b/app/views/layouts/ci/_nav_admin.html.haml @@ -9,23 +9,25 @@ = nav_link path: 'projects#index' do = link_to ci_admin_projects_path do = icon('list-alt fw') - Projects + %span + Projects = nav_link path: 'events#index' do = link_to ci_admin_events_path do = icon('book fw') - Events + %span + Events = nav_link path: ['runners#index', 'runners#show'] do = link_to ci_admin_runners_path do = icon('cog fw') - Runners - %small.pull-right - = Ci::Runner.count(:all) + %span + Runners + %span.count= Ci::Runner.count(:all) = nav_link path: 'builds#index' do = link_to ci_admin_builds_path do = icon('link fw') - Builds - %small.pull-right - = Ci::Build.count(:all) + %span + Builds + %span.count= Ci::Build.count(:all) = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to ci_admin_application_settings_path do = icon('cogs fw') diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml index d7b20bfc6b1..7e1ee2b7fc1 100644 --- a/app/views/projects/_last_commit.html.haml +++ b/app/views/projects/_last_commit.html.haml @@ -6,7 +6,7 @@ = ci_commit.status = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" - = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" + = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message" · #{time_ago_with_tooltip(commit.committed_date, skip_js: true)} by = commit_author_link(commit, avatar: true, size: 24) diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml deleted file mode 100644 index 4ce4ed63b40..00000000000 --- a/app/views/projects/builds/_build.html.haml +++ /dev/null @@ -1,53 +0,0 @@ -%tr.build - %td.status - = ci_status_with_icon(build.status) - - %td.commit_status-link - - if build.target_url - = link_to build.target_url do - %strong Build ##{build.id} - - else - %strong Build ##{build.id} - - - if build.show_warning? - %i.fa.fa-warning.text-warning - - %td - = link_to build.short_sha, namespace_project_commit_path(@project.namespace, @project, build.sha) - - %td - = link_to build.ref, namespace_project_commits_path(@project.namespace, @project, build.ref) - - %td - - if build.runner - = runner_link(build.runner) - - else - .light none - - %td - = build.name - - .pull-right - - if build.tags.any? - - build.tags.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 - - %td - .pull-right - - if current_user && can?(current_user, :manage_builds, @project) - - if build.cancel_url - = link_to build.cancel_url, title: 'Cancel' do - %i.fa.fa-remove.cred diff --git a/app/views/projects/builds/_header_title.html.haml b/app/views/projects/builds/_header_title.html.haml new file mode 100644 index 00000000000..082dab1f5b0 --- /dev/null +++ b/app/views/projects/builds/_header_title.html.haml @@ -0,0 +1 @@ +- header_title project_title(@project, "Builds", project_builds_path(@project)) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index e08556673ed..dab7164153f 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -1,12 +1,12 @@ - page_title "Builds" -- header_title project_title(@project, "Builds", project_builds_path(@project)) += render "header_title" .project-issuable-filter .controls - if @ci_project && current_user && can?(current_user, :manage_builds, @project) .pull-left.hidden-xs - if @all_builds.running_or_pending.any? - = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger' + = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post %ul.center-top-menu %li{class: ('active' if @scope.nil?)} @@ -25,7 +25,7 @@ %span.badge.js-totalbuilds-count= @all_builds.count(:id) .gray-content-block - List of #{@scope || 'running'} builds from this project + #{(@scope || 'running').capitalize} builds from this project %ul.content-list - if @builds.blank? @@ -40,14 +40,14 @@ %th Build ID %th Commit %th Ref - %th Runner + %th Stage %th Name %th Duration %th Finished at %th - @builds.each do |build| - = render 'projects/builds/build', build: build + = render 'projects/commit_statuses/commit_status', commit_status: build, commit_sha: true, stage: true, allow_retry: true - = paginate @builds + = paginate @builds, theme: 'gitlab' diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index e3d8d734913..3374d5432a5 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -1,10 +1,13 @@ +- page_title "#{@build.name} (#{@build.id})", "Builds" += render "header_title" + .build-page .gray-content-block - Build for commit + Build ##{@build.id} for commit %strong.monospace = link_to @build.commit.short_sha, ci_status_path(@build.commit) from - %code #{@build.ref} + = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) #up-build-trace - if @commit.matrix_for_ref?(@build.ref) @@ -20,7 +23,7 @@ = build.id - - unless @commit.latest_builds_for_ref(@build.ref).include?(@build) + - if @build.retried? %li.active %a Build ##{@build.id} @@ -37,7 +40,7 @@ %i.fa.fa-time #{duration_in_words(@build.finished_at, @build.started_at)} .pull-right - = @build.updated_at.stamp('19:00 Aug 27') + #{time_ago_with_tooltip(@build.finished_at) if @build.finished_at} - if @build.show_warning? - unless @build.any_runners_online? @@ -87,13 +90,13 @@ .build-widget %h4.title - Build + Build ##{@build.id} - if current_user && can?(current_user, :manage_builds, @project) .pull-right - - if @build.active? - = 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_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post + - if @build.cancel_url + = link_to "Cancel", @build.cancel_url, class: 'btn btn-sm btn-danger', method: :post + - elsif @build.retry_url + = link_to "Retry", @build.retry_url, class: 'btn btn-sm btn-primary', method: :post - if @build.duration %p @@ -101,15 +104,15 @@ #{duration_in_words(@build.finished_at, @build.started_at)} %p %span.attr-name Created: - #{time_ago_in_words(@build.created_at)} ago + #{time_ago_with_tooltip(@build.created_at)} - if @build.finished_at %p %span.attr-name Finished: - #{time_ago_in_words(@build.finished_at)} ago + #{time_ago_with_tooltip(@build.finished_at)} %p %span.attr-name Runner: - if @build.runner && current_user && current_user.admin - \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)} + = link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id) - elsif @build.runner \##{@build.runner.id} @@ -134,10 +137,11 @@ %h4.title Commit .pull-right - %small #{build_commit_link @build} + %small + = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" %p %span.attr-name Branch: - #{build_ref_link @build} + = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) %p %span.attr-name Author: #{@build.commit.git_author_name} @@ -155,7 +159,9 @@ - if @builds.present? .build-widget - %h4.title #{pluralize(@builds.count(:id), "other build")} for #{@build.short_sha}: + %h4.title #{pluralize(@builds.count(:id), "other build")} for + = succeed ":" do + = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" %table.table.builds - @builds.each_with_index do |build, i| %tr.build @@ -171,8 +177,5 @@ %td.status= build.status - = paginate @builds - - :javascript new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}") diff --git a/app/views/projects/ci_services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml index bcc5832792f..dacb6b4f6f4 100644 --- a/app/views/projects/ci_services/edit.html.haml +++ b/app/views/projects/ci_services/edit.html.haml @@ -1 +1,2 @@ +- page_title @service.title, "CI Services" = render 'form' diff --git a/app/views/projects/ci_services/index.html.haml b/app/views/projects/ci_services/index.html.haml index c164b2d4bc0..3f26c7851d8 100644 --- a/app/views/projects/ci_services/index.html.haml +++ b/app/views/projects/ci_services/index.html.haml @@ -1,3 +1,4 @@ +- page_title "CI Services" %h3.page-title Project services %p.light Project services allow you to integrate GitLab CI with other applications diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml index eedf484bf00..665556f5c20 100644 --- a/app/views/projects/ci_settings/edit.html.haml +++ b/app/views/projects/ci_settings/edit.html.haml @@ -1,3 +1,4 @@ +- page_title "CI Settings" - if @ci_project.generated_yaml_config %p.alert.alert-danger CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_project)} diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml index 369086b39ed..2998fb08ff1 100644 --- a/app/views/projects/ci_web_hooks/index.html.haml +++ b/app/views/projects/ci_web_hooks/index.html.haml @@ -1,3 +1,4 @@ +- page_title "CI Web Hooks" %h3.page-title CI Web hooks diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index a634ae5dfda..c73ba74f5ef 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -2,6 +2,8 @@ = nav_link(path: 'commit#show') do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do Changes - = nav_link(path: 'commit#ci') do - = link_to ci_namespace_project_commit_path(@project.namespace, @project, @commit.id) do + %span.badge= @diffs.count + = nav_link(path: 'commit#builds') do + = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do Builds + %span.badge= @builds.count(:id) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index fbf0a9ec0c3..a6458b84860 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -19,7 +19,7 @@ %p %span.light Commit - = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit) + = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" .commit-info-row %span.light Authored by %strong @@ -36,7 +36,7 @@ .commit-info-row %span.cgray= pluralize(@commit.parents.count, "parent") - @commit.parents.each do |parent| - = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent) + = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace" - if @ci_commit .pull-right diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml new file mode 100644 index 00000000000..f7a89232e08 --- /dev/null +++ b/app/views/projects/commit/builds.html.haml @@ -0,0 +1,72 @@ +- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits" += render "projects/commits/header_title" += render "commit_box" += render "ci_menu" + + +- if @ci_commit.yaml_errors.present? + .bs-callout.bs-callout-danger + %h4 Found errors in your .gitlab-ci.yml: + %ul + - @ci_commit.yaml_errors.split(",").each do |error| + %li= error + +- unless @ci_commit.ci_yaml_file + .bs-callout.bs-callout-warning + \.gitlab-ci.yml not found in this commit + +.gray-content-block.second-block + Latest builds + + .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.latest.failed.any?(&:retryable?) + = link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-primary', method: :post + + - if @ci_commit.builds.running_or_pending.any? + = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger', method: :post + +.table-holder + %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, + locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true } + +- if @ci_commit.retried.any? + .gray-content-block.second-block + Retried builds + + .table-holder + %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 + = 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/ci.html.haml b/app/views/projects/commit/ci.html.haml deleted file mode 100644 index 43033cad24c..00000000000 --- a/app/views/projects/commit/ci.html.haml +++ /dev/null @@ -1,69 +0,0 @@ -- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" -= render "projects/commits/header_title" -= render "commit_box" -= render "ci_menu" - - -- if @ci_commit.yaml_errors.present? - .bs-callout.bs-callout-danger - %h4 Found errors in your .gitlab-ci.yml: - %ul - - @ci_commit.yaml_errors.split(",").each do |error| - %li= error - -- unless @ci_commit.ci_yaml_file - .bs-callout.bs-callout-warning - \.gitlab-ci.yml not found in this commit - -.gray-content-block.second-block - Latest builds - - .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-holder - %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, - locals: { coverage: @ci_project.try(:coverage_enabled?), allow_retry: true } - -- if @ci_commit.retried.any? - .gray-content-block.second-block - Retried builds - - .table-holder - %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 - = 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 637154f56aa..c255559b88c 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -12,14 +12,30 @@ - if commit_status.show_warning? %i.fa.fa-warning.text-warning - %td - = commit_status.ref + - if defined?(commit_sha) && commit_sha + %td + = link_to commit_status.short_sha, namespace_project_commit_path(@project.namespace, @project, commit_status.sha), class: "monospace" %td - = commit_status.stage + - if commit_status.ref + = link_to commit_status.ref, namespace_project_commits_path(@project.namespace, @project, commit_status.ref) + - else + .light none + + - if defined?(runner) && runner + %td + - if commit_status.try(:runner) + = runner_link(commit_status.runner) + - else + .light none + + - if defined?(stage) && stage + %td + = commit_status.stage %td = commit_status.name + .pull-right - if commit_status.tags.any? - commit_status.tags.each do |tag| @@ -36,7 +52,7 @@ %td.timestamp - if commit_status.finished_at - %span #{time_ago_in_words commit_status.finished_at} ago + %span #{time_ago_with_tooltip(commit_status.finished_at)} - if defined?(coverage) && coverage %td.coverage @@ -46,9 +62,10 @@ %td .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 + - if commit_status.active? + - if commit_status.cancel_url + = link_to commit_status.cancel_url, method: :post, title: 'Cancel' do + %i.fa.fa-remove.cred - 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/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index 66851d38316..dde9e448cb9 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -1,3 +1,5 @@ +- page_title "Edit", "#{@runner.description} ##{@runner.id}", "Runners" + %h4 Runner ##{@runner.id} %hr = form_for @runner, url: runner_path(@runner), html: { class: 'form-horizontal' } do |f| diff --git a/app/views/projects/runners/index.html.haml b/app/views/projects/runners/index.html.haml index 529fb9c296d..315afe4a764 100644 --- a/app/views/projects/runners/index.html.haml +++ b/app/views/projects/runners/index.html.haml @@ -1,3 +1,4 @@ +- page_title "Runners" .light %p A 'runner' is a process which runs a build. diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml index c255cd51bd2..5bf4c09ca25 100644 --- a/app/views/projects/runners/show.html.haml +++ b/app/views/projects/runners/show.html.haml @@ -1,13 +1,14 @@ -= content_for :title do - %h3.project-title - Runner ##{@runner.id} - .pull-right - - if @runner.shared? - %span.runner-state.runner-state-shared - Shared - - else - %span.runner-state.runner-state-specific - Specific +- page_title "#{@runner.description} ##{@runner.id}", "Runners" + +%h3.page-title + Runner ##{@runner.id} + .pull-right + - if @runner.shared? + %span.runner-state.runner-state-shared + Shared + - else + %span.runner-state.runner-state-specific + Specific .table-holder %table.table diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index 18a37302c3e..b3ad79a200e 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -1,3 +1,4 @@ +- page_title "Triggers" %h3.page-title Triggers diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index 29416a94ff6..e052da1ac43 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -1,3 +1,4 @@ +- page_title "Variables" %h3.page-title Secret Variables diff --git a/config/routes.rb b/config/routes.rb index 0458f538eb6..990a00e3d0b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -472,8 +472,9 @@ Gitlab::Application.routes.draw do resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do member do get :branches - get :ci - get :cancel_builds + get :builds + post :cancel_builds + post :retry_builds end end @@ -588,12 +589,12 @@ Gitlab::Application.routes.draw do resources :builds, only: [:index, :show] do collection do - get :cancel_all + post :cancel_all end member do - get :cancel get :status + post :cancel post :retry end end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 154857e77fe..158e85e598f 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -47,10 +47,11 @@ describe "Builds" do end end - describe "GET /:project/builds/:id/cancel_all" do + describe "POST /:project/builds/:id/cancel_all" do before do @build.run! - visit cancel_all_namespace_project_builds_path(@gl_project.namespace, @gl_project) + visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + click_link "Cancel all" end it { expect(page).to have_content 'No builds to show' } @@ -67,10 +68,11 @@ describe "Builds" do it { expect(page).to have_content @commit.git_author_name } end - describe "GET /:project/builds/:id/cancel" do + describe "POST /:project/builds/:id/cancel" do before do @build.run! - visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + click_link "Cancel" end it { expect(page).to have_content 'canceled' } @@ -79,7 +81,9 @@ describe "Builds" do describe "POST /:project/builds/:id/retry" do before do - visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + @build.run! + visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + click_link "Cancel" click_link 'Retry' end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 1adc2cdf70a..340924fafe7 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -32,7 +32,7 @@ describe "Commits" do describe "Cancel all builds" do it "cancels commit" do visit ci_status_path(@commit) - click_on "Cancel all" + click_on "Cancel running" expect(page).to have_content "canceled" end end diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index 842089ebe0d..b9006b693b2 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -39,7 +39,7 @@ describe GitlabCiService do end describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/ci")} + it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/builds")} end describe "execute" do -- cgit v1.2.1 From 2becd53015c5b283d8c5bfc6b8702b22416e55d2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 3 Nov 2015 14:24:00 +0100 Subject: Add missing stage to builds view --- app/views/projects/commit/builds.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml index f7a89232e08..00cf9c76102 100644 --- a/app/views/projects/commit/builds.html.haml +++ b/app/views/projects/commit/builds.html.haml @@ -69,4 +69,4 @@ %th Coverage %th = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, - locals: { coverage: @ci_project.try(:coverage_enabled?) } + locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true } -- cgit v1.2.1 From aecc4376e20b3ad23662f6181a776ac5747baa06 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 5 Nov 2015 16:41:03 +0200 Subject: make migrations reversible --- db/migrate/20151019111551_fix_build_tags.rb | 6 +++++- db/migrate/20151019111703_fail_build_without_names.rb | 5 ++++- db/migrate/20151023112551_fail_build_with_empty_name.rb | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb index 84b142183f8..299a24b0a7c 100644 --- a/db/migrate/20151019111551_fix_build_tags.rb +++ b/db/migrate/20151019111551_fix_build_tags.rb @@ -1,5 +1,9 @@ class FixBuildTags < ActiveRecord::Migration - def change + def up execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'") end + + def down + execute("UPDATE taggings SET taggable_type='Ci::Build' WHERE taggable_type='CommitStatus'") + end end diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb index 546b03d8129..dcdb5d1b25d 100644 --- a/db/migrate/20151019111703_fail_build_without_names.rb +++ b/db/migrate/20151019111703_fail_build_without_names.rb @@ -1,5 +1,8 @@ class FailBuildWithoutNames < ActiveRecord::Migration - def change + def up execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'") end + + def down + end end diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb index f069bc60ac7..41c0f0649cd 100644 --- a/db/migrate/20151023112551_fail_build_with_empty_name.rb +++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb @@ -1,5 +1,8 @@ class FailBuildWithEmptyName < ActiveRecord::Migration - def change + def up execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'") end + + def down + end end -- cgit v1.2.1 From 6fcd5127a316ce3abf1e5c5dde5b1901e8c7941f Mon Sep 17 00:00:00 2001 From: Thirumal S Date: Thu, 5 Nov 2015 20:21:46 +0530 Subject: defined class prepend-top-10 used for alignment issue --- app/assets/stylesheets/pages/projects.scss | 4 ---- app/views/projects/hooks/index.html.haml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 2e7ad1173a5..d3b10040022 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -552,8 +552,4 @@ pre.light-well { z-index: 100; position: relative; } -} - -.form-control-padding-top { - padding-top: 10px; } \ No newline at end of file diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 65e00b38ad4..3702aeaecba 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -19,7 +19,7 @@ = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' .form-group = f.label :url, "Trigger", class: 'control-label' - .col-sm-10.form-control-padding-top + .col-sm-10.prepend-top-10 %div = f.check_box :push_events, class: 'pull-left' .prepend-left-20 -- cgit v1.2.1 From b18671a1b2c565a87663544441000063f6b83c8e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 3 Nov 2015 14:45:41 +0100 Subject: Enable shared runners for all new projects --- CHANGELOG | 1 + app/controllers/admin/application_settings_controller.rb | 1 + app/models/application_setting.rb | 3 ++- app/models/project.rb | 5 ++++- app/views/admin/application_settings/_form.html.haml | 9 +++++++++ config/initializers/1_settings.rb | 9 +++++---- db/migrate/20151103133339_add_shared_runners_setting.rb | 5 +++++ db/schema.rb | 3 ++- lib/gitlab/current_settings.rb | 3 ++- 9 files changed, 31 insertions(+), 8 deletions(-) create mode 100644 db/migrate/20151103133339_add_shared_runners_setting.rb diff --git a/CHANGELOG b/CHANGELOG index 3a75f50e9a2..22012211164 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.2.0 (unreleased) - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page - Extend yml syntax for only and except to support specifying repository path + - Enable shared runners to all new projects - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 039f18f23e0..3d9c59050ff 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -57,6 +57,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :version_check_enabled, :admin_notification_email, :user_oauth_applications, + :shared_runners_enabled, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 05430c2ee18..266045f7afa 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -87,7 +87,8 @@ class ApplicationSetting < ActiveRecord::Base default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], - import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] + import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], + shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], ) end diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..57db7f9f0a4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -37,6 +37,7 @@ class Project < ActiveRecord::Base include Gitlab::ConfigHelper include Gitlab::ShellAdapter include Gitlab::VisibilityLevel + include Gitlab::CurrentSettings include Referable include Sortable include AfterCommitQueue @@ -775,7 +776,9 @@ class Project < ActiveRecord::Base end def ensure_gitlab_ci_project - gitlab_ci_project || create_gitlab_ci_project + gitlab_ci_project || create_gitlab_ci_project( + shared_runners_enabled: current_application_settings.shared_runners_enabled + ) end def enable_ci diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 7a78526e09a..7253218c2e9 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -130,5 +130,14 @@ = f.text_area :help_page_text, class: 'form-control', rows: 4 .help-block Markdown enabled + %fieldset + %legend Continuous Integration + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :shared_runners_enabled do + = f.check_box :shared_runners_enabled + Enable shared runners for a new projects + .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index c189c88bdcb..64d73d7232d 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -181,10 +181,11 @@ Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious' # CI # Settings['gitlab_ci'] ||= Settingslogic.new({}) -Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? -Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? -Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) -Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) +Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['shared_runners_enabled'].nil? +Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? +Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? +Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) +Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) # # Reply by email diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb new file mode 100644 index 00000000000..4231dfd5c2e --- /dev/null +++ b/db/migrate/20151103133339_add_shared_runners_setting.rb @@ -0,0 +1,5 @@ +class AddSharedRunnersSetting < ActiveRecord::Migration + def up + add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 17d445a8baa..de896f2764b 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: 20151103001141) do +ActiveRecord::Schema.define(version: 20151103133339) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -47,6 +47,7 @@ ActiveRecord::Schema.define(version: 20151103001141) do t.text "import_sources" t.text "help_page_text" t.string "admin_notification_email" + t.boolean "shared_runners_enabled", default: true, null: false end create_table "audit_events", force: true do |t| diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 0ea1b6a2f6f..cd84afa31d5 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -23,7 +23,8 @@ module Gitlab restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], max_attachment_size: Settings.gitlab['max_attachment_size'], session_expire_delay: Settings.gitlab['session_expire_delay'], - import_sources: Settings.gitlab['import_sources'] + import_sources: Settings.gitlab['import_sources'], + shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], ) end -- cgit v1.2.1 From 900419c43c5a540cde22f5488675121b3ce05d31 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 17:08:47 +0100 Subject: Improve UI for tags page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/tags_controller.rb | 10 +++------- app/views/projects/tags/_download.html.haml | 17 +++++++++++++++++ app/views/projects/tags/_tag.html.haml | 8 +++----- app/views/projects/tags/destroy.js.haml | 3 --- app/views/projects/tags/show.html.haml | 22 ++++++++++------------ 5 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 app/views/projects/tags/_download.html.haml delete mode 100644 app/views/projects/tags/destroy.js.haml diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 055f328677f..670f5d3067b 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -39,13 +39,9 @@ class Projects::TagsController < Projects::ApplicationController def destroy DeleteTagService.new(project, current_user).execute(params[:id]) + release = project.releases.find_by(tag: params[:id]) + release.destroy if release - respond_to do |format| - format.html do - redirect_to namespace_project_tags_path(@project.namespace, - @project) - end - format.js - end + redirect_to namespace_project_tags_path(@project.namespace, @project) end end diff --git a/app/views/projects/tags/_download.html.haml b/app/views/projects/tags/_download.html.haml new file mode 100644 index 00000000000..667057ef2d8 --- /dev/null +++ b/app/views/projects/tags/_download.html.haml @@ -0,0 +1,17 @@ +%span.btn-group.btn-grouped + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), class: 'btn btn-default', rel: 'nofollow' do + %i.fa.fa-download + %span source code + %a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %span.caret + %span.sr-only + Select Archive Format + %ul.col-xs-10.dropdown-menu{ role: 'menu' } + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'zip'), rel: 'nofollow' do + %i.fa.fa-download + %span Download zip + %li + = link_to archive_namespace_project_repository_path(project.namespace, project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do + %i.fa.fa-download + %span Download tar.gz diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 5d203903f8b..f3dc314a894 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -4,22 +4,20 @@ %div = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do %strong - %i.fa.fa-tag + = icon('tag') = tag.name - if tag.message.present?   = strip_gpg_signature(tag.message) - if release +   %span.label.label-success release .controls = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do = icon("pencil") - if can? current_user, :download_code, @project - = render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-xs' - - if can?(current_user, :admin_project, @project) - = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-xs btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do - %i.fa.fa-trash-o + = render 'projects/tags/download', ref: tag.name, project: @project - if commit = render 'projects/branches/commit', commit: commit, project: @project diff --git a/app/views/projects/tags/destroy.js.haml b/app/views/projects/tags/destroy.js.haml deleted file mode 100644 index ada6710f940..00000000000 --- a/app/views/projects/tags/destroy.js.haml +++ /dev/null @@ -1,3 +0,0 @@ -$('.js-totaltags-count').html("#{@repository.tags.size}") -- if @repository.tags.size == 0 - $('.tags').load(document.URL + ' .nothing-here-block').hide().fadeIn(1000) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index d2f23b96a8c..22697464fd2 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -6,6 +6,16 @@ .pull-right = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn' do = icon("pencil") + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + = icon('files-o') + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + = icon('history') + - if can? current_user, :download_code, @project + = render 'projects/tags/download', ref: @tag.name, project: @project + - if can?(current_user, :admin_project, @project) + .pull-right + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do + %i.fa.fa-trash-o - if @tag.message.present? .title %strong= @tag.name @@ -24,18 +34,6 @@ - else This tag has no release notes yet. Press edit button to add one -.gray-content-block.middle-block.clearfix - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do - Browse code - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do - Commits - - if can? current_user, :download_code, @project - = render 'projects/repositories/download_archive', ref: @tag.name, btn_class: 'btn-grouped' - - if can?(current_user, :admin_project, @project) - .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do - %i.fa.fa-trash-o - .gray-content-block.second-block - if @commit = render 'projects/commits/commit', commit: @commit, project: @project -- cgit v1.2.1 From 3c0244d3d1533dab3276465a63a26e0937aef543 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 5 Nov 2015 18:37:31 +0100 Subject: Retyle tag show page Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/tags/show.html.haml | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 22697464fd2..f80ba5516a0 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -16,14 +16,17 @@ .pull-right = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do %i.fa.fa-trash-o - - if @tag.message.present? - .title - %strong= @tag.name - = strip_gpg_signature(@tag.message) + .title + %strong= @tag.name + - if @tag.message.present? + %span.light +   + = strip_gpg_signature(@tag.message) + - if @commit + = render 'projects/branches/commit', commit: @commit, project: @project - else - .oneline - .title - %strong= @tag.name + Cant find HEAD commit for this tag + .append-bottom-default.prepend-top-default - if @release.description.present? @@ -33,10 +36,3 @@ = markdown @release.description - else This tag has no release notes yet. Press edit button to add one - -.gray-content-block.second-block - - if @commit - = render 'projects/commits/commit', commit: @commit, project: @project - - else - Cant find HEAD commit for this tag - -- cgit v1.2.1 From c55b5c72e94344d138af7de198016c8ea54b80f8 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 5 Nov 2015 21:09:02 +0200 Subject: Remove duplicate documentation links --- doc/README.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/doc/README.md b/doc/README.md index a0ff856ebb6..3cd98fab0d5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -17,20 +17,20 @@ ## CI Documentation -+ [Quick Start](ci/quick_start/README.md) -+ [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) -+ [Configuring runner](ci/runners/README.md) -+ [Configuring deployment](ci/deployment/README.md) -+ [Using Docker Images](ci/docker/using_docker_images.md) -+ [Using Docker Build](ci/docker/using_docker_build.md) -+ [Using Variables](ci/variables/README.md) +- [Quick Start](ci/quick_start/README.md) +- [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) +- [Configuring runner](ci/runners/README.md) +- [Configuring deployment](ci/deployment/README.md) +- [Using Docker Images](ci/docker/using_docker_images.md) +- [Using Docker Build](ci/docker/using_docker_build.md) +- [Using Variables](ci/variables/README.md) ### CI Examples -+ [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) -+ [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md) -+ [Test Clojure applications](ci/examples/test-clojure-application.md) -+ Help your favorite programming language and GitLab by sending a merge request with a guide for that language. +- [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) +- [Test and deploy Python applications to Heroku](ci/examples/test-and-deploy-python-application-to-heroku.md) +- [Test Clojure applications](ci/examples/test-clojure-application.md) +- Help your favorite programming language and GitLab by sending a merge request with a guide for that language. ## Administrator documentation @@ -49,11 +49,6 @@ - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. -### Administrator documentation - -+ [User permissions](permissions/permissions.md) -+ [API](api/README.md) - ## Contributor documentation - [Development](development/README.md) Explains the architecture and the guidelines for shell commands. -- cgit v1.2.1 From 5f696d0c22dd367fb4687b2d18f8db92e179bbcc Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 5 Nov 2015 21:21:12 +0200 Subject: Add CI permissions and api links to README [ci skip] --- doc/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/README.md b/doc/README.md index 3cd98fab0d5..0f6866475f7 100644 --- a/doc/README.md +++ b/doc/README.md @@ -24,6 +24,8 @@ - [Using Docker Images](ci/docker/using_docker_images.md) - [Using Docker Build](ci/docker/using_docker_build.md) - [Using Variables](ci/variables/README.md) +- [User permissions](ci/permissions/README.md) +- [API](ci/api/README.md) ### CI Examples -- cgit v1.2.1 From c468461579f0dedc0ad92609e6bb54624c2e2b04 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Thu, 5 Nov 2015 21:17:15 -0800 Subject: Only have one link in emails to make clicking it easier. --- app/views/layouts/notify.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 854cda57c39..f58b9bd6ba6 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -40,9 +40,9 @@ Reply to this email directly or #{link_to "view it on GitLab", @target_url}. - else - #{link_to "View it on GitLab", @target_url} + #{link_to "View it on GitLab", @target_url}. %br - You're receiving this email because of your account on #{link_to Gitlab.config.gitlab.host, root_url}. + You're receiving this email because of your account on #{Gitlab.config.gitlab.host}. If you'd like to receive fewer emails, you can adjust your notification settings. = email_action @target_url -- cgit v1.2.1 From 40a9b1ce00ed4e4f03431b731ca5a6716356d5a6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Nov 2015 23:47:04 -0800 Subject: Fix bug where manually merged branches in a MR would end up with an empty diff Closes #3314 --- CHANGELOG | 1 + app/services/merge_requests/refresh_service.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 16055208db5..3614d790361 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.2.0 (unreleased) + - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses - Improved performance of replacing references in comments diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index d68bc79ecc0..e180edb4bf3 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -7,17 +7,17 @@ module MergeRequests @branch_name = Gitlab::Git.ref_name(ref) find_new_commits + # Be sure to close outstanding MRs before reloading them to avoid generating an + # empty diff during a manual merge + close_merge_requests reload_merge_requests # Leave a system note if a branch was deleted/added if branch_added? || branch_removed? comment_mr_branch_presence_changed - comment_mr_with_commits - else - comment_mr_with_commits - close_merge_requests end + comment_mr_with_commits execute_mr_web_hooks true -- cgit v1.2.1 From cea2afa85ed81abfa809a622ca3a548f770ad228 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 14:14:43 +0100 Subject: Add changelog item and remove release labels for tags list Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/projects/tags/_tag.html.haml | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0ec6030b130..a7a9e073ae7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -22,6 +22,7 @@ v 8.2.0 (unreleased) - Include commit logs in project search - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from + - Ability to add release notes (markdown text and attachments) to git tags v 8.1.3 - Spread out runner contacted_at updates diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index f3dc314a894..e2c5178185e 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -9,9 +9,6 @@ - if tag.message.present?   = strip_gpg_signature(tag.message) - - if release -   - %span.label.label-success release .controls = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do -- cgit v1.2.1 From ed757ef6e3aab55c8fe0f2c1efdd79d65025c550 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 14:44:08 +0100 Subject: Rewrite remove tag test Signed-off-by: Dmitriy Zaporozhets --- features/project/commits/tags.feature | 9 +-------- features/steps/project/commits/tags.rb | 25 ++++++------------------- 2 files changed, 7 insertions(+), 27 deletions(-) diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature index 02f399f7cad..24fb84d1cc9 100644 --- a/features/project/commits/tags.feature +++ b/features/project/commits/tags.feature @@ -29,13 +29,6 @@ Feature: Project Commits Tags @javascript Scenario: I delete a tag + Given I visit tag 'v1.1.0' page Given I delete tag 'v1.1.0' Then I should not see tag 'v1.1.0' - - @javascript - Scenario: I delete all tags and see info message - Given I delete all tags - Then I should see tags info message - - # @wip - # Scenario: I can download project by tag diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb index e6f8faf50fd..ff824c76955 100644 --- a/features/steps/project/commits/tags.rb +++ b/features/steps/project/commits/tags.rb @@ -52,11 +52,13 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps expect(page).to have_content 'Tag already exists' end + step "I visit tag 'v1.1.0' page" do + click_link 'v1.1.0' + end + step "I delete tag 'v1.1.0'" do - page.within '.tags' do - first('.btn-remove').click - sleep 0.05 - end + first('.btn-remove').click + sleep 0.05 end step "I should not see tag 'v1.1.0'" do @@ -64,19 +66,4 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps expect(page.all(visible: true)).not_to have_content 'v1.1.0' end end - - step 'I delete all tags' do - page.within '.tags' do - page.all('.btn-remove').each do |remove| - remove.click - sleep 0.05 - end - end - end - - step 'I should see tags info message' do - page.within '.tags' do - expect(page).to have_content 'Repository has no tags yet.' - end - end end -- cgit v1.2.1 From c1a893d91a569cf65b381e8c2b56c82824e9f593 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 14:44:46 +0100 Subject: Fix list rendering issue when dropdown was hidden in row Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 2 +- app/assets/stylesheets/framework/mixins.scss | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index f6942db5816..45f3b5849bf 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: 4px; + padding-top: 1px; float: right; .btn { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index b9c179f2881..11c48d26ab5 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -72,9 +72,10 @@ list-style: none; > li { + @include clearfix; + padding: 10px 0; border-bottom: 1px solid #EEE; - overflow: hidden; display: block; margin: 0px; -- cgit v1.2.1 From b60ad399acdd753efe4f2d724c47800b6a70056b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 14:47:29 +0100 Subject: Fix test Signed-off-by: Dmitriy Zaporozhets --- features/project/commits/tags.feature | 1 - features/steps/project/commits/tags.rb | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature index 24fb84d1cc9..660238c2319 100644 --- a/features/project/commits/tags.feature +++ b/features/project/commits/tags.feature @@ -27,7 +27,6 @@ Feature: Project Commits Tags And I submit new tag form with tag that already exists Then I should see new an error that tag already exists - @javascript Scenario: I delete a tag Given I visit tag 'v1.1.0' page Given I delete tag 'v1.1.0' diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb index ff824c76955..cb5fe20834d 100644 --- a/features/steps/project/commits/tags.rb +++ b/features/steps/project/commits/tags.rb @@ -57,13 +57,14 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps end step "I delete tag 'v1.1.0'" do - first('.btn-remove').click - sleep 0.05 + page.within('.content') do + first('.btn-remove').click + end end step "I should not see tag 'v1.1.0'" do page.within '.tags' do - expect(page.all(visible: true)).not_to have_content 'v1.1.0' + expect(page).not_to have_link 'v1.1.0' end end end -- cgit v1.2.1 From 962b57df5a3f476407858b12455030d8cda66e4b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 15:05:19 +0100 Subject: Small UI improvements to new git tag page Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/releases/edit.html.haml | 4 +++- app/views/projects/tags/new.html.haml | 14 ++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index fb841b77a25..78741416347 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -3,7 +3,9 @@ .gray-content-block .oneline - Release notes for #{@tag.name} + .title + Release notes for tag + %strong #{@tag.name} .prepend-top-default = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f| diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 9b224ff89b0..be8f2e6f70e 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -5,9 +5,11 @@ .alert.alert-danger %button{ type: "button", class: "close", "data-dismiss" => "alert"} × = @error + %h3.page-title - %i.fa.fa-code-fork - New tag + New git tag +%hr + = form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' @@ -17,15 +19,15 @@ = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' - .light Branch name or commit SHA + .help-block Branch name or commit SHA .form-group = label_tag :message, 'Message', class: 'control-label' .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' - .light (Optional) Entering a message will create an annotated tag. + .help-block (Optional) Entering a message will create an annotated tag. %hr .form-group - = label_tag :release_description, 'Release description', class: 'control-label' + = label_tag :release_description, 'Release notes', class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do .zennable @@ -39,7 +41,7 @@ = icon('compress') = render 'projects/notes/hints' - .help-block You can add release description to your tag. It will be stored in GitLab database and displayed on tags page + .help-block (Optional) You can add release notes to your tag. It will be stored in GitLab database and displayed on tags page .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' -- cgit v1.2.1 From cb8b9c3fe2caba14752978f4283affc07581087c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 15:29:04 +0100 Subject: Add association and validation for Release model Signed-off-by: Dmitriy Zaporozhets --- app/models/release.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/models/release.rb b/app/models/release.rb index 1dc9ce6dd4f..05647839e84 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,2 +1,5 @@ class Release < ActiveRecord::Base + belongs_to :project + + validates :description, :project, presence: true end -- cgit v1.2.1 From 6f15356aea488ce0085c982aac2c97cdd46db96b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 15:43:59 +0100 Subject: Add tests to release notes feature Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/tags/show.html.haml | 6 +++--- features/project/commits/tags.feature | 12 ++++++++++++ features/steps/project/commits/tags.rb | 20 ++++++++++++++++++++ spec/factories/releases.rb | 6 +++--- spec/models/release_spec.rb | 13 ++++++++++++- 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index f80ba5516a0..f95ae9edc4b 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -4,11 +4,11 @@ .gray-content-block .pull-right - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn' do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped' do + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do = icon('history') - if can? current_user, :download_code, @project = render 'projects/tags/download', ref: @tag.name, project: @project diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature index 660238c2319..56ee091acc0 100644 --- a/features/project/commits/tags.feature +++ b/features/project/commits/tags.feature @@ -12,6 +12,12 @@ Feature: Project Commits Tags And I submit new tag form Then I should see new tag created + Scenario: I create a tag with release notes + Given I click new tag link + And I submit new tag form with release notes + Then I should see new tag created + And I should see tag release notes + Scenario: I create a tag with invalid name And I click new tag link And I submit new tag form with invalid name @@ -31,3 +37,9 @@ Feature: Project Commits Tags Given I visit tag 'v1.1.0' page Given I delete tag 'v1.1.0' Then I should not see tag 'v1.1.0' + + Scenario: I add release notes to the tag + Given I visit tag 'v1.1.0' page + When I click edit tag link + And I fill release notes and submit form + Then I should see tag release notes diff --git a/features/steps/project/commits/tags.rb b/features/steps/project/commits/tags.rb index cb5fe20834d..eff4234a44a 100644 --- a/features/steps/project/commits/tags.rb +++ b/features/steps/project/commits/tags.rb @@ -18,6 +18,18 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps click_button 'Create tag' end + step 'I submit new tag form with release notes' do + fill_in 'tag_name', with: 'v7.0' + fill_in 'ref', with: 'master' + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Create tag' + end + + step 'I fill release notes and submit form' do + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Save changes' + end + step 'I submit new tag form with invalid name' do fill_in 'tag_name', with: 'v 1.0' fill_in 'ref', with: 'master' @@ -67,4 +79,12 @@ class Spinach::Features::ProjectCommitsTags < Spinach::FeatureSteps expect(page).not_to have_link 'v1.1.0' end end + + step 'I click edit tag link' do + click_link 'Edit release notes' + end + + step 'I should see tag release notes' do + expect(page).to have_content 'Awesome release notes' + end end diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 067d8138e41..80d6bbee6c7 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -2,8 +2,8 @@ FactoryGirl.define do factory :release do - tag "MyString" - description "MyText" - project_id 1 + tag "v1.1.0" + description "Awesome release" + project end end diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index e533734ba0d..527005b2b69 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -1,5 +1,16 @@ require 'rails_helper' RSpec.describe Release, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + let(:release) { create(:release) } + + it { expect(release).to be_valid } + + describe 'associations' do + it { is_expected.to belong_to(:project) } + end + + describe 'validation' do + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:description) } + end end -- cgit v1.2.1 From 14518ba65a7727583445314917d466dabe4a6811 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 15:48:18 +0100 Subject: Better english Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/tags/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index be8f2e6f70e..e106be794f1 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -41,7 +41,7 @@ = icon('compress') = render 'projects/notes/hints' - .help-block (Optional) You can add release notes to your tag. It will be stored in GitLab database and displayed on tags page + .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' -- cgit v1.2.1 From c5ec2a23a466f45127b6056693e1a473ae7e503e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 6 Nov 2015 16:27:18 +0100 Subject: Small UI improvements to merge request page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/blocks.scss | 4 ++++ app/assets/stylesheets/pages/diff.scss | 1 - app/helpers/diff_helper.rb | 4 ++-- app/views/projects/diffs/_diffs.html.haml | 2 +- app/views/projects/merge_requests/_discussion.html.haml | 2 +- app/views/projects/merge_requests/show/_commits.html.haml | 4 ++++ 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 8917c53b1f5..1635df9c97b 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -28,6 +28,10 @@ border-bottom: 1px solid $border-color; color: $gl-gray; + &.oneline-block { + line-height: 42px; + } + &.white { background-color: white; } diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index d9ef06dc6b6..afd6fb73675 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -367,7 +367,6 @@ .inline-parallel-buttons { float: right; - margin-top: -5px; } // Mobile diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index e65e37211c4..b889fb28973 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -137,7 +137,7 @@ module DiffHelper # Always use HTML to handle case where JSON diff rendered this button params_copy.delete(:format) - link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn btn-sm active' : 'btn btn-sm') do + link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do 'Inline' end end @@ -148,7 +148,7 @@ module DiffHelper # Always use HTML to handle case where JSON diff rendered this button params_copy.delete(:format) - link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active btn-sm' : 'btn btn-sm') do + link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do 'Side-by-side' end end diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 56b51f038ba..e46bf1ab1e7 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -3,7 +3,7 @@ - diff_files = safe_diff_files(diffs) -.gray-content-block.second-block +.gray-content-block.second-block.oneline-block .inline-parallel-buttons .btn-group = inline_diff_btn diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 38e66c3828b..7e60782ff5b 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -7,7 +7,7 @@ = render 'shared/show_aside' -.gray-content-block.second-block +.gray-content-block.second-block.oneline-block .row .col-md-9 .votes-holder.pull-right diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index a71b181a6a5..478054db517 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -1 +1,5 @@ +.gray-content-block.second-block.oneline-block + = icon("sort-amount-desc") + Most recent commits displayed first + = render "projects/commits/commits", project: @merge_request.project -- cgit v1.2.1 From 8f2561b193ad39f116655af0789798b45ad906c8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 6 Nov 2015 08:04:02 -0800 Subject: Add spec for manual merge of merge request --- spec/services/merge_requests/refresh_service_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 227ac995ec2..7ee4488521d 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -62,6 +62,25 @@ describe MergeRequests::RefreshService do it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') } end + context 'manual merge of source branch' do + before do + # Merge master -> feature branch + author = { email: 'test@gitlab.com', time: Time.now, name: "Me" } + commit_options = { message: 'Test message', committer: author, author: author } + master_commit = @project.repository.commit('master') + @project.repository.merge(@user, master_commit.id, 'feature', commit_options) + commit = @project.repository.commit('feature') + service.new(@project, @user).execute(@oldrev, commit.id, 'refs/heads/feature') + reload_mrs + end + + it { expect(@merge_request.notes.last.note).to include('changed to merged') } + it { expect(@merge_request).to be_merged } + it { expect(@merge_request.diffs.length).to be > 0 } + it { expect(@fork_merge_request).to be_merged } + it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') } + end + context 'push to fork repo source branch' do let(:refresh_service) { service.new(@fork_project, @user) } before do -- cgit v1.2.1 From e34904370f7383853ed9e1f5392ff76defda9530 Mon Sep 17 00:00:00 2001 From: Baldinof Date: Fri, 6 Nov 2015 23:45:33 +0100 Subject: Merge button has color from CI status --- app/assets/stylesheets/pages/merge_requests.scss | 14 ++++++++++++++ .../projects/merge_requests/widget/open/_accept.html.haml | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index f0b3667acca..08e4bcdf529 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -19,6 +19,20 @@ .accept-merge-holder { .accept-action { display: inline-block; + + .accept_merge_request { + &.ci-pending, + &.ci-running { + @include btn-orange; + } + + &.ci-skipped, + &.ci-failed, + &.ci-canceled, + &.ci-error { + @include btn-red; + } + } } .accept-control { diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 613525437ab..689247f3186 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -1,8 +1,10 @@ +- status_class = @merge_request.ci_commit ? " ci-#{@merge_request.ci_commit.status}" : nil + = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container .accept-action - = f.button class: "btn btn-create accept_merge_request" do + = f.button class: "btn btn-create accept_merge_request#{status_class}" do Accept Merge Request - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork? .accept-control.checkbox -- cgit v1.2.1 From 78171d548158584fe19496fa5113052a9c3cc235 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Fri, 6 Nov 2015 18:25:41 -0800 Subject: Prevent people from adding the link back. --- app/views/layouts/notify.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index f58b9bd6ba6..a02dd955186 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -42,6 +42,7 @@ - else #{link_to "View it on GitLab", @target_url}. %br + -# Don't link the host is the line below, hone link in the email is easier to quickly click than two. You're receiving this email because of your account on #{Gitlab.config.gitlab.host}. If you'd like to receive fewer emails, you can adjust your notification settings. -- cgit v1.2.1 From b14198a5e7520ce7cf356006507db9b190e3afe3 Mon Sep 17 00:00:00 2001 From: Christian Speich Date: Sat, 7 Nov 2015 12:40:14 +0100 Subject: Hide tab-bar in login-box when only one tabs is shown. --- app/views/devise/shared/_signin_box.html.haml | 46 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index 41ad2c231d4..9a1331b2549 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -7,26 +7,34 @@ %h3 Sign in .login-body - if form_based_providers.any? - %ul.nav.nav-tabs + - if form_based_providers.count >= 2 || signin_enabled? + %ul.nav.nav-tabs + - if crowd_enabled? + %li.active + = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab' + - @ldap_servers.each_with_index do |server, i| + %li{class: (:active if i.zero? && !crowd_enabled?)} + = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab' + - if signin_enabled? + %li + = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' + .tab-content + - if crowd_enabled? + %div.tab-pane.active{id: "tab-crowd"} + = render 'devise/sessions/new_crowd' + - @ldap_servers.each_with_index do |server, i| + %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)} + = render 'devise/sessions/new_ldap', server: server + - if signin_enabled? + %div#tab-signin.tab-pane + = render 'devise/sessions/new_base' + - else - if crowd_enabled? - %li.active - = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab' - - @ldap_servers.each_with_index do |server, i| - %li{class: (:active if i.zero? && !crowd_enabled?)} - = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab' - - if signin_enabled? - %li - = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' - .tab-content - - if crowd_enabled? - %div.tab-pane.active{id: "tab-crowd"} - = render 'devise/sessions/new_crowd' - - @ldap_servers.each_with_index do |server, i| - %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)} - = render 'devise/sessions/new_ldap', server: server - - if signin_enabled? - %div#tab-signin.tab-pane - = render 'devise/sessions/new_base' + = render 'devise/sessions/new_crowd' + - elsif @ldap_servers.any? + = render 'devise/sessions/new_ldap', server: @ldap_servers.first + - elsif signin_enabled? + = render 'devise/sessions/new_base' - elsif signin_enabled? = render 'devise/sessions/new_base' -- cgit v1.2.1 From 920a2d974c15662ffac49146348c8d638a9db92b Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Wed, 28 Oct 2015 15:58:40 +0100 Subject: Fixed markdown issue in PROCESS.md --- PROCESS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PROCESS.md b/PROCESS.md index d42168a7231..a4b0c83644b 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -119,6 +119,6 @@ rebase with master to see if that solves the issue. ### Closing down the issue tracker on GitHub We are currently in the process of closing down the issue tracker on GitHub, to -prevent duplication with the [GitLab.com issue tracker][https://gitlab.com/gitlab-org/gitlab-ce/issues]. +prevent duplication with the GitLab.com issue tracker. Since this is an older issue I'll be closing this for now. If you think this is -still an issue I encourage you to open it on the [GitLab.com issue tracker][https://gitlab.com/gitlab-org/gitlab-ce/issues]. +still an issue I encourage you to open it on the \[GitLab.com issue tracker\](https://gitlab.com/gitlab-org/gitlab-ce/issues). -- cgit v1.2.1 From 4099751ce9b33fbafbfcfbf1506e091a20f7c034 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 7 Nov 2015 20:22:16 +0100 Subject: Render same markdown hint for issue, merge request, wiki and comment forms Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/markdown_area.scss | 1 + app/assets/stylesheets/pages/note_form.scss | 6 +++++- app/views/projects/wikis/_form.html.haml | 4 +--- app/views/shared/issuable/_form.html.haml | 9 +-------- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index ed0333d2336..cc660529cb4 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -106,6 +106,7 @@ } .markdown-area { + @include border-radius(0); background: #FFF; border: 1px solid #ddd; min-height: 140px; diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 4392f08942b..268fc995aa7 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -56,6 +56,10 @@ .note_text { width: 100%; } + + .comment-hints { + margin-top: -12px; + } } /* loading indicator */ @@ -168,7 +172,7 @@ color: #999; background: #FFF; padding: 7px; - margin-top: -11px; + margin-top: -7px; border: 1px solid $border-color; font-size: 13px; } diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 261d4a92d7d..9c94c43e747 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -23,9 +23,7 @@ .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 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' }. + = render 'projects/notes/hints' .clearfix .error-alert diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 594e54f404c..0fc74d7d2b1 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -27,14 +27,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 js-quick-submit' - .col-sm-12.hint - .pull-left - 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' }. - + = render 'projects/notes/hints' .clearfix .error-alert %hr -- cgit v1.2.1 From b8cd6f9aae2eb0753c8f36284331aef6b4557bab Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sun, 8 Nov 2015 18:00:31 +0200 Subject: Update piwik template --- app/views/layouts/_piwik.html.haml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml index 135e8daca26..259b4f7cdfc 100644 --- a/app/views/layouts/_piwik.html.haml +++ b/app/views/layouts/_piwik.html.haml @@ -1,12 +1,14 @@ + :javascript var _paq = _paq || []; - _paq.push(["trackPageView"]); - _paq.push(["enableLinkTracking"]); - + _paq.push(['trackPageView']); + _paq.push(['enableLinkTracking']); (function() { - var u=(("https:" == document.location.protocol) ? "https" : "http") + "://#{extra_config.piwik_url}/"; - _paq.push(["setTrackerUrl", u+"piwik.php"]); - _paq.push(["setSiteId", "#{extra_config.piwik_site_id}"]); - var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript"; - g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s); + var u="//#{extra_config.piwik_url}/"; + _paq.push(['setTrackerUrl', u+'piwik.php']); + _paq.push(['setSiteId', #{extra_config.piwik_site_id}]); + var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; + g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s); })(); + + -- cgit v1.2.1 From 2e5d32e07a75b98e57a0476a4cdb7a1ec41cadda Mon Sep 17 00:00:00 2001 From: Pirate Praveen Date: Sun, 8 Nov 2015 12:15:58 -0500 Subject: Switch to state_machines-activerecord (Fixes: #3374) --- Gemfile | 2 +- config/initializers/state_machine_patch.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 0bac8978160..ee8b2a7a51a 100644 --- a/Gemfile +++ b/Gemfile @@ -113,7 +113,7 @@ group :unicorn do end # State machine -gem "state_machine", '~> 1.2.0' +gem "state_machines-activerecord", '~> 0.3.0' # Run events after state machine commits gem 'after_commit_queue' diff --git a/config/initializers/state_machine_patch.rb b/config/initializers/state_machine_patch.rb index 72d010fa5de..51f05794361 100644 --- a/config/initializers/state_machine_patch.rb +++ b/config/initializers/state_machine_patch.rb @@ -1,6 +1,6 @@ # This is a patch to address the issue in https://github.com/pluginaweek/state_machine/issues/251 # where gem 'state_machine' was not working for Rails 4.1 -module StateMachine +module StateMachines module Integrations module ActiveModel public :around_validation -- cgit v1.2.1 From ac5b6c3b50b5220d3fc800508267b320fa7cf78b Mon Sep 17 00:00:00 2001 From: Christophe Poulette Date: Sun, 8 Nov 2015 19:29:22 +0100 Subject: Apply new design for project graphs page --- CHANGELOG | 1 + app/views/projects/graphs/_head.html.haml | 2 +- app/views/projects/graphs/ci.html.haml | 3 +++ app/views/projects/graphs/commits.html.haml | 10 +++++++--- app/views/projects/graphs/show.html.haml | 8 ++++++-- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3a75f50e9a2..6f6bc8a7452 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.2.0 (unreleased) - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from - Allow groups to appear in the search results if the group owner allows it + - New design for project graphs page v 8.1.3 - Spread out runner contacted_at updates diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index bbfaf422a82..e0d06a14bf4 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -1,4 +1,4 @@ -%ul.nav.nav-tabs +%ul.center-top-menu = nav_link(action: :show) do = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml index 4f69cc64f7c..b2dfe97938a 100644 --- a/app/views/projects/graphs/ci.html.haml +++ b/app/views/projects/graphs/ci.html.haml @@ -1,6 +1,9 @@ - page_title "Continuous Integration", "Graphs" = render "header_title" = render 'head' +.gray-content-block + %ul.breadcrumb.repo-breadcrumb + = commits_breadcrumbs #charts.ci-charts = render 'projects/graphs/ci/builds' = render 'projects/graphs/ci/build_times' diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index 112be875b6b..a21d7448654 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,8 +1,12 @@ - page_title "Commits", "Graphs" = render "header_title" -.tree-ref-holder - = render 'shared/ref_switcher', destination: 'graphs_commits' -= render 'head' += reder 'head' + +.gray-content-block + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'graphs_commits' + %ul.breadcrumb.repo-breadcrumb + = commits_breadcrumbs %p.lead Commit statistics for diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index bd342911e49..6bbf15d05a2 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -1,9 +1,13 @@ - page_title "Contributors", "Graphs" = render "header_title" -.tree-ref-holder - = render 'shared/ref_switcher', destination: 'graphs' = render 'head' +.gray-content-block + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'graphs' + %ul.breadcrumb.repo-breadcrumb + = commits_breadcrumbs + .loading-graph .center %h3.page-title -- cgit v1.2.1 From b68a2e6f251cd0af7179eba5ed058fcaab009377 Mon Sep 17 00:00:00 2001 From: Christophe Poulette Date: Sun, 8 Nov 2015 20:15:03 +0100 Subject: Fix typo --- app/views/projects/graphs/commits.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index a21d7448654..838d3e40614 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,6 +1,6 @@ - page_title "Commits", "Graphs" = render "header_title" -= reder 'head' += redner 'head' .gray-content-block .tree-ref-holder -- cgit v1.2.1 From 4ac26c6463b53e2a02a43835e6a919e2bc023fb7 Mon Sep 17 00:00:00 2001 From: Christophe Poulette Date: Sun, 8 Nov 2015 20:31:55 +0100 Subject: Fix typo. --- app/views/projects/graphs/commits.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index 838d3e40614..4e0c3e5b3de 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -1,6 +1,6 @@ - page_title "Commits", "Graphs" = render "header_title" -= redner 'head' += render 'head' .gray-content-block .tree-ref-holder -- cgit v1.2.1 From 4fda9ef8a522352886fad08524b4ad4054a7eb60 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 5 Nov 2015 16:22:37 +0100 Subject: Fix tests --- spec/factories/ci/projects.rb | 2 ++ spec/models/application_setting_spec.rb | 28 ++++++++++++++++++++++--- spec/services/ci/register_build_service_spec.rb | 4 ++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb index 111e1a82816..1183a190353 100644 --- a/spec/factories/ci/projects.rb +++ b/spec/factories/ci/projects.rb @@ -33,6 +33,8 @@ FactoryGirl.define do gl_project factory: :empty_project + shared_runners_enabled false + factory :ci_project do token 'iPWx6WM4lhHNedGfBpPJNP' end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index de0b2ef4cda..f01fe8bd398 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -28,11 +28,11 @@ require 'spec_helper' describe ApplicationSetting, models: true do - it { expect(ApplicationSetting.create_from_defaults).to be_valid } + let(:setting) { ApplicationSetting.create_from_defaults } - context 'restricted signup domains' do - let(:setting) { ApplicationSetting.create_from_defaults } + it { expect(setting).to be_valid } + context 'restricted signup domains' do it 'set single domain' do setting.restricted_signup_domains_raw = 'example.com' expect(setting.restricted_signup_domains).to eq(['example.com']) @@ -53,4 +53,26 @@ describe ApplicationSetting, models: true do expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com']) end end + + context 'shared runners' do + let(:gl_project) { create(:empty_project) } + + before do + allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting) + end + + subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled } + + context 'enabled' do + before { setting.update_attributes(shared_runners_enabled: true) } + + it { is_expected.to be_truthy } + end + + context 'disabled' do + before { setting.update_attributes(shared_runners_enabled: false) } + + it { is_expected.to be_falsey } + end + end end diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 781764627ac..b370dfbe113 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -70,6 +70,10 @@ module Ci end context 'disallow shared runners' do + before do + gl_project.gitlab_ci_project.update(shared_runners_enabled: false) + end + context 'shared runner' do let(:build) { service.execute(shared_runner) } -- cgit v1.2.1 From 3727c9cab2ab48ca6aa2f5cfa3cd3126a02002b1 Mon Sep 17 00:00:00 2001 From: Sullivan SENECHAL Date: Wed, 21 Oct 2015 10:40:05 +0200 Subject: Add GitLabCI code coverage regex sample for PHPUnit --- app/views/projects/ci_settings/_form.html.haml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml index 20bdccc9027..ee6b8885e2d 100644 --- a/app/views/projects/ci_settings/_form.html.haml +++ b/app/views/projects/ci_settings/_form.html.haml @@ -103,8 +103,9 @@ %li pytest-cov (Python) - %code \d+\%\s*$ - - + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% %fieldset %legend Advanced settings -- cgit v1.2.1 From 0fb85939da482e50a7337d8762fb26e75bb16ce5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 9 Nov 2015 14:12:05 +0100 Subject: Fix incoming email config defaults --- CHANGELOG | 1 + config/initializers/1_settings.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 22012211164..4951ae172c2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 8.2.0 (unreleased) - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from - Allow groups to appear in the search results if the group owner allows it + - Fix incoming email config defaults v 8.1.3 - Spread out runner contacted_at updates diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 64d73d7232d..8192d727f2a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -193,8 +193,8 @@ Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_c Settings['incoming_email'] ||= Settingslogic.new({}) Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled'].nil? Settings.incoming_email['port'] = 143 if Settings.incoming_email['port'].nil? -Settings.incoming_email['ssl'] = 143 if Settings.incoming_email['ssl'].nil? -Settings.incoming_email['start_tls'] = 143 if Settings.incoming_email['start_tls'].nil? +Settings.incoming_email['ssl'] = false if Settings.incoming_email['ssl'].nil? +Settings.incoming_email['start_tls'] = false if Settings.incoming_email['start_tls'].nil? Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mailbox'].nil? # -- cgit v1.2.1 From dec3e4ce64df5f71a7cba7734cada1baa79242cd Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 4 Nov 2015 19:13:19 +0100 Subject: Added Sherlock, a custom profiling tool for GitLab Sherlock will be a new GitLab specific tool for measuring the performance of Rails requests (and SideKiq jobs at some point). Some of the things that are currently tracked: * SQL queries along with their timings, backtraces and query plans (using "EXPLAIN ANALYZE" for PostgreSQL and regular "EXPLAIN" for MySQL) * Timings of application files (including views) on a per line basis * Some meta data such as the request method, path, total duration, etc More tracking (e.g. Rugged or gitlab-shell timings) might be added in the future. Sherlock will replace any existing tools we have used so far (e.g. active_record_query_trace and rack-mini-profiler), hence the corresponding Gems have been removed from the Gemfile. Sherlock can be enabled by starting Rails as following: ENABLE_SHERLOCK=1 bundle exec rails s Recorded transactions can be found at `/sherlock/transactions`. --- Gemfile | 4 +- Gemfile.lock | 11 +-- app/assets/stylesheets/pages/sherlock.scss | 33 ++++++++ app/controllers/sherlock/application_controller.rb | 12 +++ .../sherlock/file_samples_controller.rb | 7 ++ app/controllers/sherlock/queries_controller.rb | 7 ++ .../sherlock/transactions_controller.rb | 19 +++++ app/views/sherlock/file_samples/show.html.haml | 55 +++++++++++++ app/views/sherlock/queries/_backtrace.html.haml | 27 ++++++ app/views/sherlock/queries/_general.html.haml | 50 +++++++++++ app/views/sherlock/queries/show.html.haml | 26 ++++++ .../sherlock/transactions/_file_samples.html.haml | 22 +++++ app/views/sherlock/transactions/_general.html.haml | 33 ++++++++ app/views/sherlock/transactions/_queries.html.haml | 24 ++++++ app/views/sherlock/transactions/index.html.haml | 40 +++++++++ app/views/sherlock/transactions/show.html.haml | 36 ++++++++ config/initializers/rack_profiler.rb | 10 --- config/initializers/sherlock.rb | 5 ++ config/locales/sherlock.en.yml | 36 ++++++++ config/routes.rb | 13 +++ lib/gitlab/sherlock.rb | 20 +++++ lib/gitlab/sherlock/collection.rb | 42 ++++++++++ lib/gitlab/sherlock/file_sample.rb | 27 ++++++ lib/gitlab/sherlock/line_profiler.rb | 60 ++++++++++++++ lib/gitlab/sherlock/line_sample.rb | 20 +++++ lib/gitlab/sherlock/location.rb | 22 +++++ lib/gitlab/sherlock/middleware.rb | 36 ++++++++ lib/gitlab/sherlock/query.rb | 96 ++++++++++++++++++++++ lib/gitlab/sherlock/transaction.rb | 85 +++++++++++++++++++ 29 files changed, 855 insertions(+), 23 deletions(-) create mode 100644 app/assets/stylesheets/pages/sherlock.scss create mode 100644 app/controllers/sherlock/application_controller.rb create mode 100644 app/controllers/sherlock/file_samples_controller.rb create mode 100644 app/controllers/sherlock/queries_controller.rb create mode 100644 app/controllers/sherlock/transactions_controller.rb create mode 100644 app/views/sherlock/file_samples/show.html.haml create mode 100644 app/views/sherlock/queries/_backtrace.html.haml create mode 100644 app/views/sherlock/queries/_general.html.haml create mode 100644 app/views/sherlock/queries/show.html.haml create mode 100644 app/views/sherlock/transactions/_file_samples.html.haml create mode 100644 app/views/sherlock/transactions/_general.html.haml create mode 100644 app/views/sherlock/transactions/_queries.html.haml create mode 100644 app/views/sherlock/transactions/index.html.haml create mode 100644 app/views/sherlock/transactions/show.html.haml delete mode 100644 config/initializers/rack_profiler.rb create mode 100644 config/initializers/sherlock.rb create mode 100644 config/locales/sherlock.en.yml create mode 100644 lib/gitlab/sherlock.rb create mode 100644 lib/gitlab/sherlock/collection.rb create mode 100644 lib/gitlab/sherlock/file_sample.rb create mode 100644 lib/gitlab/sherlock/line_profiler.rb create mode 100644 lib/gitlab/sherlock/line_sample.rb create mode 100644 lib/gitlab/sherlock/location.rb create mode 100644 lib/gitlab/sherlock/middleware.rb create mode 100644 lib/gitlab/sherlock/query.rb create mode 100644 lib/gitlab/sherlock/transaction.rb diff --git a/Gemfile b/Gemfile index 0bac8978160..b0a7c9b9458 100644 --- a/Gemfile +++ b/Gemfile @@ -215,11 +215,9 @@ group :development do gem "annotate", "~> 2.6.0" gem "letter_opener", '~> 1.1.2' gem 'quiet_assets', '~> 1.0.2' - gem 'rack-mini-profiler', '~> 0.9.0', require: false gem 'rerun', '~> 0.10.0' gem 'bullet', require: false - gem 'active_record_query_trace', require: false - gem 'rack-lineprof', platform: :mri + gem 'rblineprof', platform: :mri, require: false # Better errors handler gem 'better_errors', '~> 1.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index dce728baf18..c602d406711 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,6 @@ GEM activesupport (= 4.1.12) builder (~> 3.1) erubis (~> 2.7.0) - active_record_query_trace (1.5) activemodel (4.1.12) activesupport (= 4.1.12) builder (~> 3.1) @@ -491,12 +490,6 @@ GEM rack-attack (4.3.0) rack rack-cors (0.4.0) - rack-lineprof (0.0.3) - rack (~> 1.5) - rblineprof (~> 0.3.6) - term-ansicolor (~> 1.3) - rack-mini-profiler (0.9.7) - rack (>= 1.1.3) rack-mount (0.8.3) rack (>= 1.0.0) rack-oauth2 (1.0.10) @@ -779,7 +772,6 @@ PLATFORMS DEPENDENCIES RedCloth (~> 4.2.9) ace-rails-ap (~> 2.0.1) - active_record_query_trace activerecord-deprecated_finders (~> 1.0.3) activerecord-session_store (~> 0.1.0) acts-as-taggable-on (~> 3.4) @@ -878,11 +870,10 @@ DEPENDENCIES quiet_assets (~> 1.0.2) rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) - rack-lineprof - rack-mini-profiler (~> 0.9.0) rack-oauth2 (~> 1.0.5) rails (= 4.1.12) raphael-rails (~> 2.1.2) + rblineprof rdoc (~> 3.6) redcarpet (~> 3.3.3) redis-rails (~> 4.0.0) diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss new file mode 100644 index 00000000000..92d84d9640f --- /dev/null +++ b/app/assets/stylesheets/pages/sherlock.scss @@ -0,0 +1,33 @@ +table .sherlock-code { + max-width: 700px; +} + +.sherlock-code { + pre { + word-wrap: normal; + } + + pre code { + white-space: pre; + } +} + +.sherlock-line-samples-table { + margin-bottom: 0px !important; + + thead tr th, + tbody tr td { + font-size: 13px !important; + text-align: right; + padding: 0px 10px !important; + } +} + +.sherlock-file-sample pre { + padding-top: 28px !important; +} + +.sherlock-line-samples-table .slow { + color: $red-light; + font-weight: bold; +} diff --git a/app/controllers/sherlock/application_controller.rb b/app/controllers/sherlock/application_controller.rb new file mode 100644 index 00000000000..682ca5e3821 --- /dev/null +++ b/app/controllers/sherlock/application_controller.rb @@ -0,0 +1,12 @@ +module Sherlock + class ApplicationController < ::ApplicationController + before_action :find_transaction + + def find_transaction + if params[:transaction_id] + @transaction = Gitlab::Sherlock.collection. + find_transaction(params[:transaction_id]) + end + end + end +end diff --git a/app/controllers/sherlock/file_samples_controller.rb b/app/controllers/sherlock/file_samples_controller.rb new file mode 100644 index 00000000000..0c3bc100106 --- /dev/null +++ b/app/controllers/sherlock/file_samples_controller.rb @@ -0,0 +1,7 @@ +module Sherlock + class FileSamplesController < Sherlock::ApplicationController + def show + @file_sample = @transaction.find_file_sample(params[:id]) + end + end +end diff --git a/app/controllers/sherlock/queries_controller.rb b/app/controllers/sherlock/queries_controller.rb new file mode 100644 index 00000000000..63b26aab1a4 --- /dev/null +++ b/app/controllers/sherlock/queries_controller.rb @@ -0,0 +1,7 @@ +module Sherlock + class QueriesController < Sherlock::ApplicationController + def show + @query = @transaction.find_query(params[:id]) + end + end +end diff --git a/app/controllers/sherlock/transactions_controller.rb b/app/controllers/sherlock/transactions_controller.rb new file mode 100644 index 00000000000..ccc739da879 --- /dev/null +++ b/app/controllers/sherlock/transactions_controller.rb @@ -0,0 +1,19 @@ +module Sherlock + class TransactionsController < Sherlock::ApplicationController + def index + @transactions = Gitlab::Sherlock.collection.newest_first + end + + def show + @transaction = Gitlab::Sherlock.collection.find_transaction(params[:id]) + + render_404 unless @transaction + end + + def destroy_all + Gitlab::Sherlock.collection.clear + + redirect_to(:back) + end + end +end diff --git a/app/views/sherlock/file_samples/show.html.haml b/app/views/sherlock/file_samples/show.html.haml new file mode 100644 index 00000000000..cfd11e45b6a --- /dev/null +++ b/app/views/sherlock/file_samples/show.html.haml @@ -0,0 +1,55 @@ +- page_title t('sherlock.title'), t('sherlock.transaction'), + t('sherlock.file_sample') + +- header_title t('sherlock.title'), sherlock_transactions_path + +.gray-content-block + .pull-right + = link_to(sherlock_transaction_path(@transaction), class: 'btn') do + %i.fa.fa-arrow-left + = t('sherlock.transaction') + .oneline + = t('sherlock.file_sample') + = @file_sample.id + +.prepend-top-default + %p + %span.light + #{t('sherlock.time')}: + %strong + = @file_sample.duration.round(2) + = t('sherlock.milliseconds') + %p + %span.light + #{t('sherlock.events')}: + %strong + = @file_sample.events + +%article.file-holder + .file-title + %i.fa.fa-file-text-o.fa-fw + %strong + = @file_sample.file + .code.file-content.js-syntax-highlight + .line-numbers + %table.sherlock-line-samples-table + %thead + %tr + %th= t('sherlock.line_capitalized') + %th= t('sherlock.events') + %th= t('sherlock.time') + %th= t('sherlock.percent') + %tbody + - @file_sample.line_samples.each_with_index do |sample, index| + %tr{class: sample.majority_of?(@file_sample.duration) ? 'slow' : ''} + %td= index + 1 + %td= sample.events + %td + = sample.duration.round(2) + = t('sherlock.milliseconds') + %td + = sample.percentage_of(@file_sample.duration).round + = t('sherlock.percent') + + .sherlock-file-sample + = highlight(@file_sample.file, @file_sample.source) diff --git a/app/views/sherlock/queries/_backtrace.html.haml b/app/views/sherlock/queries/_backtrace.html.haml new file mode 100644 index 00000000000..5c9294c0ab5 --- /dev/null +++ b/app/views/sherlock/queries/_backtrace.html.haml @@ -0,0 +1,27 @@ +.prepend-top-default + .panel.panel-default + .panel-heading + %strong + = t('sherlock.application_backtrace') + %ul.well-list + - @query.application_backtrace.each do |location| + %li + = location.path + %small.light + = t('sherlock.line') + = location.line + + .panel.panel-default + .panel-heading + %strong + = t('sherlock.full_backtrace') + %ul.well-list + - @query.backtrace.each do |location| + %li + - if location.application? + %strong= location.path + - else + = location.path + %small.light + = t('sherlock.line') + = location.line diff --git a/app/views/sherlock/queries/_general.html.haml b/app/views/sherlock/queries/_general.html.haml new file mode 100644 index 00000000000..549b47430e6 --- /dev/null +++ b/app/views/sherlock/queries/_general.html.haml @@ -0,0 +1,50 @@ +.prepend-top-default + .panel.panel-default + .panel-heading + %strong + = t('sherlock.general') + %ul.well-list + %li + %span.light + #{t('sherlock.time')}: + %strong + = @query.duration.round(4) + = t('sherlock.milliseconds') + %li + %span.light + #{t('sherlock.origin')}: + %strong + = @query.last_application_frame.path + %small.light + = t('sherlock.line') + = @query.last_application_frame.line + + .panel.panel-default + .panel-heading + .pull-right + %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button} + %i.fa.fa-clipboard + %pre.hidden + = @query.formatted_query + %strong + = t('sherlock.query') + %ul.well-list + %li + .code.js-syntax-highlight.sherlock-code + :preserve + #{highlight("#{@query.id}.sql", @query.formatted_query)} + + .panel.panel-default + .panel-heading + .pull-right + %button.js-clipboard-trigger.btn.btn-xs{title: t('sherlock.copy_to_clipboard'), type: :button} + %i.fa.fa-clipboard + %pre.hidden + = @query.explain + %strong + = t('sherlock.query_plan') + %ul.well-list + %li + .code.js-syntax-highlight.sherlock-code + %pre + %code= @query.explain diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml new file mode 100644 index 00000000000..4a84348ac82 --- /dev/null +++ b/app/views/sherlock/queries/show.html.haml @@ -0,0 +1,26 @@ +- page_title t('sherlock.title'), t('sherlock.transaction'), t('sherlock.query') +- header_title t('sherlock.title'), sherlock_transactions_path + +%ul.center-top-menu + %li.active + %a(href="#tab-general" data-toggle="tab") + = t('sherlock.general') + %li + %a(href="#tab-backtrace" data-toggle="tab") + = t('sherlock.backtrace') + +.gray-content-block + .pull-right + = link_to(sherlock_transaction_path(@transaction), class: 'btn') do + %i.fa.fa-arrow-left + = t('sherlock.transaction') + .oneline + = t('sherlock.query') + = @query.id + +.tab-content + .tab-pane.active#tab-general + = render(partial: 'general') + + .tab-pane#tab-backtrace + = render(partial: 'backtrace') diff --git a/app/views/sherlock/transactions/_file_samples.html.haml b/app/views/sherlock/transactions/_file_samples.html.haml new file mode 100644 index 00000000000..0afdbc8dffa --- /dev/null +++ b/app/views/sherlock/transactions/_file_samples.html.haml @@ -0,0 +1,22 @@ +- if @transaction.file_samples.empty? + .nothing-here-block + = t('sherlock.no_file_samples') +- else + .table-holder + %table.table + %thead + %tr + %th= t('sherlock.time_inclusive') + %th= t('sherlock.path') + %th + %tbody + - @transaction.sorted_file_samples.each do |sample| + %tr + %td + = sample.duration.round(2) + = t('sherlock.milliseconds') + %td= sample.relative_path + %td + = link_to(t('sherlock.view'), + sherlock_transaction_file_sample_path(@transaction, sample), + class: 'btn btn-xs') diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml new file mode 100644 index 00000000000..4287a0c3203 --- /dev/null +++ b/app/views/sherlock/transactions/_general.html.haml @@ -0,0 +1,33 @@ +.prepend-top-default + .panel.panel-default + .panel-heading + %strong + = t('sherlock.general') + %ul.well-list + %li + %span.light + #{t('sherlock.id')}: + %strong + = @transaction.id + %li + %span.light + #{t('sherlock.type')}: + %strong + = @transaction.type + %li + %span.light + #{t('sherlock.path')}: + %strong + = @transaction.path + %li + %span.light + #{t('sherlock.time')}: + %strong + = @transaction.duration.round(2) + = t('sherlock.seconds') + %li + %span.light + #{t('sherlock.finished_at')}: + %strong + = time_ago_in_words(@transaction.finished_at) + = t('sherlock.ago') diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml new file mode 100644 index 00000000000..b7e0162e80d --- /dev/null +++ b/app/views/sherlock/transactions/_queries.html.haml @@ -0,0 +1,24 @@ +- if @transaction.queries.empty? + .nothing-here-block + = t('sherlock.no_queries') +- else + .table-holder + %table.table#sherlock-queries + %thead + %tr + %th= t('sherlock.time') + %th= t('sherlock.query') + %td + %tbody + - @transaction.sorted_queries.each do |query| + %tr + %td + = query.duration.round(2) + = t('sherlock.milliseconds') + %td + .code.js-syntax-highlight.sherlock-code + = highlight("#{query.id}.sql", query.formatted_query) + %td + = link_to(t('sherlock.view'), + sherlock_transaction_query_path(@transaction, query), + class: 'btn btn-xs') diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml new file mode 100644 index 00000000000..fb31131ba88 --- /dev/null +++ b/app/views/sherlock/transactions/index.html.haml @@ -0,0 +1,40 @@ +- page_title t('sherlock.title') +- header_title t('sherlock.title'), sherlock_transactions_path + +.gray-content-block + .pull-right + = link_to(destroy_all_sherlock_transactions_path, + class: 'btn btn-danger', + method: :delete) do + %i.fa.fa-trash + = t('sherlock.delete_all_transactions') + .oneline= t('sherlock.introduction') + +- if @transactions.empty? + .nothing-here-block= t('sherlock.no_transactions') +- else + .table-holder + %table.table + %thead + %tr + %th= t('sherlock.type') + %th= t('sherlock.path') + %th= t('sherlock.time') + %th= t('sherlock.queries') + %th= t('sherlock.finished_at') + %th + %tbody + - @transactions.each do |trans| + %tr + %td= trans.type + %td= trans.path + %td + = trans.duration.round(2) + = t('sherlock.seconds') + %td= trans.queries.length + %td + = time_ago_in_words(trans.finished_at) + = t('sherlock.ago') + %td + = link_to(sherlock_transaction_path(trans), class: 'btn btn-xs') do + = t('sherlock.view') diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml new file mode 100644 index 00000000000..3c8ffb06648 --- /dev/null +++ b/app/views/sherlock/transactions/show.html.haml @@ -0,0 +1,36 @@ +- page_title t('sherlock.title'), t('sherlock.transaction') +- header_title t('sherlock.title'), sherlock_transactions_path + +%ul.center-top-menu + %li.active + %a(href="#tab-general" data-toggle="tab") + = t('sherlock.general') + %li + %a(href="#tab-queries" data-toggle="tab") + = t('sherlock.queries') + %span.badge + #{@transaction.queries.length} + %li + %a(href="#tab-file-samples" data-toggle="tab") + = t('sherlock.file_samples') + %span.badge + #{@transaction.file_samples.length} + +.gray-content-block + .pull-right + = link_to(sherlock_transactions_path, class: 'btn') do + %i.fa.fa-arrow-left + = t('sherlock.all_transactions') + .oneline + = t('sherlock.transaction') + = @transaction.id + +.tab-content + .tab-pane.active#tab-general + = render(partial: 'general') + + .tab-pane#tab-queries + = render(partial: 'queries') + + .tab-pane#tab-file-samples + = render(partial: 'file_samples') diff --git a/config/initializers/rack_profiler.rb b/config/initializers/rack_profiler.rb deleted file mode 100644 index 7710eeac453..00000000000 --- a/config/initializers/rack_profiler.rb +++ /dev/null @@ -1,10 +0,0 @@ -if Rails.env.development? - require 'rack-mini-profiler' - - # initialization is skipped so trigger it - Rack::MiniProfilerRails.initialize!(Gitlab::Application) - - Rack::MiniProfiler.config.position = 'right' - Rack::MiniProfiler.config.start_hidden = false - Rack::MiniProfiler.config.skip_paths << '/teaspoon' -end diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb new file mode 100644 index 00000000000..42b0d78c85f --- /dev/null +++ b/config/initializers/sherlock.rb @@ -0,0 +1,5 @@ +if Gitlab::Sherlock.enabled? + Gitlab::Application.configure do |config| + config.middleware.use(Gitlab::Sherlock::Middleware) + end +end diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml new file mode 100644 index 00000000000..5c146b172b1 --- /dev/null +++ b/config/locales/sherlock.en.yml @@ -0,0 +1,36 @@ +en: + sherlock: + title: Sherlock + delete_all_transactions: Delete All Transactions + introduction: > + Below is a list of all transactions recorded by Sherlock. Requests to + Sherlock's own routes are ignored. + no_transactions: No transactions to show + no_queries: No queries to show + no_file_samples: No file samples to show + all_transactions: All Transactions + transaction: Transaction + query: Query + file_sample: File Sample + type: Type + path: Path + time: Time + queries: Queries + finished_at: Finished at + ago: ago + view: View + seconds: seconds + milliseconds: ms + general: General + id: ID + time_inclusive: Time (inclusive) + backtrace: Backtrace + application_backtrace: Application Backtrace + full_backtrace: Full Backtrace + origin: Origin + line: line + line_capitalized: Line + copy_to_clipboard: Copy to clipboard + query_plan: Query Plan + events: Events + percent: '%' diff --git a/config/routes.rb b/config/routes.rb index 990a00e3d0b..7d8a546a64c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,19 @@ require 'sidekiq/web' require 'api/api' Gitlab::Application.routes.draw do + if Gitlab::Sherlock.enabled? + namespace :sherlock do + resources :transactions, only: [:index, :show] do + resources :queries, only: [:show] + resources :file_samples, only: [:show] + + collection do + delete :destroy_all + end + end + end + end + namespace :ci do # CI API Ci::API::API.logger Rails.logger diff --git a/lib/gitlab/sherlock.rb b/lib/gitlab/sherlock.rb new file mode 100644 index 00000000000..c4b35b24ceb --- /dev/null +++ b/lib/gitlab/sherlock.rb @@ -0,0 +1,20 @@ +require 'securerandom' +require 'rblineprof' if RUBY_ENGINE == 'ruby' + +module Gitlab + module Sherlock + @collection = Collection.new + + class << self + attr_reader :collection + end + + def self.enabled? + Rails.env.development? && !!ENV['ENABLE_SHERLOCK'] + end + + def self.enable_line_profiler? + RUBY_ENGINE == 'ruby' + end + end +end diff --git a/lib/gitlab/sherlock/collection.rb b/lib/gitlab/sherlock/collection.rb new file mode 100644 index 00000000000..accdc6469bc --- /dev/null +++ b/lib/gitlab/sherlock/collection.rb @@ -0,0 +1,42 @@ +module Gitlab + module Sherlock + class Collection + include Enumerable + + def initialize + @transactions = [] + @mutex = Mutex.new + end + + def add(transaction) + synchronize { @transactions << transaction } + end + + alias_method :<<, :add + + def each(&block) + synchronize { @transactions.each(&block) } + end + + def clear + synchronize { @transactions.clear } + end + + def empty? + synchronize { @transactions.empty? } + end + + def find_transaction(id) + find { |trans| trans.id == id } + end + + def newest_first + sort { |a, b| b.finished_at <=> a.finished_at } + end + + def synchronize(&block) + @mutex.synchronize(&block) + end + end + end +end diff --git a/lib/gitlab/sherlock/file_sample.rb b/lib/gitlab/sherlock/file_sample.rb new file mode 100644 index 00000000000..7a220de9abc --- /dev/null +++ b/lib/gitlab/sherlock/file_sample.rb @@ -0,0 +1,27 @@ +module Gitlab + module Sherlock + class FileSample + attr_reader :id, :file, :line_samples, :events, :duration + + def initialize(file, line_samples, duration, events) + @id = SecureRandom.uuid + @file = file + @line_samples = line_samples + @duration = duration + @events = events + end + + def relative_path + @relative_path ||= @file.gsub(/^#{Rails.root.to_s}\/?/, '') + end + + def to_param + @id + end + + def source + @source ||= File.read(@file) + end + end + end +end diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb new file mode 100644 index 00000000000..a191b1e646d --- /dev/null +++ b/lib/gitlab/sherlock/line_profiler.rb @@ -0,0 +1,60 @@ +module Gitlab + module Sherlock + class LineProfiler + # The minimum amount of time that has to be spent in a file for it to be + # included in a list of samples. + MINIMUM_DURATION = 10.0 + + def profile(&block) + if RUBY_ENGINE == 'ruby' + profile_mri(&block) + else + raise NotImplementedError, + 'Line profiling is not supported on this platform' + end + end + + def profile_mri + retval = nil + samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield } + + file_samples = aggregate_rblineprof(samples) + + [retval, file_samples] + end + + # Returns an Array of file samples based on the output of rblineprof. + def aggregate_rblineprof(lineprof_stats) + samples = [] + + lineprof_stats.each do |(file, stats)| + source_lines = File.read(file).each_line.to_a + line_samples = [] + + total_duration = microsec_to_millisec(stats[0][0]) + total_events = stats[0][2] + + next if total_duration <= MINIMUM_DURATION + + stats[1..-1].each_with_index do |data, index| + next unless source_lines[index] + + duration = microsec_to_millisec(data[0]) + events = data[2] + + line_samples << LineSample.new(duration, events) + end + + samples << FileSample. + new(file, line_samples, total_duration, total_events) + end + + samples + end + + def microsec_to_millisec(microsec) + microsec / 1000.0 + end + end + end +end diff --git a/lib/gitlab/sherlock/line_sample.rb b/lib/gitlab/sherlock/line_sample.rb new file mode 100644 index 00000000000..38df7a88e4e --- /dev/null +++ b/lib/gitlab/sherlock/line_sample.rb @@ -0,0 +1,20 @@ +module Gitlab + module Sherlock + class LineSample + attr_reader :duration, :events + + def initialize(duration, events) + @duration = duration + @events = events + end + + def percentage_of(total_duration) + (duration.to_f / total_duration) * 100.0 + end + + def majority_of?(total_duration) + percentage_of(total_duration) >= 30 + end + end + end +end diff --git a/lib/gitlab/sherlock/location.rb b/lib/gitlab/sherlock/location.rb new file mode 100644 index 00000000000..8c0b77dce1a --- /dev/null +++ b/lib/gitlab/sherlock/location.rb @@ -0,0 +1,22 @@ +module Gitlab + module Sherlock + class Location + attr_reader :path, :line + + SHERLOCK_DIR = File.dirname(__FILE__) + + def self.from_ruby_location(location) + new(location.path, location.lineno) + end + + def initialize(path, line) + @path = path + @line = line + end + + def application? + @path.start_with?(Rails.root.to_s) && !path.start_with?(SHERLOCK_DIR) + end + end + end +end diff --git a/lib/gitlab/sherlock/middleware.rb b/lib/gitlab/sherlock/middleware.rb new file mode 100644 index 00000000000..fca7be858eb --- /dev/null +++ b/lib/gitlab/sherlock/middleware.rb @@ -0,0 +1,36 @@ +module Gitlab + module Sherlock + # Rack middleware used for tracking request metrics. + class Middleware + CONTENT_TYPES = /text\/html|application\/json/i + + IGNORE_PATHS = %r{^/sherlock} + + def initialize(app) + @app = app + end + + def call(env) + if instrument?(env) + call_with_instrumentation(env) + else + @app.call(env) + end + end + + def call_with_instrumentation(env) + trans = Transaction.new(env['REQUEST_METHOD'], env['REQUEST_URI']) + retval = trans.run { @app.call(env) } + + Sherlock.collection.add(trans) + + retval + end + + def instrument?(env) + !!(env['HTTP_ACCEPT'] =~ CONTENT_TYPES && + env['REQUEST_URI'] !~ IGNORE_PATHS) + end + end + end +end diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb new file mode 100644 index 00000000000..af76e6fd2bf --- /dev/null +++ b/lib/gitlab/sherlock/query.rb @@ -0,0 +1,96 @@ +module Gitlab + module Sherlock + class Query + attr_reader :id, :query, :started_at, :finished_at, :backtrace + + PREFIX_NEWLINE = / + \s+(FROM + |(LEFT|RIGHT)?INNER\s+JOIN + |(LEFT|RIGHT)?OUTER\s+JOIN + |WHERE + |AND + |GROUP\s+BY + |ORDER\s+BY + |LIMIT + |OFFSET)\s+ + /ix + + def self.new_with_bindings(query, bindings, started_at, finished_at) + bindings.each_with_index do |(column, value), index| + quoted_value = ActiveRecord::Base.connection.quote(value) + + query = query.gsub("$#{index + 1}", quoted_value) + end + + new(query, started_at, finished_at) + end + + def initialize(query, started_at, finished_at) + @id = SecureRandom.uuid + @query = query + @started_at = started_at + @finished_at = finished_at + @backtrace = caller_locations.map do |loc| + Location.from_ruby_location(loc) + end + + unless @query.end_with?(';') + @query += ';' + end + end + + def duration + @duration ||= (@finished_at - @started_at) * 1000.0 + end + + def to_param + @id + end + + def formatted_query + @formatted_query ||= format_sql(@query) + end + + def last_application_frame + @last_application_frame ||= @backtrace.find(&:application?) + end + + def application_backtrace + @application_backtrace ||= @backtrace.select(&:application?) + end + + def explain + unless @explain + ActiveRecord::Base.connection.transaction do + @explain = raw_explain(@query).values.flatten.join("\n") + + # Roll back any queries that mutate data so we don't mess up + # anything when running explain on an INSERT, UPDATE, DELETE, etc. + raise ActiveRecord::Rollback + end + end + + @explain + end + + private + + def raw_explain(query) + if Gitlab::Database.postgresql? + explain = "EXPLAIN ANALYZE #{query};" + else + explain = "EXPLAIN #{query};" + end + + ActiveRecord::Base.connection.execute(explain) + end + + def format_sql(query) + query.each_line. + map { |line| line.strip }. + join("\n"). + gsub(PREFIX_NEWLINE) { "\n#{$1} " } + end + end + end +end diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb new file mode 100644 index 00000000000..5cb3e86aa4e --- /dev/null +++ b/lib/gitlab/sherlock/transaction.rb @@ -0,0 +1,85 @@ +module Gitlab + module Sherlock + class Transaction + attr_reader :id, :type, :path, :queries, :file_samples, :started_at, + :finished_at + + def initialize(type, path) + @id = SecureRandom.uuid + @type = type + @path = path + @duration = 0 + @queries = [] + @file_samples = [] + @started_at = nil + @finished_at = nil + @thread = Thread.current + end + + def run + @started_at = Time.now + + subscriber = subscribe_to_active_record + + retval = profile_lines { yield } + + @finished_at = Time.now + + ActiveSupport::Notifications.unsubscribe(subscriber) + + retval + end + + def duration + @started_at && @finished_at ? @finished_at - @started_at : 0 + end + + def to_param + @id + end + + def sorted_queries + @queries.sort { |a, b| b.duration <=> a.duration } + end + + def sorted_file_samples + @file_samples.sort { |a, b| b.duration <=> a.duration } + end + + def find_query(id) + @queries.find { |query| query.id == id } + end + + def find_file_sample(id) + @file_samples.find { |sample| sample.id == id } + end + + def track_query(query, bindings, start, finish) + @queries << Query.new_with_bindings(query, bindings, start, finish) + end + + def profile_lines + retval = nil + + if Sherlock.enable_line_profiler? + retval, @file_samples = LineProfiler.new.profile { yield } + else + retval = yield + end + + retval + end + + def subscribe_to_active_record + ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data| + # In case somebody uses a multi-threaded server locally (e.g. Puma) we + # _only_ want to track queries that originate from the transaction + # thread. + next unless Thread.current == @thread + + track_query(data[:sql].strip, data[:binds], start, finish) + end + end + end + end +end -- cgit v1.2.1 From 265ef867fff165643784640d837579ce4fcc2207 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 Nov 2015 18:01:05 +0100 Subject: Added specs and source documentation for Sherlock --- lib/gitlab/sherlock/collection.rb | 7 ++ lib/gitlab/sherlock/file_sample.rb | 4 + lib/gitlab/sherlock/line_profiler.rb | 38 +++++- lib/gitlab/sherlock/line_sample.rb | 16 +++ lib/gitlab/sherlock/location.rb | 4 + lib/gitlab/sherlock/middleware.rb | 7 +- lib/gitlab/sherlock/query.rb | 26 +++- lib/gitlab/sherlock/transaction.rb | 29 ++++- spec/lib/gitlab/sherlock/collection_spec.rb | 82 ++++++++++++ spec/lib/gitlab/sherlock/file_sample_spec.rb | 54 ++++++++ spec/lib/gitlab/sherlock/line_profiler_spec.rb | 73 +++++++++++ spec/lib/gitlab/sherlock/line_sample_spec.rb | 33 +++++ spec/lib/gitlab/sherlock/location_spec.rb | 40 ++++++ spec/lib/gitlab/sherlock/middleware_spec.rb | 79 ++++++++++++ spec/lib/gitlab/sherlock/query_spec.rb | 113 +++++++++++++++++ spec/lib/gitlab/sherlock/transaction_spec.rb | 165 +++++++++++++++++++++++++ 16 files changed, 758 insertions(+), 12 deletions(-) create mode 100644 spec/lib/gitlab/sherlock/collection_spec.rb create mode 100644 spec/lib/gitlab/sherlock/file_sample_spec.rb create mode 100644 spec/lib/gitlab/sherlock/line_profiler_spec.rb create mode 100644 spec/lib/gitlab/sherlock/line_sample_spec.rb create mode 100644 spec/lib/gitlab/sherlock/location_spec.rb create mode 100644 spec/lib/gitlab/sherlock/middleware_spec.rb create mode 100644 spec/lib/gitlab/sherlock/query_spec.rb create mode 100644 spec/lib/gitlab/sherlock/transaction_spec.rb diff --git a/lib/gitlab/sherlock/collection.rb b/lib/gitlab/sherlock/collection.rb index accdc6469bc..66bd6258521 100644 --- a/lib/gitlab/sherlock/collection.rb +++ b/lib/gitlab/sherlock/collection.rb @@ -1,5 +1,10 @@ module Gitlab module Sherlock + # A collection of transactions recorded by Sherlock. + # + # Method calls for this class are synchronized using a mutex to allow + # sharing of a single Collection instance between threads (e.g. when using + # Puma as a webserver). class Collection include Enumerable @@ -34,6 +39,8 @@ module Gitlab sort { |a, b| b.finished_at <=> a.finished_at } end + private + def synchronize(&block) @mutex.synchronize(&block) end diff --git a/lib/gitlab/sherlock/file_sample.rb b/lib/gitlab/sherlock/file_sample.rb index 7a220de9abc..8a3e1a5e5bf 100644 --- a/lib/gitlab/sherlock/file_sample.rb +++ b/lib/gitlab/sherlock/file_sample.rb @@ -3,6 +3,10 @@ module Gitlab class FileSample attr_reader :id, :file, :line_samples, :events, :duration + # file - The full path to the file this sample belongs to. + # line_samples - An array of LineSample objects. + # duration - The total execution time in milliseconds. + # events - The total amount of events. def initialize(file, line_samples, duration, events) @id = SecureRandom.uuid @file = file diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb index a191b1e646d..152749dcc39 100644 --- a/lib/gitlab/sherlock/line_profiler.rb +++ b/lib/gitlab/sherlock/line_profiler.rb @@ -1,12 +1,36 @@ module Gitlab module Sherlock + # Class for profiling code on a per line basis. + # + # The LineProfiler class can be used to profile code on per line basis + # without littering your code with Ruby implementation specific profiling + # methods. + # + # This profiler only includes samples taking longer than a given threshold + # and those that occur in the actual application (e.g. files from Gems are + # ignored). class LineProfiler # The minimum amount of time that has to be spent in a file for it to be # included in a list of samples. MINIMUM_DURATION = 10.0 + # Profiles the given block. + # + # Example: + # + # profiler = LineProfiler.new + # + # retval, samples = profiler.profile do + # "cats are amazing" + # end + # + # retval # => "cats are amazing" + # samples # => [#, ...] + # + # Returns an Array containing the block's return value and an Array of + # FileSample objects. def profile(&block) - if RUBY_ENGINE == 'ruby' + if mri? profile_mri(&block) else raise NotImplementedError, @@ -14,6 +38,7 @@ module Gitlab end end + # Profiles the given block using rblineprof (MRI only). def profile_mri retval = nil samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield } @@ -24,6 +49,11 @@ module Gitlab end # Returns an Array of file samples based on the output of rblineprof. + # + # lineprof_stats - A Hash containing rblineprof statistics on a per file + # basis. + # + # Returns an Array of FileSample objects. def aggregate_rblineprof(lineprof_stats) samples = [] @@ -52,9 +82,15 @@ module Gitlab samples end + private + def microsec_to_millisec(microsec) microsec / 1000.0 end + + def mri? + RUBY_ENGINE == 'ruby' + end end end end diff --git a/lib/gitlab/sherlock/line_sample.rb b/lib/gitlab/sherlock/line_sample.rb index 38df7a88e4e..eb1948eb6d6 100644 --- a/lib/gitlab/sherlock/line_sample.rb +++ b/lib/gitlab/sherlock/line_sample.rb @@ -3,15 +3,31 @@ module Gitlab class LineSample attr_reader :duration, :events + # duration - The execution time in milliseconds. + # events - The amount of events. def initialize(duration, events) @duration = duration @events = events end + # Returns the sample duration percentage relative to the given duration. + # + # Example: + # + # sample.duration # => 150 + # sample.percentage_of(1500) # => 10.0 + # + # total_duration - The total duration to compare with. + # + # Returns a float def percentage_of(total_duration) (duration.to_f / total_duration) * 100.0 end + # Returns true if the current sample takes up the majority of the given + # duration. + # + # total_duration - The total duration to compare with. def majority_of?(total_duration) percentage_of(total_duration) >= 30 end diff --git a/lib/gitlab/sherlock/location.rb b/lib/gitlab/sherlock/location.rb index 8c0b77dce1a..5ac265618ad 100644 --- a/lib/gitlab/sherlock/location.rb +++ b/lib/gitlab/sherlock/location.rb @@ -5,15 +5,19 @@ module Gitlab SHERLOCK_DIR = File.dirname(__FILE__) + # Creates a new Location from a `Thread::Backtrace::Location`. def self.from_ruby_location(location) new(location.path, location.lineno) end + # path - The full path of the frame as a String. + # line - The line number of the frame as a Fixnum. def initialize(path, line) @path = path @line = line end + # Returns true if the current frame originated from the application. def application? @path.start_with?(Rails.root.to_s) && !path.start_with?(SHERLOCK_DIR) end diff --git a/lib/gitlab/sherlock/middleware.rb b/lib/gitlab/sherlock/middleware.rb index fca7be858eb..687332fc5fc 100644 --- a/lib/gitlab/sherlock/middleware.rb +++ b/lib/gitlab/sherlock/middleware.rb @@ -10,6 +10,7 @@ module Gitlab @app = app end + # env - A Hash containing Rack environment details. def call(env) if instrument?(env) call_with_instrumentation(env) @@ -19,7 +20,7 @@ module Gitlab end def call_with_instrumentation(env) - trans = Transaction.new(env['REQUEST_METHOD'], env['REQUEST_URI']) + trans = transaction_from_env(env) retval = trans.run { @app.call(env) } Sherlock.collection.add(trans) @@ -31,6 +32,10 @@ module Gitlab !!(env['HTTP_ACCEPT'] =~ CONTENT_TYPES && env['REQUEST_URI'] !~ IGNORE_PATHS) end + + def transaction_from_env(env) + Transaction.new(env['REQUEST_METHOD'], env['REQUEST_URI']) + end end end end diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb index af76e6fd2bf..4917c4ae2ac 100644 --- a/lib/gitlab/sherlock/query.rb +++ b/lib/gitlab/sherlock/query.rb @@ -3,6 +3,7 @@ module Gitlab class Query attr_reader :id, :query, :started_at, :finished_at, :backtrace + # SQL identifiers that should be prefixed with newlines. PREFIX_NEWLINE = / \s+(FROM |(LEFT|RIGHT)?INNER\s+JOIN @@ -12,11 +13,20 @@ module Gitlab |GROUP\s+BY |ORDER\s+BY |LIMIT - |OFFSET)\s+ - /ix - + |OFFSET)\s+/ix # Vim indent breaks when this is on a newline :< + + # Creates a new Query using a String and a separate Array of bindings. + # + # query - A String containing a SQL query, optionally with numeric + # placeholders (`$1`, `$2`, etc). + # + # bindings - An Array of ActiveRecord columns and their values. + # started_at - The start time of the query as a Time-like object. + # finished_at - The completion time of the query as a Time-like object. + # + # Returns a new Query object. def self.new_with_bindings(query, bindings, started_at, finished_at) - bindings.each_with_index do |(column, value), index| + bindings.each_with_index do |(_, value), index| quoted_value = ActiveRecord::Base.connection.quote(value) query = query.gsub("$#{index + 1}", quoted_value) @@ -25,6 +35,9 @@ module Gitlab new(query, started_at, finished_at) end + # query - The SQL query as a String (without placeholders). + # started_at - The start time of the query as a Time-like object. + # finished_at - The completion time of the query as a Time-like object. def initialize(query, started_at, finished_at) @id = SecureRandom.uuid @query = query @@ -39,6 +52,7 @@ module Gitlab end end + # Returns the query duration in milliseconds. def duration @duration ||= (@finished_at - @started_at) * 1000.0 end @@ -47,18 +61,22 @@ module Gitlab @id end + # Returns a human readable version of the query. def formatted_query @formatted_query ||= format_sql(@query) end + # Returns the last application frame of the backtrace. def last_application_frame @last_application_frame ||= @backtrace.find(&:application?) end + # Returns an Array of application frames (excluding Gems and the likes). def application_backtrace @application_backtrace ||= @backtrace.select(&:application?) end + # Returns the query plan as a String. def explain unless @explain ActiveRecord::Base.connection.transaction do diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb index 5cb3e86aa4e..4641f15ee33 100644 --- a/lib/gitlab/sherlock/transaction.rb +++ b/lib/gitlab/sherlock/transaction.rb @@ -4,11 +4,12 @@ module Gitlab attr_reader :id, :type, :path, :queries, :file_samples, :started_at, :finished_at + # type - The type of transaction (e.g. "GET", "POST", etc) + # path - The path of the transaction (e.g. the HTTP request path) def initialize(type, path) @id = SecureRandom.uuid @type = type @path = path - @duration = 0 @queries = [] @file_samples = [] @started_at = nil @@ -16,6 +17,7 @@ module Gitlab @thread = Thread.current end + # Runs the transaction and returns the block's return value. def run @started_at = Time.now @@ -30,34 +32,43 @@ module Gitlab retval end + # Returns the duration in seconds. def duration - @started_at && @finished_at ? @finished_at - @started_at : 0 + @duration ||= started_at && finished_at ? finished_at - started_at : 0 end def to_param @id end + # Returns the queries sorted in descending order by their durations. def sorted_queries @queries.sort { |a, b| b.duration <=> a.duration } end + # Returns the file samples sorted in descending order by their durations. def sorted_file_samples @file_samples.sort { |a, b| b.duration <=> a.duration } end + # Finds a query by the given ID. + # + # id - The query ID as a String. + # + # Returns a Query object if one could be found, nil otherwise. def find_query(id) @queries.find { |query| query.id == id } end + # Finds a file sample by the given ID. + # + # id - The query ID as a String. + # + # Returns a FileSample object if one could be found, nil otherwise. def find_file_sample(id) @file_samples.find { |sample| sample.id == id } end - def track_query(query, bindings, start, finish) - @queries << Query.new_with_bindings(query, bindings, start, finish) - end - def profile_lines retval = nil @@ -70,6 +81,12 @@ module Gitlab retval end + private + + def track_query(query, bindings, start, finish) + @queries << Query.new_with_bindings(query, bindings, start, finish) + end + def subscribe_to_active_record ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data| # In case somebody uses a multi-threaded server locally (e.g. Puma) we diff --git a/spec/lib/gitlab/sherlock/collection_spec.rb b/spec/lib/gitlab/sherlock/collection_spec.rb new file mode 100644 index 00000000000..a8a9d6fc7bc --- /dev/null +++ b/spec/lib/gitlab/sherlock/collection_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::Collection do + let(:collection) { described_class.new } + + let(:transaction) do + Gitlab::Sherlock::Transaction.new('POST', '/cat_pictures') + end + + describe '#add' do + it 'adds a new transaction' do + collection.add(transaction) + + expect(collection).to_not be_empty + end + + it 'is aliased as <<' do + collection << transaction + + expect(collection).to_not be_empty + end + end + + describe '#each' do + it 'iterates over every transaction' do + collection.add(transaction) + + expect { |b| collection.each(&b) }.to yield_with_args(transaction) + end + end + + describe '#clear' do + it 'removes all transactions' do + collection.add(transaction) + + collection.clear + + expect(collection).to be_empty + end + end + + describe '#empty?' do + it 'returns true for an empty collection' do + expect(collection).to be_empty + end + + it 'returns false for a collection with a transaction' do + collection.add(transaction) + + expect(collection).to_not be_empty + end + end + + describe '#find_transaction' do + it 'returns the transaction for the given ID' do + collection.add(transaction) + + expect(collection.find_transaction(transaction.id)).to eq(transaction) + end + + it 'returns nil when no transaction could be found' do + collection.add(transaction) + + expect(collection.find_transaction('cats')).to be_nil + end + end + + describe '#newest_first' do + it 'returns transactions sorted from new to old' do + trans1 = Gitlab::Sherlock::Transaction.new('POST', '/cat_pictures') + trans2 = Gitlab::Sherlock::Transaction.new('POST', '/more_cat_pictures') + + allow(trans1).to receive(:finished_at).and_return(Time.utc(2015, 1, 1)) + allow(trans2).to receive(:finished_at).and_return(Time.utc(2015, 1, 2)) + + collection.add(trans1) + collection.add(trans2) + + expect(collection.newest_first).to eq([trans2, trans1]) + end + end +end diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb new file mode 100644 index 00000000000..f05a59f56f6 --- /dev/null +++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::FileSample do + let(:sample) { described_class.new(__FILE__, [], 150.4, 2) } + + describe '#id' do + it 'returns the ID' do + expect(sample.id).to be_an_instance_of(String) + end + end + + describe '#file' do + it 'returns the file path' do + expect(sample.file).to eq(__FILE__) + end + end + + describe '#line_samples' do + it 'returns the line samples' do + expect(sample.line_samples).to eq([]) + end + end + + describe '#events' do + it 'returns the total number of events' do + expect(sample.events).to eq(2) + end + end + + describe '#duration' do + it 'returns the total execution time' do + expect(sample.duration).to eq(150.4) + end + end + + describe '#relative_path' do + it 'returns the relative path' do + expect(sample.relative_path). + to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb') + end + end + + describe '#to_param' do + it 'returns the sample ID' do + expect(sample.to_param).to eq(sample.id) + end + end + + describe '#source' do + it 'returns the contents of the file' do + expect(sample.source).to eq(File.read(__FILE__)) + end + end +end diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb new file mode 100644 index 00000000000..8f2e1299714 --- /dev/null +++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::LineProfiler do + let(:profiler) { described_class.new } + + describe '#profile' do + it 'runs the profiler when using MRI' do + allow(profiler).to receive(:mri?).and_return(true) + allow(profiler).to receive(:profile_mri) + + profiler.profile { 'cats' } + end + + it 'raises NotImplementedError when profiling an unsupported platform' do + allow(profiler).to receive(:mri?).and_return(false) + + expect { profiler.profile { 'cats' } }.to raise_error(NotImplementedError) + end + end + + describe '#profile_mri' do + it 'returns an Array containing the return value and profiling samples' do + allow(profiler).to receive(:lineprof). + and_yield. + and_return({ __FILE__ => [[0, 0, 0, 0]] }) + + retval, samples = profiler.profile_mri { 42 } + + expect(retval).to eq(42) + expect(samples).to eq([]) + end + end + + describe '#aggregate_rblineprof' do + let(:raw_samples) do + { __FILE__ => [[30000, 30000, 5, 0], [15000, 15000, 4, 0]] } + end + + it 'returns an Array of FileSample objects' do + samples = profiler.aggregate_rblineprof(raw_samples) + + expect(samples).to be_an_instance_of(Array) + expect(samples[0]).to be_an_instance_of(Gitlab::Sherlock::FileSample) + end + + describe 'the first FileSample object' do + let(:file_sample) do + profiler.aggregate_rblineprof(raw_samples)[0] + end + + it 'uses the correct file path' do + expect(file_sample.file).to eq(__FILE__) + end + + it 'contains a list of line samples' do + line_sample = file_sample.line_samples[0] + + expect(line_sample).to be_an_instance_of(Gitlab::Sherlock::LineSample) + + expect(line_sample.duration).to eq(15.0) + expect(line_sample.events).to eq(4) + end + + it 'contains the total file execution time' do + expect(file_sample.duration).to eq(30.0) + end + + it 'contains the total amount of file events' do + expect(file_sample.events).to eq(5) + end + end + end +end diff --git a/spec/lib/gitlab/sherlock/line_sample_spec.rb b/spec/lib/gitlab/sherlock/line_sample_spec.rb new file mode 100644 index 00000000000..5f02f6a3213 --- /dev/null +++ b/spec/lib/gitlab/sherlock/line_sample_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::LineSample do + let(:sample) { described_class.new(150.0, 4) } + + describe '#duration' do + it 'returns the duration' do + expect(sample.duration).to eq(150.0) + end + end + + describe '#events' do + it 'returns the amount of events' do + expect(sample.events).to eq(4) + end + end + + describe '#percentage_of' do + it 'returns the percentage of 1500.0' do + expect(sample.percentage_of(1500.0)).to be_within(0.1).of(10.0) + end + end + + describe '#majority_of' do + it 'returns true if the sample takes up the majority of the given duration' do + expect(sample.majority_of?(500.0)).to eq(true) + end + + it "returns false if the sample doesn't take up the majority of the given duration" do + expect(sample.majority_of?(1500.0)).to eq(false) + end + end +end diff --git a/spec/lib/gitlab/sherlock/location_spec.rb b/spec/lib/gitlab/sherlock/location_spec.rb new file mode 100644 index 00000000000..b295a624b35 --- /dev/null +++ b/spec/lib/gitlab/sherlock/location_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::Location do + let(:location) { described_class.new(__FILE__, 1) } + + describe 'from_ruby_location' do + it 'creates a Location from a Thread::Backtrace::Location' do + input = caller_locations[0] + output = described_class.from_ruby_location(input) + + expect(output).to be_an_instance_of(described_class) + expect(output.path).to eq(input.path) + expect(output.line).to eq(input.lineno) + end + end + + describe '#path' do + it 'returns the file path' do + expect(location.path).to eq(__FILE__) + end + end + + describe '#line' do + it 'returns the line number' do + expect(location.line).to eq(1) + end + end + + describe '#application?' do + it 'returns true for an application frame' do + expect(location.application?).to eq(true) + end + + it 'returns false for a non application frame' do + loc = described_class.new('/tmp/cats.rb', 1) + + expect(loc.application?).to eq(false) + end + end +end diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb new file mode 100644 index 00000000000..aa74fc53a79 --- /dev/null +++ b/spec/lib/gitlab/sherlock/middleware_spec.rb @@ -0,0 +1,79 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::Middleware do + let(:app) { double(:app) } + let(:middleware) { described_class.new(app) } + + describe '#call' do + describe 'when instrumentation is enabled' do + it 'instruments a request' do + allow(middleware).to receive(:instrument?).and_return(true) + allow(middleware).to receive(:call_with_instrumentation) + + middleware.call({}) + end + end + + describe 'when instrumentation is disabled' do + it "doesn't instrument a request" do + allow(middleware).to receive(:instrument).and_return(false) + allow(app).to receive(:call) + + middleware.call({}) + end + end + end + + describe '#call_with_instrumentation' do + it 'instruments a request' do + trans = double(:transaction) + retval = 'cats are amazing' + env = {} + + allow(app).to receive(:call).with(env).and_return(retval) + allow(middleware).to receive(:transaction_from_env).and_return(trans) + allow(trans).to receive(:run).and_yield.and_return(retval) + allow(Gitlab::Sherlock.collection).to receive(:add).with(trans) + + middleware.call_with_instrumentation(env) + end + end + + describe '#instrument?' do + it 'returns false for a text/css request' do + env = { 'HTTP_ACCEPT' => 'text/css', 'REQUEST_URI' => '/' } + + expect(middleware.instrument?(env)).to eq(false) + end + + it 'returns false for a request to a Sherlock route' do + env = { + 'HTTP_ACCEPT' => 'text/html', + 'REQUEST_URI' => '/sherlock/transactions' + } + + expect(middleware.instrument?(env)).to eq(false) + end + + it 'returns true for a request that should be instrumented' do + env = { + 'HTTP_ACCEPT' => 'text/html', + 'REQUEST_URI' => '/cats' + } + + expect(middleware.instrument?(env)).to eq(true) + end + end + + describe '#transaction_from_env' do + it 'returns a Transaction' do + env = { + 'HTTP_ACCEPT' => 'text/html', + 'REQUEST_URI' => '/cats' + } + + expect(middleware.transaction_from_env(env)). + to be_an_instance_of(Gitlab::Sherlock::Transaction) + end + end +end diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb new file mode 100644 index 00000000000..b15a125a40c --- /dev/null +++ b/spec/lib/gitlab/sherlock/query_spec.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::Query do + let(:started_at) { Time.utc(2015, 1, 1) } + let(:finished_at) { started_at + 5 } + + let(:query) do + described_class.new('SELECT COUNT(*) FROM users', started_at, finished_at) + end + + describe 'new_with_bindings' do + it 'returns a Query' do + sql = 'SELECT COUNT(*) FROM users WHERE id = $1' + bindings = [[double(:column), 10]] + + query = described_class. + new_with_bindings(sql, bindings, started_at, finished_at) + + expect(query.query).to eq('SELECT COUNT(*) FROM users WHERE id = 10;') + end + end + + describe '#id' do + it 'returns a String' do + expect(query.id).to be_an_instance_of(String) + end + end + + describe '#query' do + it 'returns the query with a trailing semi-colon' do + expect(query.query).to eq('SELECT COUNT(*) FROM users;') + end + end + + describe '#started_at' do + it 'returns the start time' do + expect(query.started_at).to eq(started_at) + end + end + + describe '#finished_at' do + it 'returns the completion time' do + expect(query.finished_at).to eq(finished_at) + end + end + + describe '#backtrace' do + it 'returns the backtrace' do + expect(query.backtrace).to be_an_instance_of(Array) + end + end + + describe '#duration' do + it 'returns the duration in milliseconds' do + expect(query.duration).to be_within(0.1).of(5000.0) + end + end + + describe '#to_param' do + it 'returns the query ID' do + expect(query.to_param).to eq(query.id) + end + end + + describe '#formatted_query' do + it 'returns a formatted version of the query' do + expect(query.formatted_query).to eq(<<-EOF.strip) +SELECT COUNT(*) +FROM users; + EOF + end + end + + describe '#last_application_frame' do + it 'returns the last application frame' do + frame = query.last_application_frame + + expect(frame).to be_an_instance_of(Gitlab::Sherlock::Location) + expect(frame.path).to eq(__FILE__) + end + end + + describe '#application_backtrace' do + it 'returns an Array of application frames' do + frames = query.application_backtrace + + expect(frames).to be_an_instance_of(Array) + expect(frames).to_not be_empty + + frames.each do |frame| + expect(frame.path).to start_with(Rails.root.to_s) + end + end + end + + describe '#explain' do + it 'returns the query plan as a String' do + lines = [ + ['Aggregate (cost=123 rows=1)'], + [' -> Index Only Scan using index_cats_are_amazing'] + ] + + result = double(:result, :values => lines) + + allow(query).to receive(:raw_explain).and_return(result) + + expect(query.explain).to eq(<<-EOF.strip) +Aggregate (cost=123 rows=1) + -> Index Only Scan using index_cats_are_amazing + EOF + end + end +end diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb new file mode 100644 index 00000000000..bb4ff42e6e1 --- /dev/null +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -0,0 +1,165 @@ +require 'spec_helper' + +describe Gitlab::Sherlock::Transaction do + let(:transaction) { described_class.new('POST', '/cat_pictures') } + + describe '#id' do + it 'returns the transaction ID' do + expect(transaction.id).to be_an_instance_of(String) + end + end + + describe '#type' do + it 'returns the type' do + expect(transaction.type).to eq('POST') + end + end + + describe '#path' do + it 'returns the path' do + expect(transaction.path).to eq('/cat_pictures') + end + end + + describe '#queries' do + it 'returns an Array of queries' do + expect(transaction.queries).to be_an_instance_of(Array) + end + end + + describe '#file_samples' do + it 'returns an Array of file samples' do + expect(transaction.file_samples).to be_an_instance_of(Array) + end + end + + describe '#started_at' do + it 'returns the start time' do + allow(transaction).to receive(:profile_lines).and_yield + + transaction.run { 'cats are amazing' } + + expect(transaction.started_at).to be_an_instance_of(Time) + end + end + + describe '#finished_at' do + it 'returns the completion time' do + allow(transaction).to receive(:profile_lines).and_yield + + transaction.run { 'cats are amazing' } + + expect(transaction.finished_at).to be_an_instance_of(Time) + end + end + + describe '#run' do + it 'runs the transaction' do + allow(transaction).to receive(:profile_lines).and_yield + + retval = transaction.run { 'cats are amazing' } + + expect(retval).to eq('cats are amazing') + end + end + + describe '#duration' do + it 'returns the duration in seconds' do + start_time = Time.now + + allow(transaction).to receive(:started_at).and_return(start_time) + allow(transaction).to receive(:finished_at).and_return(start_time + 5) + + expect(transaction.duration).to be_within(0.1).of(5.0) + end + end + + describe '#to_param' do + it 'returns the transaction ID' do + expect(transaction.to_param).to eq(transaction.id) + end + end + + describe '#sorted_queries' do + it 'returns the queries in descending order' do + start_time = Time.now + + query1 = Gitlab::Sherlock::Query.new('SELECT 1', start_time, start_time) + + query2 = Gitlab::Sherlock::Query. + new('SELECT 2', start_time, start_time + 5) + + transaction.queries << query1 + transaction.queries << query2 + + expect(transaction.sorted_queries).to eq([query2, query1]) + end + end + + describe '#sorted_file_samples' do + it 'returns the file samples in descending order' do + sample1 = Gitlab::Sherlock::FileSample.new(__FILE__, [], 10.0, 1) + sample2 = Gitlab::Sherlock::FileSample.new(__FILE__, [], 15.0, 1) + + transaction.file_samples << sample1 + transaction.file_samples << sample2 + + expect(transaction.sorted_file_samples).to eq([sample2, sample1]) + end + end + + describe '#find_query' do + it 'returns a Query when found' do + query = Gitlab::Sherlock::Query.new('SELECT 1', Time.now, Time.now) + + transaction.queries << query + + expect(transaction.find_query(query.id)).to eq(query) + end + + it 'returns nil when no query could be found' do + expect(transaction.find_query('cats')).to be_nil + end + end + + describe '#find_file_sample' do + it 'returns a FileSample when found' do + sample = Gitlab::Sherlock::FileSample.new(__FILE__, [], 10.0, 1) + + transaction.file_samples << sample + + expect(transaction.find_file_sample(sample.id)).to eq(sample) + end + + it 'returns nil when no file sample could be found' do + expect(transaction.find_file_sample('cats')).to be_nil + end + end + + describe '#profile_lines' do + describe 'when line profiling is enabled' do + it 'yields the block using the line profiler' do + allow(Gitlab::Sherlock).to receive(:enable_line_profiler?). + and_return(true) + + allow_any_instance_of(Gitlab::Sherlock::LineProfiler). + to receive(:profile).and_return('cats are amazing', []) + + retval = transaction.profile_lines { 'cats are amazing' } + + expect(retval).to eq('cats are amazing') + end + end + + describe 'when line profiling is disabled' do + it 'yields the block' do + allow(Gitlab::Sherlock).to receive(:enable_line_profiler?). + and_return(false) + + retval = transaction.profile_lines { 'cats are amazing' } + + expect(retval).to eq('cats are amazing') + end + end + end +end -- cgit v1.2.1 From 126a2428cd7fb5b2766da10373f72a370aa68ba5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 5 Nov 2015 18:06:05 +0100 Subject: Updated profiling guides for Sherlock --- doc/development/profiling.md | 45 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/doc/development/profiling.md b/doc/development/profiling.md index 80c86ef921e..e244ad4e881 100644 --- a/doc/development/profiling.md +++ b/doc/development/profiling.md @@ -4,11 +4,15 @@ To make it easier to track down performance problems GitLab comes with a set of profiling tools, some of these are available by default while others need to be explicitly enabled. -## rack-mini-profiler +## Sherlock -This Gem is enabled by default in development only. It allows you to see the -timings of the various components that made up a web request (e.g. the SQL -queries executed and their execution timings). +Sherlock is a custom profiling tool built into GitLab. Sherlock is _only_ +available when running GitLab in development mode _and_ when setting the +environment variable `ENABLE_SHERLOCK` to a non empty value. For example: + + ENABLE_SHERLOCK=1 bundle exec rails s + +Recorded transactions can be found by navigating to `/sherlock/transactions`. ## Bullet @@ -21,36 +25,3 @@ starting GitLab. For example: Bullet will log query problems to both the Rails log as well as the Chrome console. - -## ActiveRecord Query Trace - -This Gem adds backtraces for every ActiveRecord query in the Rails console. This -can be useful to track down where a query was executed. Because this Gem adds -quite a bit of noise (5-10 extra lines per ActiveRecord query) it's disabled by -default. To use this Gem you'll need to set `ENABLE_QUERY_TRACE` to a non empty -file before starting GitLab. For example: - - ENABLE_QUERY_TRACE=true bundle exec rails s - -## rack-lineprof - -This is a Gem that can trace the execution time of code on a per line basis. -Because this Gem can add quite a bit of overhead it's disabled by default. To -enable it, set the environment variable `ENABLE_LINEPROF` to a non-empty value. -For example: - - ENABLE_LINEPROF=true bundle exec rails s - -Once enabled you'll need to add a query string parameter to a request to -actually profile code execution. The name of the parameter is `lineprof` and -should be set to a regular expression (minus the starting/ending slash) used to -select what files to profile. To profile all files containing "foo" somewhere in -the path you'd use the following parameter: - - ?lineprof=foo - -Or when filtering for files containing "foo" and "bar" in their path: - - ?lineprof=foo|bar - -Once set the profiling output will be displayed in your terminal. -- cgit v1.2.1 From db46d49c0ed7e34a9679b0475480306d92b0de31 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 11:21:50 +0100 Subject: Fixed Hash key style in Sherlock::Query spec --- spec/lib/gitlab/sherlock/query_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb index b15a125a40c..a9afef5dc1d 100644 --- a/spec/lib/gitlab/sherlock/query_spec.rb +++ b/spec/lib/gitlab/sherlock/query_spec.rb @@ -100,7 +100,7 @@ FROM users; [' -> Index Only Scan using index_cats_are_amazing'] ] - result = double(:result, :values => lines) + result = double(:result, values: lines) allow(query).to receive(:raw_explain).and_return(result) -- cgit v1.2.1 From cdaa97443e89a08d857a244e6e8ab0235db9746d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 11:30:57 +0100 Subject: Added navigation link to Sherlock --- app/views/layouts/header/_default.html.haml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index c31b1cbe9a8..c08a7b80744 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -21,6 +21,11 @@ %li = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('plus fw') + - if Gitlab::Sherlock.enabled? + %li + = link_to sherlock_transactions_path, title: 'Sherlock Transactions', + data: {toggle: 'tooltip', placement: 'bottom'} do + = icon('tachometer fw') %li = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do = icon('sign-out') -- cgit v1.2.1 From 7b5fd8742e6112491f61f27dcca2d8e441cc33a1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 12:36:01 +0100 Subject: Track the amount of times views are rendered --- .../sherlock/transactions/_file_samples.html.haml | 2 + config/locales/sherlock.en.yml | 1 + lib/gitlab/sherlock/transaction.rb | 57 ++++++++++++++++------ spec/lib/gitlab/sherlock/transaction_spec.rb | 57 ++++++++++++++++++++++ 4 files changed, 103 insertions(+), 14 deletions(-) diff --git a/app/views/sherlock/transactions/_file_samples.html.haml b/app/views/sherlock/transactions/_file_samples.html.haml index 0afdbc8dffa..4349c9b7ace 100644 --- a/app/views/sherlock/transactions/_file_samples.html.haml +++ b/app/views/sherlock/transactions/_file_samples.html.haml @@ -7,6 +7,7 @@ %thead %tr %th= t('sherlock.time_inclusive') + %th= t('sherlock.count') %th= t('sherlock.path') %th %tbody @@ -15,6 +16,7 @@ %td = sample.duration.round(2) = t('sherlock.milliseconds') + %td= @transaction.view_counts.fetch(sample.file, 1) %td= sample.relative_path %td = link_to(t('sherlock.view'), diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml index 5c146b172b1..683b09dc329 100644 --- a/config/locales/sherlock.en.yml +++ b/config/locales/sherlock.en.yml @@ -34,3 +34,4 @@ en: query_plan: Query Plan events: Events percent: '%' + count: Count diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb index 4641f15ee33..d87a4c9bb4a 100644 --- a/lib/gitlab/sherlock/transaction.rb +++ b/lib/gitlab/sherlock/transaction.rb @@ -2,7 +2,7 @@ module Gitlab module Sherlock class Transaction attr_reader :id, :type, :path, :queries, :file_samples, :started_at, - :finished_at + :finished_at, :view_counts # type - The type of transaction (e.g. "GET", "POST", etc) # path - The path of the transaction (e.g. the HTTP request path) @@ -15,20 +15,19 @@ module Gitlab @started_at = nil @finished_at = nil @thread = Thread.current + @view_counts = Hash.new(0) end # Runs the transaction and returns the block's return value. def run @started_at = Time.now - subscriber = subscribe_to_active_record - - retval = profile_lines { yield } + retval = with_subscriptions do + profile_lines { yield } + end @finished_at = Time.now - ActiveSupport::Notifications.unsubscribe(subscriber) - retval end @@ -81,21 +80,51 @@ module Gitlab retval end + def subscribe_to_active_record + ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data| + next unless same_thread? + + track_query(data[:sql].strip, data[:binds], start, finish) + end + end + + def subscribe_to_action_view + regex = /render_(template|partial)\.action_view/ + + ActiveSupport::Notifications.subscribe(regex) do |_, start, finish, _, data| + next unless same_thread? + + track_view(data[:identifier]) + end + end + private def track_query(query, bindings, start, finish) @queries << Query.new_with_bindings(query, bindings, start, finish) end - def subscribe_to_active_record - ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data| - # In case somebody uses a multi-threaded server locally (e.g. Puma) we - # _only_ want to track queries that originate from the transaction - # thread. - next unless Thread.current == @thread + def track_view(path) + @view_counts[path] += 1 + end - track_query(data[:sql].strip, data[:binds], start, finish) - end + def with_subscriptions + ar_subscriber = subscribe_to_active_record + av_subscriber = subscribe_to_action_view + + retval = yield + + ActiveSupport::Notifications.unsubscribe(ar_subscriber) + ActiveSupport::Notifications.unsubscribe(av_subscriber) + + retval + end + + # In case somebody uses a multi-threaded server locally (e.g. Puma) we + # _only_ want to track notifications that originate from the transaction + # thread. + def same_thread? + Thread.current == @thread end end end diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index bb4ff42e6e1..bb49fb65cf8 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -53,6 +53,16 @@ describe Gitlab::Sherlock::Transaction do end end + describe '#view_counts' do + it 'returns a Hash' do + expect(transaction.view_counts).to be_an_instance_of(Hash) + end + + it 'sets the default value of a key to 0' do + expect(transaction.view_counts['cats.rb']).to be_zero + end + end + describe '#run' do it 'runs the transaction' do allow(transaction).to receive(:profile_lines).and_yield @@ -162,4 +172,51 @@ describe Gitlab::Sherlock::Transaction do end end end + + describe '#subscribe_to_active_record' do + let(:subscription) { transaction.subscribe_to_active_record } + let(:time) { Time.now } + let(:query_data) { { sql: 'SELECT 1', binds: [] } } + + after do + ActiveSupport::Notifications.unsubscribe(subscription) + end + + it 'tracks executed queries' do + expect(transaction).to receive(:track_query). + with('SELECT 1', [], time, time) + + subscription.publish('test', time, time, nil, query_data) + end + + it 'only tracks queries triggered from the transaction thread' do + expect(transaction).to_not receive(:track_query) + + Thread.new { subscription.publish('test', time, time, nil, query_data) }. + join + end + end + + describe '#subscribe_to_action_view' do + let(:subscription) { transaction.subscribe_to_action_view } + let(:time) { Time.now } + let(:view_data) { { identifier: 'foo.rb' } } + + after do + ActiveSupport::Notifications.unsubscribe(subscription) + end + + it 'tracks rendered views' do + expect(transaction).to receive(:track_view).with('foo.rb') + + subscription.publish('test', time, time, nil, view_data) + end + + it 'only tracks views rendered from the transaction thread' do + expect(transaction).to_not receive(:track_view) + + Thread.new { subscription.publish('test', time, time, nil, view_data) }. + join + end + end end -- cgit v1.2.1 From 7f9f07023bbe4393620998d1be46cc65d836d5c8 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 12:42:28 +0100 Subject: Truncate transaction paths to 70 characters This ensures that long URLs don't completely mess up the layout of the table. --- app/views/sherlock/transactions/index.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/sherlock/transactions/index.html.haml b/app/views/sherlock/transactions/index.html.haml index fb31131ba88..010e1a2a902 100644 --- a/app/views/sherlock/transactions/index.html.haml +++ b/app/views/sherlock/transactions/index.html.haml @@ -27,7 +27,9 @@ - @transactions.each do |trans| %tr %td= trans.type - %td= trans.path + %td + %span{title: trans.path} + = truncate(trans.path, length: 70) %td = trans.duration.round(2) = t('sherlock.seconds') -- cgit v1.2.1 From 68843a53e6cfe5493ad46dac58b469dd969a0edf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 14:29:50 +0100 Subject: Added changelog entry for Sherlock --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 19f288d0c7e..217dc4e0043 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.2.0 (unreleased) + - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses -- cgit v1.2.1 From e357e4fbab79b0d834590b623644476a2ef980f5 Mon Sep 17 00:00:00 2001 From: Pirate Praveen Date: Mon, 9 Nov 2015 08:34:25 -0500 Subject: remove state_machine_patch.rb --- config/initializers/state_machine_patch.rb | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 config/initializers/state_machine_patch.rb diff --git a/config/initializers/state_machine_patch.rb b/config/initializers/state_machine_patch.rb deleted file mode 100644 index 51f05794361..00000000000 --- a/config/initializers/state_machine_patch.rb +++ /dev/null @@ -1,9 +0,0 @@ -# This is a patch to address the issue in https://github.com/pluginaweek/state_machine/issues/251 -# where gem 'state_machine' was not working for Rails 4.1 -module StateMachines - module Integrations - module ActiveModel - public :around_validation - end - end -end -- cgit v1.2.1 From ddd3dd72fec5d31fc76023e10f824cc079f65b63 Mon Sep 17 00:00:00 2001 From: Pirate Praveen Date: Mon, 9 Nov 2015 09:11:42 -0500 Subject: update Gemfile.lock --- Gemfile.lock | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dce728baf18..21cbf854c1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -692,7 +692,13 @@ GEM activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) stamp (0.6.0) - state_machine (1.2.0) + state_machines (0.4.0) + state_machines-activemodel (0.3.0) + activemodel (~> 4.1) + state_machines (>= 0.4.0) + state_machines-activerecord (0.3.0) + activerecord (~> 4.1) + state_machines-activemodel (>= 0.3.0) stringex (2.5.2) systemu (2.6.5) task_list (1.0.2) @@ -913,7 +919,7 @@ DEPENDENCIES spring-commands-teaspoon (~> 0.0.2) sprockets (~> 2.12.3) stamp (~> 0.6.0) - state_machine (~> 1.2.0) + state_machines-activerecord (~> 0.3.0) task_list (~> 1.0.2) teaspoon (~> 1.0.0) teaspoon-jasmine (~> 2.2.0) -- cgit v1.2.1 From b67fdfff3c245538ee5a5e9360a2613b76ebada5 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 9 Nov 2015 15:30:50 +0100 Subject: Refactor release code a bit Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/releases_controller.rb | 8 +++++--- app/controllers/projects/tags_controller.rb | 8 -------- app/models/release.rb | 2 +- app/services/create_tag_service.rb | 8 ++++++-- app/services/delete_tag_service.rb | 4 +++- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index f69a4bc729e..0825a4311cb 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -10,9 +10,7 @@ class Projects::ReleasesController < Projects::ApplicationController end def update - description = params[:release][:description] - release.update_attributes(description: description) - release.save + release.update_attributes(release_params) redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) end @@ -26,4 +24,8 @@ class Projects::ReleasesController < Projects::ApplicationController def release @release ||= @project.releases.find_or_initialize_by(tag: @tag.name) end + + def release_params + params.require(:release).permit(:description) + end end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 670f5d3067b..f512f01dc78 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -24,12 +24,6 @@ class Projects::TagsController < Projects::ApplicationController if result[:status] == :success @tag = result[:tag] - if params[:release_description] - release = @project.releases.find_or_initialize_by(tag: @tag.name) - release.update_attributes(description: params[:release_description]) - release.save - end - redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name) else @error = result[:message] @@ -39,8 +33,6 @@ class Projects::TagsController < Projects::ApplicationController def destroy DeleteTagService.new(project, current_user).execute(params[:id]) - release = project.releases.find_by(tag: params[:id]) - release.destroy if release redirect_to namespace_project_tags_path(@project.namespace, @project) end diff --git a/app/models/release.rb b/app/models/release.rb index 05647839e84..e196b84eb18 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,5 +1,5 @@ class Release < ActiveRecord::Base belongs_to :project - validates :description, :project, presence: true + validates :description, :project, :tag, presence: true end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 1a7318048b3..9917119fce2 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -1,7 +1,7 @@ require_relative 'base_service' class CreateTagService < BaseService - def execute(tag_name, ref, message) + def execute(tag_name, ref, message, release_description = nil) valid_tag = Gitlab::GitRefValidator.validate(tag_name) if valid_tag == false return error('Tag name invalid') @@ -19,8 +19,12 @@ class CreateTagService < BaseService new_tag = repository.find_tag(tag_name) if new_tag - push_data = create_push_data(project, current_user, new_tag) + if release_description + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: release_description) + end + push_data = create_push_data(project, current_user, new_tag) EventCreateService.new.push(project, current_user, push_data) project.execute_hooks(push_data.dup, :tag_push_hooks) project.execute_services(push_data.dup, :tag_push_hooks) diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb index 0c836401136..de3352a6756 100644 --- a/app/services/delete_tag_service.rb +++ b/app/services/delete_tag_service.rb @@ -11,8 +11,10 @@ class DeleteTagService < BaseService end if repository.rm_tag(tag_name) + release = project.releases.find_by(tag: tag_name) + release.destroy if release + push_data = build_push_data(tag) - EventCreateService.new.push(project, current_user, push_data) project.execute_hooks(push_data.dup, :tag_push_hooks) project.execute_services(push_data.dup, :tag_push_hooks) -- cgit v1.2.1 From b7619dad52504f8fc61bfb3b42e7f8bcc42dc06d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 9 Nov 2015 15:39:18 +0100 Subject: Add missing param and title for tag Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/tags_controller.rb | 2 +- app/views/projects/releases/edit.html.haml | 1 + app/views/projects/tags/show.html.haml | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index f512f01dc78..cb39c2b8782 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -19,7 +19,7 @@ class Projects::TagsController < Projects::ApplicationController def create result = CreateTagService.new(@project, current_user). - execute(params[:tag_name], params[:ref], params[:message]) + execute(params[:tag_name], params[:ref], params[:message], params[:release_description]) if result[:status] == :success @tag = result[:tag] diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index 78741416347..e7db09cdaa9 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -1,3 +1,4 @@ +- page_title "Edit", @tag.name, "Tags" = render "projects/commits/header_title" = render "projects/commits/head" diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index f95ae9edc4b..ebe3718afcc 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -4,8 +4,9 @@ .gray-content-block .pull-right - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do - = icon("pencil") + - if can?(current_user, :push_code, @project) + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do + = icon("pencil") = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do = icon('files-o') = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do @@ -35,4 +36,4 @@ = preserve do = markdown @release.description - else - This tag has no release notes yet. Press edit button to add one + This tag has no release notes. -- cgit v1.2.1 From 73cf0f1647806a4ce064707c6f1f416181de48ef Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 15:54:13 +0100 Subject: Only load rblineprof when actually needed This ensures the application can still boot when the "development" group is not available. --- lib/gitlab/sherlock.rb | 1 - lib/gitlab/sherlock/line_profiler.rb | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/sherlock.rb b/lib/gitlab/sherlock.rb index c4b35b24ceb..6360527a7aa 100644 --- a/lib/gitlab/sherlock.rb +++ b/lib/gitlab/sherlock.rb @@ -1,5 +1,4 @@ require 'securerandom' -require 'rblineprof' if RUBY_ENGINE == 'ruby' module Gitlab module Sherlock diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb index 152749dcc39..aa1468bff6b 100644 --- a/lib/gitlab/sherlock/line_profiler.rb +++ b/lib/gitlab/sherlock/line_profiler.rb @@ -40,6 +40,8 @@ module Gitlab # Profiles the given block using rblineprof (MRI only). def profile_mri + require 'rblineprof' + retval = nil samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield } -- cgit v1.2.1 From 4017a4fcbcb0ed1beb56718cf586c38bbb951527 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 9 Nov 2015 04:40:29 -0800 Subject: Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3. Switch back to github-linguist --- CHANGELOG | 1 + Gemfile | 10 +++------- Gemfile.lock | 25 +++++++++++++------------ 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 217dc4e0043..afb3d1086b2 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.2.0 (unreleased) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) + - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses diff --git a/Gemfile b/Gemfile index b0a7c9b9458..51848621b64 100644 --- a/Gemfile +++ b/Gemfile @@ -40,7 +40,7 @@ gem "browser", '~> 1.0.0' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 7.2.19' +gem "gitlab_git", '~> 7.2.20' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes @@ -51,11 +51,7 @@ gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap" gem 'gollum-lib', '~> 4.0.2' # Language detection -# GitLab fork of linguist does not require pygments/python dependency. -# New version of original gem also dropped pygments support but it has strict -# dependency to unstable rugged version. We have internal issue for replacing -# fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052. -gem "gitlab-linguist", "~> 3.0.1", require: "linguist" +gem "github-linguist", "~> 4.7.0", require: "linguist" # API gem 'grape', '~> 0.6.1' @@ -185,7 +181,7 @@ gem 'ace-rails-ap', '~> 2.0.1' gem 'mousetrap-rails', '~> 1.4.6' # Detect and convert string character encoding -gem 'charlock_holmes', '~> 0.6.9.4' +gem 'charlock_holmes', '~> 0.7.3' gem "sass-rails", '~> 4.0.5' gem "coffee-rails", '~> 4.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index c602d406711..d358e236302 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -175,7 +175,7 @@ GEM activesupport (>= 3.2) equalizer (0.0.11) erubis (2.7.0) - escape_utils (0.2.4) + escape_utils (1.1.0) eventmachine (1.0.8) excon (0.45.4) execjs (2.6.0) @@ -266,6 +266,11 @@ GEM json get_process_mem (0.2.0) gherkin-ruby (0.3.2) + github-linguist (4.7.0) + charlock_holmes (~> 0.7.3) + escape_utils (~> 1.1.0) + mime-types (>= 1.19) + rugged (>= 0.23.0b) github-markup (1.3.3) gitlab-flowdock-git-hook (1.0.1) flowdock (~> 0.7) @@ -276,17 +281,13 @@ GEM diff-lcs (~> 1.1) mime-types (~> 1.15) posix-spawn (~> 0.3) - gitlab-linguist (3.0.1) - charlock_holmes (~> 0.6.6) - escape_utils (~> 0.2.4) - mime-types (~> 1.19) gitlab_emoji (0.1.1) gemojione (~> 2.0) - gitlab_git (7.2.19) + gitlab_git (7.2.20) activesupport (~> 4.0) - charlock_holmes (~> 0.6) - gitlab-linguist (~> 3.0) - rugged (~> 0.22.2) + charlock_holmes (~> 0.7.3) + github-linguist (~> 4.7.0) + rugged (~> 0.23.3) gitlab_meta (7.0) gitlab_omniauth-ldap (1.2.1) net-ldap (~> 0.9) @@ -794,7 +795,7 @@ DEPENDENCIES capybara (~> 2.4.0) capybara-screenshot (~> 1.0.0) carrierwave (~> 0.9.0) - charlock_holmes (~> 0.6.9.4) + charlock_holmes (~> 0.7.3) coffee-rails (~> 4.1.0) colored (~> 1.2) colorize (~> 0.5.8) @@ -819,11 +820,11 @@ DEPENDENCIES foreman fuubar (~> 2.0.0) gemnasium-gitlab-service (~> 0.2) + github-linguist (~> 4.7.0) github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) - gitlab-linguist (~> 3.0.1) gitlab_emoji (~> 0.1) - gitlab_git (~> 7.2.19) + gitlab_git (~> 7.2.20) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) -- cgit v1.2.1 From 798873ca75e656ed0fbd2a3080022eb55a6f3106 Mon Sep 17 00:00:00 2001 From: adamliesko Date: Mon, 9 Nov 2015 17:26:01 +0100 Subject: Add notification to the former assignee upon unassignment --- CHANGELOG | 1 + app/services/notification_service.rb | 6 ++++-- spec/services/issues/update_service_spec.rb | 10 ++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 217dc4e0043..c8db4da1d74 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.2.0 (unreleased) - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from - Allow groups to appear in the search results if the group owner allows it + - Add email notification to former assignee upon unassignment v 8.1.3 - Spread out runner contacted_at updates diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index a6b22348650..16c84f4a055 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -362,7 +362,8 @@ class NotificationService def reassign_resource_email(target, project, current_user, method) assignee_id_was = previous_record(target, "assignee_id") - recipients = build_recipients(target, project, current_user) + previous_assignee = User.find(assignee_id_was) + recipients = build_recipients(target, project, current_user, [previous_assignee]) recipients.each do |recipient| mailer.send(method, recipient.id, target.id, assignee_id_was, current_user.id) @@ -377,8 +378,9 @@ class NotificationService end end - def build_recipients(target, project, current_user) + def build_recipients(target, project, current_user, previous_records = nil ) recipients = target.participants(current_user) + recipients.concat(previous_records).compact.uniq if previous_records recipients = add_project_watchers(recipients, project) recipients = reject_mention_users(recipients, project) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index a91be3b4472..4e79484f26a 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Issues::UpdateService do let(:user) { create(:user) } let(:user2) { create(:user) } - let(:issue) { create(:issue, title: 'Old title') } + let(:issue) { create(:issue, title: 'Old title', assignee_id: user.id) } let(:label) { create(:label) } let(:project) { issue.project } @@ -34,9 +34,11 @@ describe Issues::UpdateService do it { expect(@issue.labels.count).to eq(1) } it { expect(@issue.labels.first.title).to eq('Bug') } - it 'should send email to user2 about assign of new issue' do - email = ActionMailer::Base.deliveries.last - expect(email.to.first).to eq(user2.email) + it 'should send email to user2 about assign of new issue and email to user about issue unassignment' do + deliveries = ActionMailer::Base.deliveries + email = deliveries.last + recipients = deliveries.map(&:to).uniq.flatten + expect(recipients.last(2)).to include(user.email,user2.email) expect(email.subject).to include(issue.title) end -- cgit v1.2.1 From f526d43432447fe8de84ffc247b65e987ddfde84 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 9 Nov 2015 13:26:29 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 78ce4f3bfdd..4c0e9aa0fb9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses - Improved performance of replacing references in comments - Show last project commit to default branch on project home page @@ -19,7 +17,6 @@ v 8.2.0 (unreleased) - Enable shared runners to all new projects - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - - Use issue editor as cross reference comment author when issue is edited with a new mention. - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Add "New file" link to dropdown on project page - Include commit logs in project search @@ -29,9 +26,14 @@ v 8.2.0 (unreleased) - New design for project graphs page - Fix incoming email config defaults +v 8.1.4 + - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) + - Prevent redirect loop when home_page_url is set to the root URL + v 8.1.3 + - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Spread out runner contacted_at updates - - New design for user profile page + - Use issue editor as cross reference comment author when issue is edited with a new mention - Add Facebook authentication v 8.1.1 -- cgit v1.2.1 From 9822bce467b19e7b04e3e3c97bb1bcd9e4074161 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 9 Nov 2015 14:24:26 -0500 Subject: Update monthly release issue template [ci skip] --- doc/release/monthly.md | 86 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index bd8a67d1d85..4925816daaa 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -25,68 +25,84 @@ If the release is falling behind immediately warn the team. ## Create an overall issue and follow it -Create issue for GitLab CE project(internal). Name it "Release x.x.x" for easier searching. -Replace the dates with actual dates based on the number of workdays before the release. -All steps from issue template are explained below +Create an issue in the GitLab CE project. Name it "Release x.x" and tag it with +the `release` label for easier searching. Replace the dates with actual dates +based on the number of workdays before the release. All steps from issue +template are explained below: ``` -Xth: (7 working days before the 22nd) +### Xth: (7 working days before the 22nd) -- [ ] Triage the omnibus-gitlab milestone +- [ ] Triage the [Omnibus milestone] -Xth: (6 working days before the 22nd) +### Xth: (6 working days before the 22nd) -- [ ] Merge CE master in to EE master via merge request (#LINK) +- [ ] Merge CE `master` into EE `master` via merge request (#LINK) - [ ] Determine QA person and notify this person - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary -- [ ] Create CE, EE, CI RC1 versions (#LINK) -- [ ] Build RC1 packages (EE first) (#LINK) +- [ ] Create CE and EE RC1 versions (#LINK) +- [ ] Build RC1 packages -Xth: (5 working days before the 22nd) +### Xth: (5 working days before the 22nd) - [ ] Do QA and fix anything coming out of it (#LINK) -- [ ] Close the omnibus-gitlab milestone -- [ ] Prepare the blog post (#LINK) +- [ ] Close the [Omnibus milestone] +- [ ] Prepare the [blog post] -Xth: (4 working days before the 22nd) +### Xth: (4 working days before the 22nd) -- [ ] Update GitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package) -- [ ] Update ci.gitLab.com with rc1 (#LINK) (https://dev.gitlab.org/cookbooks/chef-repo/blob/master/doc/administration.md#deploy-the-package) -- [ ] Create regression issues (CE, CI) (#LINK) -- [ ] Tweet about rc1 (#LINK), proposed text: +- [ ] Update GitLab.com with RC1 +- [ ] Create the regression issue in the CE issue tracker: -> GitLab x.x.0.rc1 is available https://packages.gitlab.com/gitlab/unstable Use at your own risk. Please link regressions issues from LINK_TO_REGRESSION_ISSUE + > This is a meta issue to index possible regressions in this monthly release + > and any patch versions. + > + > Please do not raise or discuss issues directly in this issue but link to + > issues that might warrant a patch release. If there is a Merge Request + > that fixes the issue, please link to that as well. + > + > Please only post one regression issue and/or merge request per comment. + > Comments will be updated by the release manager as they are addressed. -Xth: (3 working days before the 22nd) +- [ ] Tweet about RC1 release: -- [ ] Merge CE stable branch into EE stable branch + > GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable + > Use at your own risk. Please link regressions issues from + > LINK_TO_REGRESSION_ISSUE -Xth: (2 working days before the 22nd) +### Xth: (3 working days before the 22nd) -- [ ] Check that everyone is mentioned on the blog post using `@all` (the reviewer should have done this one working day ago) -- [ ] Check that MVP is added to the mvp page (source/mvp/index.html in www-gitlab-com) +- [ ] Merge `x-y-stable` into `x-y-stable-ee` +- [ ] Check that everyone is mentioned on the [blog post] using `@all` -Xth: (1 working day before the 22nd) +### Xth: (2 working days before the 22nd) -- [ ] Merge CE stable into EE stable -- [ ] Create CE, EE, CI release candidates (#LINK) (hopefully final ones with the same commit as the release tomorrow) +- [ ] Check that MVP is added to the [MVP page] + +### Xth: (1 working day before the 22nd) + +- [ ] Merge `x-y-stable` into `x-y-stable-ee` +- [ ] Create CE and EE release candidates - [ ] Create Omnibus tags and build packages for the latest release candidates -- [ ] Update GitLab.com with the latest RC (#LINK) -- [ ] Update ci.gitLab.com with the latest RC (#LINK) +- [ ] Update GitLab.com with the latest RC -22nd before 1200 CET: +### 22nd before 1200 CET: 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) +- [ ] Merge `x-y-stable` into `x-y-stable-ee` +- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) - [ ] Create the 'x.y.0' version on version.gitlab.com -- [ ] 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 1800 CET / 8AM PST +- [ ] Try to do before 1100 CET: Create and push Omnibus tags for x.y.0 (will auto-release the packages) +- [ ] Try to do before 1200 CET: Publish the release [blog post] +- [ ] Tweet about the release +- [ ] Schedule a second Tweet of the release announcement with the same text at 1800 CET / 8AM PST + +[Omnibus milestone]: LINK_TO_OMNIBUS_MILESTONE +[blog post]: LINK_TO_WIP_BLOG_POST +[MVP page]: https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/source/mvp/index.html ``` - - - -- cgit v1.2.1 From 746e49fee9a28f509f115074d9985830de45513d Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Tue, 3 Nov 2015 16:44:14 +0200 Subject: Display target branch on MR list when it is different from project's default --- CHANGELOG | 1 + .../projects/merge_requests_controller.rb | 1 + .../projects/merge_requests/_merge_request.html.haml | 5 +++++ features/project/merge_requests.feature | 9 +++++++++ features/steps/project/merge_requests.rb | 20 ++++++++++++++++++++ 5 files changed, 36 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 44c78ed62c6..fa0b3614599 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.2.0 (unreleased) - Allow groups to appear in the search results if the group owner allows it - New design for project graphs page - Fix incoming email config defaults + - MR target branch is now visible on a list view when it is different from project's default one v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 16c42386623..b0788a2d073 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -31,6 +31,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) + @merge_requests = @merge_requests.preload(:target_project) respond_to do |format| format.html diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 300a3715292..c5234c0618c 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -41,6 +41,11 @@ %span %i.fa.fa-clock-o = merge_request.milestone.title + - if merge_request.target_project.default_branch != merge_request.target_branch +   + %span + %i.fa.fa-code-fork + = merge_request.target_branch - if merge_request.tasks? %span.task-status = merge_request.task_status diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index f423c3ba542..6cd081c868e 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -16,6 +16,15 @@ Feature: Project Merge Requests When I visit project "Shop" merge requests page Then I should see merge request "Bug NS-05" with CI status + Scenario: I should not see target branch name when it is project's default branch + Then I should see "Bug NS-04" in merge requests + And I should not see "master" branch + + Scenario: I should see target branch when it is different from default + Given project "Shop" have "Bug NS-06" open merge request + When I visit project "Shop" merge requests page + Then I should see "other_branch" branch + Scenario: I should see rejected merge requests Given I click link "Closed" Then I should see "Feature NS-03" in merge requests diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 92ec14d0d76..d5f2c4209a1 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -40,6 +40,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps expect(page).to have_content "Bug NS-04" end + step 'I should not see "master" branch' do + expect(page).not_to have_content "master" + end + + step 'I should see "other_branch" branch' do + expect(page).to have_content "other_branch" + end + step 'I should see "Bug NS-04" in merge requests' do expect(page).to have_content "Bug NS-04" end @@ -93,6 +101,18 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ) end + step 'project "Shop" have "Bug NS-06" open merge request' do + create(:merge_request, + title: "Bug NS-06", + source_project: project, + target_project: project, + source_branch: 'fix', + target_branch: 'other_branch', + author: project.users.first, + description: "# Description header" + ) + end + step 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do create(:merge_request_with_diffs, title: "Bug NS-05", -- cgit v1.2.1 From f3bf0fe7aca0672c406141ff88fffe623e507bdc Mon Sep 17 00:00:00 2001 From: Marco Vito Moscaritolo Date: Tue, 10 Nov 2015 11:25:28 +0100 Subject: Fix duplicate entry for 8.1.0 release Fix duplicate entry for 8.1.0 release and remove unreleased tag. --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d11076f4848..843589eacfb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -56,8 +56,6 @@ v 8.1.0 - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - Adds ability to create directories using the web editor (Ben Ford) - Cleanup stuck CI builds - -v 8.1.0 (unreleased) - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. -- cgit v1.2.1 From 1b7a2fc5363d7e2334f5c48940e8eca9d88354a6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 11:49:38 +0100 Subject: Improve Continuous Integration graphs page * fix commit duration graph * make graphs responsive * fix wrong padding * add a bit of explanation to colors Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/graphs/ci.html.haml | 16 ++++++--- app/views/projects/graphs/ci/_build_times.haml | 17 ++++----- app/views/projects/graphs/ci/_builds.haml | 48 ++++++++++++++++---------- app/views/projects/graphs/ci/_overall.haml | 16 ++++----- app/views/projects/graphs/commits.html.haml | 2 +- app/views/projects/graphs/show.html.haml | 2 +- lib/ci/charts.rb | 3 +- 7 files changed, 60 insertions(+), 44 deletions(-) diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml index b2dfe97938a..6fa77cc10c6 100644 --- a/app/views/projects/graphs/ci.html.haml +++ b/app/views/projects/graphs/ci.html.haml @@ -1,10 +1,16 @@ - page_title "Continuous Integration", "Graphs" = render "header_title" = render 'head' -.gray-content-block - %ul.breadcrumb.repo-breadcrumb - = commits_breadcrumbs +.gray-content-block.append-bottom-default + .oneline + A collection of graphs for Continuous Integration + #charts.ci-charts + .row + .col-md-6 + = render 'projects/graphs/ci/overall' + .col-md-6 + = render 'projects/graphs/ci/build_times' + + %hr = render 'projects/graphs/ci/builds' - = render 'projects/graphs/ci/build_times' -= render 'projects/graphs/ci/overall' diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml index c3c2f572414..c58223fd39e 100644 --- a/app/views/projects/graphs/ci/_build_times.haml +++ b/app/views/projects/graphs/ci/_build_times.haml @@ -1,21 +1,22 @@ -%fieldset - %legend +%div + %p.light Commit duration in minutes for last 30 commits - %canvas#build_timesChart.padded{width: 800, height: 300} + %canvas#build_timesChart{height: 200} :javascript var data = { labels : #{@charts[:build_times].labels.to_json}, datasets : [ { - fillColor : "#4A3", - strokeColor : "rgba(151,187,205,1)", - pointColor : "rgba(151,187,205,1)", - pointStrokeColor : "#fff", + fillColor : "rgba(220,220,220,0.5)", + strokeColor : "rgba(220,220,220,1)", + barStrokeWidth: 1, + barValueSpacing: 1, + barDatasetSpacing: 1, data : #{@charts[:build_times].build_times.to_json} } ] } var ctx = $("#build_timesChart").get(0).getContext("2d"); - new Chart(ctx).Line(data,{"scaleOverlay": true}); + new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false}); diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml index 1b0039fb834..84247455403 100644 --- a/app/views/projects/graphs/ci/_builds.haml +++ b/app/views/projects/graphs/ci/_builds.haml @@ -1,20 +1,30 @@ -%fieldset - %legend - Builds chart for last week - (#{date_from_to(Date.today - 7.days, Date.today)}) +%h4 Build charts +%p +   + %span.cgreen + = icon("circle") + success +   + %span.cgray + = icon("circle") + all - %canvas#weekChart.padded{width: 800, height: 200} +.prepend-top-default + %p.light + Builds for last week + (#{date_from_to(Date.today - 7.days, Date.today)}) + %canvas#weekChart{height: 200} -%fieldset - %legend +.prepend-top-default + %p.light Builds chart for last month (#{date_from_to(Date.today - 30.days, Date.today)}) + %canvas#monthChart{height: 200} - %canvas#monthChart.padded{width: 800, height: 300} - -%fieldset - %legend Builds chart for last year - %canvas#yearChart.padded{width: 800, height: 400} +.prepend-top-default + %p.light + Builds chart for last year + %canvas#yearChart.padded{height: 250} - [:week, :month, :year].each do |scope| :javascript @@ -22,20 +32,20 @@ labels : #{@charts[scope].labels.to_json}, datasets : [ { - fillColor : "rgba(220,220,220,0.5)", - strokeColor : "rgba(220,220,220,1)", - pointColor : "rgba(220,220,220,1)", + fillColor : "#7f8fa4", + strokeColor : "#7f8fa4", + pointColor : "#7f8fa4", pointStrokeColor : "#EEE", data : #{@charts[scope].total.to_json} }, { - fillColor : "#4A3", - strokeColor : "rgba(151,187,205,1)", - pointColor : "rgba(151,187,205,1)", + fillColor : "#44aa22", + strokeColor : "#44aa22", + pointColor : "#44aa22", pointStrokeColor : "#fff", data : #{@charts[scope].success.to_json} } ] } var ctx = $("##{scope}Chart").get(0).getContext("2d"); - new Chart(ctx).Line(data,{"scaleOverlay": true}); + new Chart(ctx).Line(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false}); diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml index 9550d719471..cf4285a2671 100644 --- a/app/views/projects/graphs/ci/_overall.haml +++ b/app/views/projects/graphs/ci/_overall.haml @@ -1,22 +1,20 @@ - ci_project = @project.gitlab_ci_project -%fieldset - %legend Overall - %p +%h4 Overall stats +%ul + %li Total: %strong= pluralize ci_project.builds.count(:all), 'build' - %p + %li Successful: %strong= pluralize ci_project.builds.success.count(:all), 'build' - %p + %li Failed: %strong= pluralize ci_project.builds.failed.count(:all), 'build' - - %p + %li Success ratio: %strong #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}% - - %p + %li Commits covered: %strong = ci_project.commits.count(:all) diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index 4e0c3e5b3de..c03790aea06 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'head' -.gray-content-block +.gray-content-block.append-bottom-default .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs_commits' %ul.breadcrumb.repo-breadcrumb diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 6bbf15d05a2..84ee843d9b7 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -2,7 +2,7 @@ = render "header_title" = render 'head' -.gray-content-block +.gray-content-block.append-bottom-default .tree-ref-holder = render 'shared/ref_switcher', destination: 'graphs' %ul.breadcrumb.repo-breadcrumb diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb index 915a4f526a6..5ff7407c6fe 100644 --- a/lib/ci/charts.rb +++ b/lib/ci/charts.rb @@ -60,7 +60,8 @@ module Ci class BuildTime < Chart def collect - commits = project.commits.joins(:builds).where("#{Ci::Build.table_name}.finished_at is NOT NULL AND #{Ci::Build.table_name}.started_at is NOT NULL").last(30) + commits = project.commits.last(30) + commits.each do |commit| @labels << commit.short_sha @build_times << (commit.duration / 60) -- cgit v1.2.1 From b1a1cadcc2056540e4eb5a6b48c87b83656633be Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 11:51:58 +0100 Subject: Add changelog item Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index d11076f4848..fe521e0f140 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.2.0 (unreleased) - Allow groups to appear in the search results if the group owner allows it - New design for project graphs page - Fix incoming email config defaults + - Improve Continuous Integration graphs page v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From e8d97eb118ecbfe2a77abf2dcd56d7ab1cfac62a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 10 Nov 2015 12:01:29 +0100 Subject: Send build name and stage in CI notification e-mail --- CHANGELOG | 1 + app/views/ci/notify/build_fail_email.html.haml | 4 ++++ app/views/ci/notify/build_fail_email.text.erb | 2 ++ app/views/ci/notify/build_success_email.html.haml | 4 ++++ app/views/ci/notify/build_success_email.text.erb | 2 ++ 5 files changed, 13 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d11076f4848..19850b58f79 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.2.0 (unreleased) - Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork - Use git follow flag for commits page when retrieve history for file or directory - Show merge request CI status on merge requests index page + - Send build name and stage in CI notification e-mail - Extend yml syntax for only and except to support specifying repository path - Enable shared runners to all new projects - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml index cefb75040e9..b0aaea89075 100644 --- a/app/views/ci/notify/build_fail_email.html.haml +++ b/app/views/ci/notify/build_fail_email.html.haml @@ -12,6 +12,10 @@ Author: #{@build.commit.git_author_name} %p Branch: #{@build.ref} +%p + Stage: #{@build.stage} +%p + Job: #{@build.name} %p Message: #{@build.commit.git_commit_message} diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb index 6de5dc10f17..17a3b9b1d33 100644 --- a/app/views/ci/notify/build_fail_email.text.erb +++ b/app/views/ci/notify/build_fail_email.text.erb @@ -4,6 +4,8 @@ Status: <%= @build.status %> Commit: <%= @build.commit.short_sha %> Author: <%= @build.commit.git_author_name %> Branch: <%= @build.ref %> +Stage: <%= @build.stage %> +Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml index 617b88f7345..24c439e50eb 100644 --- a/app/views/ci/notify/build_success_email.html.haml +++ b/app/views/ci/notify/build_success_email.html.haml @@ -13,6 +13,10 @@ Author: #{@build.commit.git_author_name} %p Branch: #{@build.ref} +%p + Stage: #{@build.stage} +%p + Job: #{@build.name} %p Message: #{@build.commit.git_commit_message} diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb index d0a43ae1c12..bc8b978c3d7 100644 --- a/app/views/ci/notify/build_success_email.text.erb +++ b/app/views/ci/notify/build_success_email.text.erb @@ -4,6 +4,8 @@ Status: <%= @build.status %> Commit: <%= @build.commit.short_sha %> Author: <%= @build.commit.git_author_name %> Branch: <%= @build.ref %> +Stage: <%= @build.stage %> +Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> -- cgit v1.2.1 From d024db0cc816d03063f889a6d3d570f70e8e896c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 10 Nov 2015 12:14:32 +0100 Subject: Remove deprecated dumped yaml file generated from previous job definitions --- CHANGELOG | 1 + app/controllers/ci/projects_controller.rb | 4 ---- app/views/projects/ci_settings/edit.html.haml | 19 ------------------- config/routes.rb | 1 - 4 files changed, 1 insertion(+), 24 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d11076f4848..2d866a5c6cc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.2.0 (unreleased) - Rename "Back to" links to "Go to" because its not always a case it point to place user come from - Allow groups to appear in the search results if the group owner allows it - New design for project graphs page + - Remove deprecated dumped yaml file generated from previous job definitions - Fix incoming email config defaults v 8.1.4 diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 809b44387ba..8406399fb60 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -26,10 +26,6 @@ module Ci redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project) end - def dumped_yaml - send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml' - end - protected def project diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml index 665556f5c20..acc912d4596 100644 --- a/app/views/projects/ci_settings/edit.html.haml +++ b/app/views/projects/ci_settings/edit.html.haml @@ -1,25 +1,6 @@ - page_title "CI Settings" -- if @ci_project.generated_yaml_config - %p.alert.alert-danger - CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_project)} - or - %a.preview-yml{:href => "#yaml-content", "data-toggle" => "modal"} preview - 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 - #yaml-content.modal.fade{"aria-hidden" => "true", "aria-labelledby" => ".gitlab-ci.yml", :role => "dialog", :tabindex => "-1"} - .modal-dialog - .modal-content - .modal-header - %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} × - %h4.modal-title Content of .gitlab-ci.yml - .modal-body - = text_area_tag :yaml, @ci_project.generated_yaml_config, size: "70x25", class: "form-control" - .modal-footer - %button.btn.btn-default{"data-dismiss" => "modal", :type => "button"} Close diff --git a/config/routes.rb b/config/routes.rb index 2028ea938e4..dfc1c571ce8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,7 +32,6 @@ Gitlab::Application.routes.draw do get :status, to: 'projects#badge' get :integration post :toggle_shared_runners - get :dumped_yaml end resources :runner_projects, only: [:create, :destroy] -- cgit v1.2.1 From 18cb430f7983ea557cf2308f5ea7c0af8b79a7b5 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Tue, 10 Nov 2015 19:17:37 +0800 Subject: Replace CoffeeScript block into JavaScript in Views. For example view: shared/issuable/_context CoffeeScript: 190ms JavaScript: 19.7ms --- app/views/admin/labels/_form.html.haml | 4 +- app/views/ci/lints/show.html.haml | 16 ++++--- app/views/groups/group_members/index.html.haml | 9 ++-- app/views/help/_shortcuts.html.haml | 8 ++-- app/views/import/bitbucket/status.html.haml | 4 +- app/views/import/fogbugz/new_user_map.html.haml | 6 +-- app/views/import/fogbugz/status.html.haml | 4 +- app/views/import/github/status.html.haml | 4 +- app/views/import/gitlab/status.html.haml | 4 +- app/views/import/gitorious/status.html.haml | 4 +- app/views/import/google_code/status.html.haml | 4 +- app/views/layouts/_search.html.haml | 4 +- app/views/projects/_activity.html.haml | 4 +- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/blob/_upload.html.haml | 6 +-- app/views/projects/buttons/_star.html.haml | 12 ++--- app/views/projects/commit/_commit_box.html.haml | 4 +- app/views/projects/graphs/commits.html.haml | 52 +++++++++++----------- app/views/projects/graphs/show.html.haml | 21 +++++---- .../projects/merge_requests/_new_compare.html.haml | 17 +++---- .../merge_requests/widget/_heading.html.haml | 7 +-- .../merge_requests/widget/_merged.html.haml | 31 +++++++------ .../merge_requests/widget/open/_accept.html.haml | 11 ++--- .../merge_requests/widget/open/_check.html.haml | 8 ++-- app/views/projects/new.html.haml | 14 +++--- app/views/projects/project_members/index.html.haml | 9 ++-- app/views/shared/issuable/_context.html.haml | 6 +-- app/views/shared/issuable/_filter.html.haml | 12 ++--- app/views/shared/projects/_list.html.haml | 4 +- app/views/users/show.html.haml | 4 +- 30 files changed, 157 insertions(+), 138 deletions(-) diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml index ad58a3837f6..a5ace4e7a3b 100644 --- a/app/views/admin/labels/_form.html.haml +++ b/app/views/admin/labels/_form.html.haml @@ -31,5 +31,5 @@ = f.submit 'Save', class: 'btn btn-save js-save-button' = link_to "Cancel", admin_labels_path, class: 'btn btn-cancel' -:coffeescript - new Labels +:javascript + new Labels(); diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml index a9b954771c5..fb9057e4882 100644 --- a/app/views/ci/lints/show.html.haml +++ b/app/views/ci/lints/show.html.haml @@ -11,15 +11,17 @@ .controls.pull-left.prepend-top-10 = submit_tag "Validate", class: 'btn btn-success submit-yml' - + %p.text-center.loading %i.fa.fa-refresh.fa-spin .results.prepend-top-20 -:coffeescript - $(".loading").hide() - $('form').bind 'ajax:beforeSend', -> - $(".loading").show() - $('form').bind 'ajax:complete', -> - $(".loading").hide() +:javascript + $(".loading").hide(); + $('form').bind('ajax:beforeSend', function() { + $(".loading").show(); + }); + $('form').bind('ajax:complete', function() { + $(".loading").hide(); + }); diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index fee4b0052b5..15d289471c9 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -36,7 +36,8 @@ = paginate @members, theme: 'gitlab' -:coffeescript - $('form.member-search-form').on 'submit', (event) -> - event.preventDefault() - Turbolinks.visit @.action + '?' + $(@).serialize() +:javascript + $('form.member-search-form').on('submit', function(event) { + event.preventDefault(); + Turbolinks.visit(this.action + '?' + $(this).serialize()); + }); diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 67349fcbd78..7e801b5332d 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -222,8 +222,8 @@ :javascript - $('.js-more-help-button').click(function(e){ - $(this).remove() - $('.hidden-shortcut').show() - e.preventDefault() + $('.js-more-help-button').click(function (e) { + $(this).remove()l + $('.hidden-shortcut').show(); + e.preventDefault(); }); diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 30bcdb86827..1f09a27e2d6 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -66,5 +66,5 @@ again. -:coffeescript - new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}") +:javascript + new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}"); diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index a701e49ac56..bc3c90294e3 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -22,7 +22,7 @@ %strong Map a FogBugz account ID to a GitLab user %p Selecting a GitLab user will add a link to the GitLab user in the descriptions - of issues and comments (e.g. "By @johnsmith"). It will also + of issues and comments (e.g. "By @johnsmith"). It will also associate and/or assign these issues and comments with the selected user. .table-holder @@ -46,5 +46,5 @@ .form-actions = submit_tag 'Continue to the next step', class: 'btn btn-create' -:coffeescript - new UsersSelect() +:javascript + new UsersSelect(); diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index beca6ab1423..b902006597b 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -48,5 +48,5 @@ %td.import-actions.job-status = button_tag "Import", class: "btn js-add-to-import" -:coffeescript - new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}") +:javascript + new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}"); diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 0669b05adca..0699321c8c0 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -43,5 +43,5 @@ %td.import-actions.job-status = button_tag "Import", class: "btn js-add-to-import" -:coffeescript - new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}") +:javascript + new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}"); diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index 3bc85059e7d..f4a2b33af21 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -43,5 +43,5 @@ %td.import-actions.job-status = button_tag "Import", class: "btn js-add-to-import" -:coffeescript - new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}") +:javascript + new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}"); diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index 2e3a535737f..71752d21efa 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -43,5 +43,5 @@ %td.import-actions.job-status = button_tag "Import", class: "btn js-add-to-import" -:coffeescript - new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}") +:javascript + new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}"); diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index c5af06edf87..8c64fd27e60 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -67,5 +67,5 @@ = link_to "import flow", new_import_google_code_path again. -:coffeescript - new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}") +:javascript + new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}"); diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index d1aa8f62463..a44f5762a6b 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -25,6 +25,6 @@ :javascript $('.search-input').on('keyup', function(e) { if (e.keyCode == 27) { - $('.search-input').blur() + $('.search-input').blur(); } - }) + }); diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 012858f70b4..101880bd105 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -8,5 +8,5 @@ .content_list{:"data-href" => activity_project_path(@project)} = spinner -:coffeescript - new Activities() +:javascript + new Activities(); diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index cb1567a2e68..a0fc8bbd752 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -21,5 +21,5 @@ = submit_tag "Create directory", class: 'btn btn-primary btn-create' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" -:coffeescript +:javascript 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 e27f1707527..a1c54e731f0 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -26,6 +26,6 @@ = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" -:coffeescript - disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file' - new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}') +:javascript + disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'); + new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}'); diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 3501dddefbe..06583902035 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -4,11 +4,13 @@ %span.count = @project.star_count - :coffeescript - $('.project-home-panel .toggle-star').on 'ajax:success', (e, data, status, xhr) -> - $(@).replaceWith(data.html) - .on 'ajax:error', (e, xhr, status, error) -> - new Flash('Star toggle failed. Try again later.', 'alert') + :javascript + $('.project-home-panel .toggle-star').on('ajax:success', function (e, data, status, xhr) { + $(this).replaceWith(data.html); + }) + .on('ajax:error', function (e, xhr, status, error) { + new Flash('Star toggle failed. Try again later.', 'alert'); + }); - else = link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index a6458b84860..776768537d0 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -55,5 +55,5 @@ %pre.commit-description = preserve(gfm(escape_once(@commit.description))) -:coffeescript - $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}") +:javascript + $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}"); diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml index 4e0c3e5b3de..eb33da7a5bb 100644 --- a/app/views/projects/graphs/commits.html.haml +++ b/app/views/projects/graphs/commits.html.haml @@ -49,26 +49,24 @@ Commits per weekday %canvas#weekday-chart -:coffeescript - responsiveChart = (selector, data) -> - options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false } +:javascript + var responsiveChart = function (selector, data) { + var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }; + // get selector by context + var ctx = selector.get(0).getContext("2d"); + // pointing parent container to make chart.js inherit its width + var container = $(selector).parent(); + var generateChart = function() { + selector.attr('width', $(container).width()); + return new Chart(ctx).Bar(data, options); + }; + // enabling auto-resizing + $(window).resize(generateChart); + return generateChart(); + }; - # get selector by context - ctx = selector.get(0).getContext("2d") - # pointing parent container to make chart.js inherit its width - container = $(selector).parent() - - generateChart = -> - selector.attr('width', $(container).width()) - new Chart(ctx).Bar(data, options) - - # enabling auto-resizing - $(window).resize( generateChart ) - - generateChart() - - chartData = (keys, values) -> - data = { + var chartData = function (keys, values) { + var data = { labels : keys, datasets : [{ fillColor : "rgba(220,220,220,0.5)", @@ -78,13 +76,15 @@ barDatasetSpacing: 1, data : values }] - } + }; + return data; + }; - hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json}) - responsiveChart($('#hour-chart'), hourData) + var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json}); + responsiveChart($('#hour-chart'), hourData); - dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json}) - responsiveChart($('#weekday-chart'), dayData) + var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json}); + responsiveChart($('#weekday-chart'), dayData); - monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json}) - responsiveChart($('#month-chart'), monthData) + var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json}); + responsiveChart($('#month-chart'), monthData); diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml index 6bbf15d05a2..9e1feadeb26 100644 --- a/app/views/projects/graphs/show.html.haml +++ b/app/views/projects/graphs/show.html.haml @@ -28,18 +28,21 @@ -:coffeescript - $.ajax +:javascript + $.ajax({ type: "GET", url: location.href, - success: (data) -> - graph = new ContributorsStatGraph() - graph.init(data) + dataType: "json", + success: function (data) { + var graph = new ContributorsStatGraph(); + graph.init(data); - $("#brush_change").change -> - graph.change_date_header() - graph.redraw_authors() + $("#brush_change").change(function(){ + graph.change_date_header(); + graph.redraw_authors(); + }); $(".stat-graph").fadeIn(); $(".loading-graph").hide(); - dataType: "json" + } + }); diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 452006162db..d9eff1f9320 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -77,12 +77,13 @@ }); -:coffeescript - - $(".merge-request-form").on 'submit', -> - if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is "" - $(".mr-compare-errors").html("You must select source and target branch to proceed") - $(".mr-compare-errors").fadeIn() - event.preventDefault() - return +:javascript + $(".merge-request-form").on('submit', function () { + if ($("#merge_request_source_branch").val() === "" || $('#merge_request_target_branch').val() === "") { + $(".mr-compare-errors").html("You must select source and target branch to proceed"); + $(".mr-compare-errors").fadeIn(); + event.preventDefault(); + return; + } + }); diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index a3551516bfe..ba5ad22bca7 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -38,6 +38,7 @@ = icon("times-circle") Could not connect to the CI server. Please check your settings and try again. - :coffeescript - $ -> - merge_request_widget.getCiStatus() + :javascript + $(function() { + merge_request_widget.getCiStatus(); + }); diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index f223f687def..a788fcea23f 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -15,7 +15,7 @@ - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) .remove_source_branch_widget - %p + %p = succeed '.' do The changes were merged into %span.label-branch= @merge_request.target_branch @@ -25,7 +25,7 @@ Remove Source Branch .remove_source_branch_widget.failed.hide - %p + %p Failed to remove source branch '#{@merge_request.source_branch}'. .remove_source_branch_in_progress.hide @@ -33,17 +33,20 @@ = icon('spinner spin') Removing source branch '#{@merge_request.source_branch}'. Please wait. This page will be automatically reload. - :coffeescript - $('.remove_source_branch').on 'click', -> - $('.remove_source_branch_widget').hide() - $('.remove_source_branch_in_progress').show() - - $(".remove_source_branch").on "ajax:success", (e, data, status, xhr) -> - location.reload() - - $(".remove_source_branch").on "ajax:error", (e, data, status, xhr) -> - $('.remove_source_branch_widget').hide() - $('.remove_source_branch_in_progress').hide() - $('.remove_source_branch_widget.failed').show() + :javascript + $('.remove_source_branch').on('click', function() { + $('.remove_source_branch_widget').hide(); + $('.remove_source_branch_in_progress').show(); + }); + + $(".remove_source_branch").on("ajax:success", function (e, data, status, xhr) { + location.reload(); + }); + + $(".remove_source_branch").on("ajax:error", function (e, data, status, xhr) { + $('.remove_source_branch_widget').hide(); + $('.remove_source_branch_in_progress').hide(); + $('.remove_source_branch_widget.failed').show(); + }); diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 689247f3186..9b31014b581 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -20,8 +20,9 @@ text: @merge_request.merge_commit_message, rows: 14, hint: true - :coffeescript - $('.accept-mr-form').on 'ajax:before', -> - btn = $('.accept_merge_request') - btn.disable() - btn.html(" Merge in progress") + :javascript + $('.accept-mr-form').on('ajax:before', function() { + var btn = $('.accept_merge_request'); + btn.disable(); + btn.html(" Merge in progress"); + }); diff --git a/app/views/projects/merge_requests/widget/open/_check.html.haml b/app/views/projects/merge_requests/widget/open/_check.html.haml index b6b8974297e..e16878ba513 100644 --- a/app/views/projects/merge_requests/widget/open/_check.html.haml +++ b/app/views/projects/merge_requests/widget/open/_check.html.haml @@ -2,6 +2,8 @@ = icon("spinner spin") Checking ability to merge automatically… -:coffeescript - $ -> - merge_request_widget.getMergeStatus() +:javascript + $(function() { + merge_request_widget.getMergeStatus(); + }); + diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index daab2326bc7..a02c12f06a8 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -124,9 +124,11 @@ Creating project & repository. %p Please wait a moment, this page will automatically refresh when ready. -:coffeescript - $('.how_to_import_link').bind 'click', (e) -> - e.preventDefault() - import_modal = $(this).next(".modal").show() - $('.modal-header .close').bind 'click', -> - $(".modal").hide() +:javascript + $('.how_to_import_link').bind('click', function (e) { + e.preventDefault(); + var import_modal = $(this).next(".modal").show(); + }); + $('.modal-header .close').bind('click', function() { + $(".modal").hide(); + }); diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 82809bec5b8..9fc4be583cc 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -29,7 +29,8 @@ - if @group = render "group_members", members: @group_members -:coffeescript - $('form.member-search-form').on 'submit', (event) -> - event.preventDefault() - Turbolinks.visit @.action + '?' + $(@).serialize() +:javascript + $('form.member-search-form').on('submit', function (event) { + event.preventDefault(); + Turbolinks.visit(this.action + '?' + $(this).serialize()); + }); diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index cba18c14568..be66256c7b0 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -45,6 +45,6 @@ .description-block.subscribed{class: ( 'hidden' unless subscribed )} You're receiving notifications because you're subscribed to this thread. -:coffeescript - new Subscription("#{toggle_subscription_path(issuable)}") - new IssuableContext() +:javascript + new Subscription("#{toggle_subscription_path(issuable)}"); + new IssuableContext(); diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index 0e4e9c0987a..d1231438ee4 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -60,9 +60,9 @@ = hidden_field_tag :state_event, params[:state_event] = button_tag "Update issues", class: "btn update_selected_issues btn-save" -:coffeescript - new UsersSelect() - - $('form.filter-form').on 'submit', (event) -> - event.preventDefault() - Turbolinks.visit @.action + '&' + $(@).serialize() +:javascript + new UsersSelect(); + $('form.filter-form').on('submit', function (event) { + event.preventDefault(); + Turbolinks.visit(this.action + '&' + $(this).serialize()); + }); diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 357cfd6a370..e5ffe1e29ae 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -17,5 +17,5 @@ = link_to '#', class: 'js-expand' do Show all -:coffeescript - new ProjectsList() +:javascript + new ProjectsList(); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 5a15c6c244a..30992412184 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -115,5 +115,5 @@ projects: @projects.sort_by(&:star_count).reverse, projects_limit: 10, stars: true, avatar: true -:coffeescript - $(".user-calendar").load("#{user_calendar_path}") +:javascript + $(".user-calendar").load("#{user_calendar_path}"); -- cgit v1.2.1 From d0e3e823a2dd56260550aec648b0cbfae64543ae Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 12 Oct 2015 23:47:32 +0200 Subject: Implement Build Artifacts - Offloads uploading to GitLab Workhorse - Use /authorize request for fast uploading - Added backup recipes for artifacts - Support download acceleration using X-Sendfile --- .gitignore | 3 + CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 6 +- .../admin/application_settings_controller.rb | 1 + app/controllers/projects/builds_controller.rb | 27 +++ app/models/ability.rb | 1 + app/models/application_setting.rb | 1 + app/models/ci/build.rb | 17 ++ app/models/commit_status.rb | 4 + app/uploaders/artifact_uploader.rb | 50 ++++++ .../admin/application_settings/_form.html.haml | 5 + app/views/projects/builds/show.html.haml | 3 + .../commit_statuses/_commit_status.html.haml | 3 + config/initializers/1_settings.rb | 1 + config/routes.rb | 1 + .../20151013092124_add_artifacts_file_to_builds.rb | 5 + ...d_max_artifacts_size_to_application_settings.rb | 5 + db/schema.rb | 4 +- doc/ci/yaml/README.md | 15 ++ doc/install/installation.md | 5 + doc/raketasks/backup_restore.md | 3 +- lib/api/helpers.rb | 44 +++++ lib/backup/artifacts.rb | 13 ++ lib/backup/manager.rb | 2 +- lib/ci/api/api.rb | 1 + lib/ci/api/builds.rb | 102 +++++++++++ lib/ci/api/entities.rb | 7 + lib/ci/api/helpers.rb | 11 ++ lib/ci/gitlab_ci_yaml_processor.rb | 9 +- lib/file_streamer.rb | 16 ++ lib/gitlab/current_settings.rb | 1 + lib/support/nginx/gitlab | 16 ++ lib/support/nginx/gitlab-ssl | 16 ++ lib/tasks/gitlab/backup.rake | 21 +++ lib/uploaded_file.rb | 37 ++++ shared/artifacts/.gitkeep | 0 shared/tmp/artifacts-cache/.gitkeep | 0 shared/tmp/artifacts-uploads/.gitkeep | 0 spec/features/builds_spec.rb | 21 +++ spec/features/commits_spec.rb | 16 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 38 +++++ spec/models/build_spec.rb | 15 ++ spec/requests/api/users_spec.rb | 15 +- spec/requests/ci/api/builds_spec.rb | 186 ++++++++++++++++++++- spec/tasks/gitlab/backup_rake_spec.rb | 14 +- 46 files changed, 740 insertions(+), 24 deletions(-) create mode 100644 app/uploaders/artifact_uploader.rb create mode 100644 db/migrate/20151013092124_add_artifacts_file_to_builds.rb create mode 100644 db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb create mode 100644 lib/backup/artifacts.rb create mode 100644 lib/file_streamer.rb create mode 100644 lib/uploaded_file.rb create mode 100644 shared/artifacts/.gitkeep create mode 100644 shared/tmp/artifacts-cache/.gitkeep create mode 100644 shared/tmp/artifacts-uploads/.gitkeep diff --git a/.gitignore b/.gitignore index 73bde4cc761..fd137a50473 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,9 @@ nohup.out public/assets/ public/uploads.* public/uploads/ +shared/artifacts/ +shared/tmp/artifacts-uploads/ +shared/tmp/artifacts-cache/ rails_best_practices_output.html /tags tmp/ diff --git a/CHANGELOG b/CHANGELOG index d11076f4848..e54e7329ca1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -95,6 +95,7 @@ v 8.1.0 (unreleased) - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) + - Added build artifacts - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area - Move CI variables page to project settings area diff --git a/Gemfile b/Gemfile index 51848621b64..49e35e873ba 100644 --- a/Gemfile +++ b/Gemfile @@ -54,7 +54,7 @@ gem 'gollum-lib', '~> 4.0.2' gem "github-linguist", "~> 4.7.0", require: "linguist" # API -gem 'grape', '~> 0.6.1' +gem 'grape', '~> 0.13.0' gem 'grape-entity', '~> 0.4.2' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' diff --git a/Gemfile.lock b/Gemfile.lock index d358e236302..310694ff6af 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -306,10 +306,10 @@ GEM gon (5.0.4) actionpack (>= 2.3.0) json - grape (0.6.1) + grape (0.13.0) activesupport builder - hashie (>= 1.2.0) + hashie (>= 2.1.0) multi_json (>= 1.3.2) multi_xml (>= 0.5.2) rack (>= 1.3.0) @@ -829,7 +829,7 @@ DEPENDENCIES gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) gon (~> 5.0.0) - grape (~> 0.6.1) + grape (~> 0.13.0) grape-entity (~> 0.4.2) haml-rails (~> 0.9.0) hipchat (~> 1.5.0) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 3d9c59050ff..a9bcfc7456a 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -58,6 +58,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :admin_notification_email, :user_oauth_applications, :shared_runners_enabled, + :max_artifacts_size, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 953f30e7c03..4638f77b887 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -3,6 +3,7 @@ class Projects::BuildsController < Projects::ApplicationController before_action :build, except: [:index, :cancel_all] before_action :authorize_manage_builds!, except: [:index, :show, :status] + before_action :authorize_download_build_artifacts!, only: [:download] layout "project" @@ -51,6 +52,18 @@ class Projects::BuildsController < Projects::ApplicationController redirect_to build_path(build) end + def download + unless artifacts_file.file_storage? + return redirect_to artifacts_file.url + end + + unless artifacts_file.exists? + return not_found! + end + + send_file artifacts_file.path, disposition: 'attachment' + end + def status render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha) end @@ -67,6 +80,10 @@ class Projects::BuildsController < Projects::ApplicationController @build ||= ci_project.builds.unscoped.find_by!(id: params[:id]) end + def artifacts_file + build.artifacts_file + end + def build_path(build) namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) end @@ -76,4 +93,14 @@ class Projects::BuildsController < Projects::ApplicationController return page_404 end end + + def authorize_download_build_artifacts! + unless can?(current_user, :download_build_artifacts, @project) + if current_user.nil? + return authenticate_user! + else + return render_404 + end + end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index b72178fa126..5ae28d5133e 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -154,6 +154,7 @@ class Ability :create_merge_request, :create_wiki, :manage_builds, + :download_build_artifacts, :push_code ] end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 266045f7afa..fa7cf2464ad 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -89,6 +89,7 @@ class ApplicationSetting < ActiveRecord::Base restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], + max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'], ) end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 7f185ae7cc3..0ec7e210321 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -39,6 +39,8 @@ module Ci scope :ignore_failures, ->() { where(allow_failure: false) } scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) } + mount_uploader :artifacts_file, ArtifactUploader + acts_as_taggable # To prevent db load megabytes of data from trace @@ -217,6 +219,14 @@ module Ci "#{dir_to_trace}/#{id}.log" end + def token + project.token + end + + def valid_token? token + project.valid_token? token + end + def target_url Gitlab::Application.routes.url_helpers. namespace_project_build_url(gl_project.namespace, gl_project, self) @@ -248,6 +258,13 @@ module Ci pending? && !any_runners_online? end + def download_url + if artifacts_file.exists? + Gitlab::Application.routes.url_helpers. + download_namespace_project_build_path(gl_project.namespace, gl_project, self) + end + end + private def yaml_variables diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 7d54d83974a..d346c5d35d2 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -92,4 +92,8 @@ class CommitStatus < ActiveRecord::Base def show_warning? false end + + def download_url + nil + end end diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb new file mode 100644 index 00000000000..848e0bcbde1 --- /dev/null +++ b/app/uploaders/artifact_uploader.rb @@ -0,0 +1,50 @@ +# encoding: utf-8 +class ArtifactUploader < CarrierWave::Uploader::Base + storage :file + + attr_accessor :build, :field + + def self.artifacts_path + File.expand_path('shared/artifacts/', Rails.root) + end + + def self.artifacts_upload_path + File.expand_path('shared/tmp/artifacts-uploads/', Rails.root) + end + + def self.artifacts_cache_path + File.expand_path('shared/tmp/artifacts-cache/', Rails.root) + end + + def initialize(build, field) + @build, @field = build, field + end + + def artifacts_path + File.join(build.created_at.utc.strftime('%Y_%m'), build.project.id.to_s, build.id.to_s) + end + + def store_dir + File.join(ArtifactUploader.artifacts_path, artifacts_path) + end + + def cache_dir + File.join(ArtifactUploader.artifacts_cache_path, artifacts_path) + end + + def file_storage? + self.class.storage == CarrierWave::Storage::File + end + + def exists? + file.try(:exists?) + end + + def move_to_cache + true + end + + def move_to_store + true + end +end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 7253218c2e9..ddaf0e0e8ff 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -139,5 +139,10 @@ = f.check_box :shared_runners_enabled Enable shared runners for a new projects + .form-group + = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :max_artifacts_size, class: 'form-control' + .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 3374d5432a5..7661452e6ec 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -87,6 +87,9 @@ Test coverage %h1 #{@build.coverage}% + - if current_user && can?(current_user, :download_build_artifacts, @project) && @build.download_url + .build-widget.center + = link_to "Download artifacts", @build.download_url, class: 'btn btn-sm btn-primary' .build-widget %h4.title diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index c255559b88c..9a0e7bff3f1 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -61,6 +61,9 @@ %td .pull-right + - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url + = link_to commit_status.download_url, title: 'Download artifacts' do + %i.fa.fa-download - if current_user && can?(current_user, :manage_builds, commit_status.gl_project) - if commit_status.active? - if commit_status.cancel_url diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8192d727f2a..b39e263e39a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -186,6 +186,7 @@ Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_br Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) +Settings.gitlab_ci['max_artifacts_size'] ||= 100 # # Reply by email diff --git a/config/routes.rb b/config/routes.rb index 2028ea938e4..c892c034f01 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -611,6 +611,7 @@ Gitlab::Application.routes.draw do member do get :status post :cancel + get :download post :retry end end diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb new file mode 100644 index 00000000000..5a299f7b26d --- /dev/null +++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb @@ -0,0 +1,5 @@ +class AddArtifactsFileToBuilds < ActiveRecord::Migration + def change + add_column :ci_builds, :artifacts_file, :text + end +end diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb new file mode 100644 index 00000000000..01d8c0f043e --- /dev/null +++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 116c0c8d97d..f631d73f334 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: 20151105094515) do +ActiveRecord::Schema.define(version: 20151109100728) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -48,6 +48,7 @@ ActiveRecord::Schema.define(version: 20151105094515) do t.text "help_page_text" t.string "admin_notification_email" t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false end create_table "audit_events", force: true do |t| @@ -108,6 +109,7 @@ ActiveRecord::Schema.define(version: 20151105094515) do t.string "type" t.string "target_url" t.string "description" + t.text "artifacts_file" 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 diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index d117a2969be..d8504aca86a 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -141,6 +141,7 @@ job_name: | tags | optional | Defines a list of tags which are used to select runner | | allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status | | when | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` | +| artifacts | optional | Define list build artifacts | ### script `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`. @@ -258,6 +259,20 @@ The above script will: 1. Execute `cleanup_build` only when the `build` failed, 2. Always execute `cleanup` as the last step in pipeline. +### artifacts +`artifacts` is used to specify list of files and directories which should be attached to build after success. + +``` +artifacts: +- binaries/ +- .config +``` + +The above definition will archive all files in `binaries/` and `.config`. +The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download. + +This feature requires GitLab Runner v 0.7.0. + ## Validate the .gitlab-ci.yml Each instance of GitLab CI has an embedded debug tool called Lint. You can find the link to the Lint in the project's settings page or use short url `/lint`. diff --git a/doc/install/installation.md b/doc/install/installation.md index f17477a3218..0ece8bd9315 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -246,6 +246,11 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Change the permissions of the directory where CI build traces are stored sudo chmod -R u+rwX builds/ + # Change the permissions of the directory where CI artifacts are stored + sudo chmod -R u+rwX shared/artifacts/ + sudo chmod -R u+rwX shared/tmp/artifacts-uploads/ + sudo chmod -R u+rwX shared/tmp/artifacts-cache/ + # Copy the example Unicorn config sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 606532a6fbe..1a5442cdac7 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -29,7 +29,8 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` Also you can choose what should be backed up by adding environment variable SKIP. Available options: db, -uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time. +uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts). +Use a comma to specify several options at the same time. ``` sudo gitlab-rake gitlab:backup:create SKIP=db,uploads diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 652bdf9b278..b980cd8391e 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -133,6 +133,12 @@ module API authorize! :admin_project, user_project end + def require_gitlab_workhorse! + unless headers['Gitlab-Git-Http-Server'].present? || headers['GitLab-Git-HTTP-Server'].present? + forbidden!('Request should be executed via GitLab Workhorse') + end + end + def can?(object, action, subject) abilities.allowed?(object, action, subject) end @@ -234,6 +240,10 @@ module API render_api_error!(message || '409 Conflict', 409) end + def file_to_large! + render_api_error!('413 Request Entity Too Large', 413) + end + def render_validation_error!(model) if model.errors.any? render_api_error!(model.errors.messages || '400 Bad Request', 400) @@ -282,6 +292,40 @@ module API end end + # file helpers + + def uploaded_file!(uploads_path) + required_attributes! [:file] + + # sanitize file paths + # this requires for all paths to exist + uploads_path = File.realpath(uploads_path) + file_path = File.realpath(params[:file]) + bad_request!('Bad file path') unless file_path.start_with?(uploads_path) + + UploadedFile.new( + file_path, + params[:filename], + params[:filetype] || 'application/octet-stream', + ) + end + + def present_file!(path, filename, content_type = 'application/octet-stream') + filename ||= File.basename(path) + header['Content-Disposition'] = "attachment; filename=#{filename}" + header['Content-Transfer-Encoding'] = 'binary' + content_type content_type + + # Support download acceleration + case headers['X-Sendfile-Type'] + when 'X-Sendfile' + header['X-Sendfile'] = path + body + else + file FileStreamer.new(path) + end + end + private def add_pagination_headers(paginated, per_page) diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb new file mode 100644 index 00000000000..51fa3867e67 --- /dev/null +++ b/lib/backup/artifacts.rb @@ -0,0 +1,13 @@ +require 'backup/files' + +module Backup + class Artifacts < Files + def initialize + super('artifacts', ArtifactUploader.artifacts_path) + end + + def create_files_dir + Dir.mkdir(app_files_dir, 0700) + end + end +end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index f011fd03de0..9e15d5411a1 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,7 +150,7 @@ module Backup private def backup_contents - folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"] + folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"] end def folders_to_backup diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 0a4cbf69b63..07e68216d7f 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -27,6 +27,7 @@ module Ci helpers Helpers helpers ::API::Helpers + helpers Gitlab::CurrentSettings mount Builds mount Commits diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 83ca1e6481c..622849c4b11 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -47,6 +47,108 @@ module Ci build.drop end end + + # Authorize artifacts uploading for build - Runners only + # + # Parameters: + # id (required) - The ID of a build + # token (required) - The build authorization token + # size (optional) - the size of uploaded file + # Example Request: + # POST /builds/:id/artifacts/authorize + post ":id/artifacts/authorize" do + require_gitlab_workhorse! + build = Ci::Build.find_by_id(params[:id]) + not_found! unless build + authenticate_build_token!(build) + forbidden!('build is not running') unless build.running? + + if params[:filesize] + file_size = params[:filesize].to_i + file_to_large! unless file_size < max_artifacts_size + end + + status 200 + { temp_path: ArtifactUploader.artifacts_upload_path } + end + + # Upload artifacts to build - Runners only + # + # Parameters: + # id (required) - The ID of a build + # token (required) - The build authorization token + # Headers: + # Content-Type - File content type + # Content-Disposition - File media type and real name + # BUILD-TOKEN (required) - The build authorization token, the same as token + # Body: + # The file content + # + # Parameters (set by GitLab Workhorse): + # file - path to locally stored body (generated by Workhorse) + # filename - real filename as send in Content-Disposition + # filetype - real content type as send in Content-Type + # filesize - real file size as send in Content-Length + # Example Request: + # POST /builds/:id/artifacts + post ":id/artifacts" do + require_gitlab_workhorse! + build = Ci::Build.find_by_id(params[:id]) + not_found! unless build + authenticate_build_token!(build) + forbidden!('build is not running') unless build.running? + + file = uploaded_file!(ArtifactUploader.artifacts_upload_path) + file_to_large! unless file.size < max_artifacts_size + + if build.update_attributes(artifacts_file: file) + present build, with: Entities::Build + else + render_validation_error!(build) + end + end + + # Download the artifacts file from build - Runners only + # + # Parameters: + # id (required) - The ID of a build + # token (required) - The build authorization token + # Headers: + # BUILD-TOKEN (required) - The build authorization token, the same as token + # Example Request: + # GET /builds/:id/artifacts + get ":id/artifacts" do + build = Ci::Build.find_by_id(params[:id]) + not_found! unless build + authenticate_build_token!(build) + artifacts_file = build.artifacts_file + + unless artifacts_file.file_storage? + return redirect_to build.artifacts_file.url + end + + unless artifacts_file.exists? + not_found! + end + + present_file!(artifacts_file.path, artifacts_file.filename) + end + + # Remove the artifacts file from build + # + # Parameters: + # id (required) - The ID of a build + # token (required) - The build authorization token + # Headers: + # BUILD-TOKEN (required) - The build authorization token, the same as token + # Example Request: + # DELETE /builds/:id/artifacts + delete ":id/artifacts" do + build = Ci::Build.find_by_id(params[:id]) + not_found! unless build + authenticate_build_token!(build) + build.remove_artifacts_file! + end end end end diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index b80c0b8b273..750f421872d 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -11,10 +11,16 @@ module Ci expose :builds end + class ArtifactFile < Grape::Entity + expose :filename, :size + end + class Build < Grape::Entity expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url, :before_sha, :allow_git_fetch, :project_name + expose :name, :token, :stage + expose :options do |model| model.options end @@ -24,6 +30,7 @@ module Ci end expose :variables + expose :artifacts_file, using: ArtifactFile end class Runner < Grape::Entity diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 7e4986b6af3..02502333756 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -1,6 +1,8 @@ module Ci module API module Helpers + BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN" + BUILD_TOKEN_PARAM = :token UPDATE_RUNNER_EVERY = 60 def authenticate_runners! @@ -15,6 +17,11 @@ module Ci forbidden! unless project.valid_token?(params[:project_token]) end + def authenticate_build_token!(build) + token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s + forbidden! unless token && build.valid_token?(token) + end + def update_runner_last_contact # Use a random threshold to prevent beating DB updates contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY) @@ -32,6 +39,10 @@ module Ci info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"]) current_runner.update(info) end + + def max_artifacts_size + current_application_settings.max_artifacts_size.megabytes.to_i + end end end end diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 0f57a4f53ab..6f9af5388ca 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -5,7 +5,7 @@ module Ci DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables] - ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when] + ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts] attr_reader :before_script, :image, :services, :variables, :path @@ -77,7 +77,8 @@ module Ci when: job[:when] || 'on_success', options: { image: job[:image] || @image, - services: job[:services] || @services + services: job[:services] || @services, + artifacts: job[:artifacts] }.compact } end @@ -159,6 +160,10 @@ module Ci raise ValidationError, "#{name} job: except parameter should be an array of strings" end + if job[:artifacts] && !validate_array_of_strings(job[:artifacts]) + raise ValidationError, "#{name}: artifacts parameter should be an array of strings" + end + if job[:allow_failure] && !job[:allow_failure].in?([true, false]) raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" end diff --git a/lib/file_streamer.rb b/lib/file_streamer.rb new file mode 100644 index 00000000000..4e3c6d3c773 --- /dev/null +++ b/lib/file_streamer.rb @@ -0,0 +1,16 @@ +class FileStreamer #:nodoc: + attr_reader :to_path + + def initialize(path) + @to_path = path + end + + # Stream the file's contents if Rack::Sendfile isn't present. + def each + File.open(to_path, 'rb') do |file| + while chunk = file.read(16384) + yield chunk + end + end + end +end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index cd84afa31d5..2d3e32d9539 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -25,6 +25,7 @@ module Gitlab session_expire_delay: Settings.gitlab['session_expire_delay'], import_sources: Settings.gitlab['import_sources'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], + max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'], ) end diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index e767027dc29..e511d5e4b4b 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -131,6 +131,22 @@ server { return 418; } + # Build artifacts should be submitted to this location + location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block + error_page 418 = @gitlab-git-http-server; + return 418; + } + + # Build artifacts should be submitted to this location + location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block + error_page 418 = @gitlab-git-http-server; + return 418; + } + location @gitlab-workhorse { ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 4d31e31f8d5..47b1ec8cb0c 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -178,6 +178,22 @@ server { return 418; } + # Build artifacts should be submitted to this location + location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block + error_page 418 = @gitlab-git-http-server; + return 418; + } + + # Build artifacts should be submitted to this location + location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block + error_page 418 = @gitlab-git-http-server; + return 418; + } + location @gitlab-workhorse { ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index f20c7f71ba5..3c46bcea40e 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -12,6 +12,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:repo:create"].invoke Rake::Task["gitlab:backup:uploads:create"].invoke Rake::Task["gitlab:backup:builds:create"].invoke + Rake::Task["gitlab:backup:artifacts:create"].invoke backup = Backup::Manager.new backup.pack @@ -32,6 +33,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:repo:restore"].invoke unless backup.skipped?("repositories") Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads") Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds") + Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts") Rake::Task["gitlab:shell:setup"].invoke backup.cleanup @@ -113,6 +115,25 @@ namespace :gitlab do end end + namespace :artifacts do + task create: :environment do + $progress.puts "Dumping artifacts ... ".blue + + if ENV["SKIP"] && ENV["SKIP"].include?("artifacts") + $progress.puts "[SKIPPED]".cyan + else + Backup::Artifacts.new.dump + $progress.puts "done".green + end + end + + task restore: :environment do + $progress.puts "Restoring artifacts ... ".blue + Backup::Artifacts.new.restore + $progress.puts "done".green + end + end + def configure_cron_mode if ENV['CRON'] # We need an object we can say 'puts' and 'print' to; let's use a diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb new file mode 100644 index 00000000000..d4291f012d3 --- /dev/null +++ b/lib/uploaded_file.rb @@ -0,0 +1,37 @@ +require "tempfile" +require "fileutils" + +# Taken from: Rack::Test::UploadedFile +class UploadedFile + + # The filename, *not* including the path, of the "uploaded" file + attr_reader :original_filename + + # The tempfile + attr_reader :tempfile + + # The content type of the "uploaded" file + attr_accessor :content_type + + def initialize(path, filename, content_type = "text/plain") + raise "#{path} file does not exist" unless ::File.exist?(path) + + @content_type = content_type + @original_filename = filename || ::File.basename(path) + @tempfile = File.new(path, 'rb') + end + + def path + @tempfile.path + end + + alias_method :local_path, :path + + def method_missing(method_name, *args, &block) #:nodoc: + @tempfile.__send__(method_name, *args, &block) + end + + def respond_to?(method_name, include_private = false) #:nodoc: + @tempfile.respond_to?(method_name, include_private) || super + end +end diff --git a/shared/artifacts/.gitkeep b/shared/artifacts/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/shared/tmp/artifacts-cache/.gitkeep b/shared/tmp/artifacts-cache/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/shared/tmp/artifacts-uploads/.gitkeep b/shared/tmp/artifacts-uploads/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 158e85e598f..5213ce1099f 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Builds" do + let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + before do login_as(:user) @commit = FactoryGirl.create :ci_commit @@ -66,6 +68,15 @@ describe "Builds" do it { expect(page).to have_content @commit.sha[0..7] } it { expect(page).to have_content @commit.git_commit_message } it { expect(page).to have_content @commit.git_author_name } + + context "Download artifacts" do + before do + @build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + end + + it { expect(page).to have_content 'Download artifacts' } + end end describe "POST /:project/builds/:id/cancel" do @@ -90,4 +101,14 @@ describe "Builds" do it { expect(page).to have_content 'pending' } it { expect(page).to have_content 'Cancel' } end + + describe "GET /:project/builds/:id/download" do + before do + @build.update_attributes(artifacts_file: artifacts_file) + visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + click_link 'Download artifacts' + end + + it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) } + end end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 340924fafe7..90739cd6a28 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -19,7 +19,7 @@ describe "Commits" do stub_ci_commit_to_return_yaml_file end - describe "GET /:project/commits/:sha" do + describe "GET /:project/commits/:sha/ci" do before do visit ci_status_path(@commit) end @@ -29,6 +29,20 @@ describe "Commits" do it { expect(page).to have_content @commit.git_author_name } end + context "Download artifacts" do + let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + + before do + @build.update_attributes(artifacts_file: artifacts_file) + end + + it do + visit ci_status_path(@commit) + click_on "Download artifacts" + expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) + end + end + describe "Cancel all builds" do it "cancels commit" do visit ci_status_path(@commit) diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 9963f76f993..5e3779af19e 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -333,6 +333,37 @@ module Ci end end + describe "Artifacts" do + it "returns artifacts when defined" do + config = YAML.dump({ + image: "ruby:2.1", + services: ["mysql"], + before_script: ["pwd"], + rspec: { artifacts: ["logs/", "binaries/"], script: "rspec" } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ + except: nil, + stage: "test", + stage_idx: 1, + name: :rspec, + only: nil, + commands: "pwd\nrspec", + tag_list: [], + options: { + image: "ruby:2.1", + services: ["mysql"], + artifacts: ["logs/", "binaries/"] + }, + when: "on_success", + allow_failure: false + }) + end + end + describe "Error handling" do it "indicates that object is invalid" do expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) @@ -491,6 +522,13 @@ module Ci GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always") end + + it "returns errors if job artifacts is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: "string" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts parameter should be an array of strings") + end end end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 7f5abb83ac2..839b4c6b16e 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -400,4 +400,19 @@ describe Ci::Build do end end end + + describe :download_url do + subject { build.download_url } + + it "should be nil if artifact doesn't exist" do + build.update_attributes(artifacts_file: nil) + is_expected.to be_nil + end + + it 'should be nil if artifact exist' do + gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') + build.update_attributes(artifacts_file: gif) + is_expected.to_not be_nil + end + end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index d26a300ed82..a9ef2fe5885 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -343,8 +343,9 @@ describe API::API, api: true do end.to change{ user.keys.count }.by(1) end - it "should raise error for invalid ID" do - expect{post api("/users/ASDF/keys", admin) }.to raise_error(ActionController::RoutingError) + it "should return 405 for invalid ID" do + post api("/users/ASDF/keys", admin) + expect(response.status).to eq(405) end end @@ -374,9 +375,9 @@ describe API::API, api: true do expect(json_response.first['title']).to eq(key.title) end - it "should return 404 for invalid ID" do + it "should return 405 for invalid ID" do get api("/users/ASDF/keys", admin) - expect(response.status).to eq(404) + expect(response.status).to eq(405) end end end @@ -434,7 +435,8 @@ describe API::API, api: true do end it "should raise error for invalid ID" do - expect{post api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError) + post api("/users/ASDF/emails", admin) + expect(response.status).to eq(405) end end @@ -465,7 +467,8 @@ describe API::API, api: true do end it "should raise error for invalid ID" do - expect{put api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError) + put api("/users/ASDF/emails", admin) + expect(response.status).to eq(405) end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 88218a93e1f..92ea25a3723 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -41,7 +41,7 @@ describe Ci::API::API do it "should return 404 error if no builds for specific runner" do commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project) - FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) + FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: runner.token @@ -50,7 +50,7 @@ describe Ci::API::API do it "should return 404 error if no builds for shared runner" do commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) - FactoryGirl.create(:ci_build, commit: commit, status: 'pending' ) + FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: shared_runner.token @@ -79,7 +79,7 @@ describe Ci::API::API do { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, { "key" => "DB_NAME", "value" => "postgres", "public" => true }, - { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }, + { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false } ]) end @@ -122,5 +122,185 @@ describe Ci::API::API do expect(build.reload.trace).to eq 'hello_world' end end + + context "Artifacts" do + let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } + let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } + let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } + let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } + let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") } + let(:post_url) { ci_api("/builds/#{build.id}/artifacts") } + let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") } + let(:get_url) { ci_api("/builds/#{build.id}/artifacts") } + let(:headers) { { "Gitlab-Git-Http-Server" => "1.0" } } + let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) } + + describe "POST /builds/:id/artifacts/authorize" do + context "should authorize posting artifact to running build" do + before do + build.run! + end + + it "using token as parameter" do + post authorize_url, { token: build.project.token }, headers + expect(response.status).to eq(200) + expect(json_response["temp_path"]).to_not be_nil + end + + it "using token as header" do + post authorize_url, {}, headers_with_token + expect(response.status).to eq(200) + expect(json_response["temp_path"]).to_not be_nil + end + end + + context "should fail to post too large artifact" do + before do + build.run! + end + + it "using token as parameter" do + settings = Gitlab::CurrentSettings::current_application_settings + settings.update_attributes(max_artifacts_size: 0) + post authorize_url, { token: build.project.token, filesize: 100 }, headers + expect(response.status).to eq(413) + end + + it "using token as header" do + settings = Gitlab::CurrentSettings::current_application_settings + settings.update_attributes(max_artifacts_size: 0) + post authorize_url, { filesize: 100 }, headers_with_token + expect(response.status).to eq(413) + end + end + + context "should get denied" do + it do + post authorize_url, { token: 'invalid', filesize: 100 } + expect(response.status).to eq(403) + end + end + end + + describe "POST /builds/:id/artifacts" do + context "Disable sanitizer" do + before do + # by configuring this path we allow to pass temp file from any path + allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/') + end + + context "should post artifact to running build" do + before do + build.run! + end + + it do + upload_artifacts(file_upload, headers_with_token) + expect(response.status).to eq(201) + expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) + end + + it "updates artifact" do + upload_artifacts(file_upload, headers_with_token) + upload_artifacts(file_upload2, headers_with_token) + expect(response.status).to eq(201) + expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename) + end + end + + context "should fail to post too large artifact" do + before do + build.run! + end + + it do + settings = Gitlab::CurrentSettings::current_application_settings + settings.update_attributes(max_artifacts_size: 0) + upload_artifacts(file_upload, headers_with_token) + expect(response.status).to eq(413) + end + end + + context "should fail to post artifacts without file" do + before do + build.run! + end + + it do + post post_url, {}, headers_with_token + expect(response.status).to eq(400) + end + end + + context "should fail to post artifacts without GitLab-Workhorse" do + before do + build.run! + end + + it do + post post_url, { token: build.project.token }, {} + expect(response.status).to eq(403) + end + end + end + + context "should fail to post artifacts for outside of tmp path" do + before do + # by configuring this path we allow to pass file from @tmpdir only + # but all temporary files are stored in system tmp directory + @tmpdir = Dir.mktmpdir + allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir) + build.run! + end + + after do + FileUtils.remove_entry @tmpdir + end + + it do + upload_artifacts(file_upload, headers_with_token) + expect(response.status).to eq(400) + end + end + + def upload_artifacts(file, headers = {}) + params = { + file: file.path, + filename: file.original_filename, + } + post post_url, params, headers + end + end + + describe "DELETE /builds/:id/artifacts" do + before do + build.run! + post delete_url, token: build.project.token, file: file_upload + end + + it "should delete artifact build" do + build.success + delete delete_url, token: build.project.token + expect(response.status).to eq(200) + end + end + + describe "GET /builds/:id/artifacts" do + before do + build.run! + end + + it "should download artifact" do + build.update_attributes(artifacts_file: file_upload) + get get_url, token: build.project.token + expect(response.status).to eq(200) + end + + it "should fail to download if no artifact uploaded" do + get get_url, token: build.project.token + expect(response.status).to eq(404) + end + end + end end end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 386ac9c8372..06559c3925d 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do end def reenable_backup_sub_tasks - %w{db repo uploads builds}.each do |subtask| + %w{db repo uploads builds artifacts}.each do |subtask| Rake::Task["gitlab:backup:#{subtask}:create"].reenable end end @@ -56,6 +56,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke) + expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke) expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end @@ -113,19 +114,20 @@ describe 'gitlab:app namespace rake task' do it 'should set correct permissions on the tar contents' do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} ) expect(exit_status).to eq(0) expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('repositories/') expect(tar_contents).to match('builds.tar.gz') - expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz)\/$/) + expect(tar_contents).to match('artifacts.tar.gz') + expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/) end it 'should delete temp directories' do temp_dirs = Dir.glob( - File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds}') + File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}') ) expect(temp_dirs).to be_empty @@ -161,12 +163,13 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do tar_contents, _exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} ) expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('builds.tar.gz') + expect(tar_contents).to match('artifacts.tar.gz') expect(tar_contents).not_to match('repositories/') end @@ -178,6 +181,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke + expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end -- cgit v1.2.1 From 97f58bae87dfcfb36d5a7a490b1c0983435a19f4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 9 Nov 2015 12:18:00 +0100 Subject: Change artifacts syntax to allow uploading untracked files --- doc/ci/yaml/README.md | 21 ++++++++++++++++++--- lib/ci/gitlab_ci_yaml_processor.rb | 16 +++++++++++++--- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 23 ++++++++++++++++++----- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index d8504aca86a..11065fee012 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -262,13 +262,28 @@ The above script will: ### artifacts `artifacts` is used to specify list of files and directories which should be attached to build after success. +1. Send all files in `binaries` and `.config`: ``` artifacts: -- binaries/ -- .config + paths: + - binaries/ + - .config +``` + +2. Send all git untracked files: +``` +artifacts: + untracked: true +``` + +3. Send all git untracked files and files in `binaries`: +``` +artifacts: + untracked: true + paths: + - binaries/ ``` -The above definition will archive all files in `binaries/` and `.config`. The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download. This feature requires GitLab Runner v 0.7.0. diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 6f9af5388ca..2e2209031ee 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -160,11 +160,17 @@ module Ci raise ValidationError, "#{name} job: except parameter should be an array of strings" end - if job[:artifacts] && !validate_array_of_strings(job[:artifacts]) - raise ValidationError, "#{name}: artifacts parameter should be an array of strings" + if job[:artifacts] + if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) + raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" + end + + if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths]) + raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings" + end end - if job[:allow_failure] && !job[:allow_failure].in?([true, false]) + if job[:allow_failure] && !validate_boolean(job[:allow_failure]) raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" end @@ -187,6 +193,10 @@ module Ci value.is_a?(String) || value.is_a?(Symbol) end + def validate_boolean(value) + value.in?([true, false]) + end + def process?(only_params, except_params, ref, tag) if only_params.present? return false unless matching?(only_params, ref, tag) diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 5e3779af19e..29fc2713821 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -339,7 +339,10 @@ module Ci image: "ruby:2.1", services: ["mysql"], before_script: ["pwd"], - rspec: { artifacts: ["logs/", "binaries/"], script: "rspec" } + rspec: { + artifacts: { paths: ["logs/", "binaries/"], untracked: true }, + script: "rspec" + } }) config_processor = GitlabCiYamlProcessor.new(config) @@ -356,7 +359,10 @@ module Ci options: { image: "ruby:2.1", services: ["mysql"], - artifacts: ["logs/", "binaries/"] + artifacts: { + paths: ["logs/", "binaries/"], + untracked: true + } }, when: "on_success", allow_failure: false @@ -523,11 +529,18 @@ module Ci end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always") end - it "returns errors if job artifacts is not an array of strings" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: "string" } }) + it "returns errors if job artifacts:untracked is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } }) expect do GitlabCiYamlProcessor.new(config) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts parameter should be an array of strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:untracked parameter should be an boolean") + end + + it "returns errors if job artifacts:paths is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings") end end end -- cgit v1.2.1 From 445cdb7579792d0d76c2562b971583bd7d05429b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 9 Nov 2015 12:49:47 +0100 Subject: Move tmp artifacts to shared/artifacts/tmp/. Check for GitLab-Workhorse now --- .gitignore | 2 -- app/uploaders/artifact_uploader.rb | 4 ++-- doc/install/installation.md | 2 -- lib/api/helpers.rb | 2 +- shared/artifacts/tmp/cache/.gitkeep | 0 shared/artifacts/tmp/uploads/.gitkeep | 0 shared/tmp/artifacts-cache/.gitkeep | 0 shared/tmp/artifacts-uploads/.gitkeep | 0 spec/requests/ci/api/builds_spec.rb | 2 +- 9 files changed, 4 insertions(+), 8 deletions(-) create mode 100644 shared/artifacts/tmp/cache/.gitkeep create mode 100644 shared/artifacts/tmp/uploads/.gitkeep delete mode 100644 shared/tmp/artifacts-cache/.gitkeep delete mode 100644 shared/tmp/artifacts-uploads/.gitkeep diff --git a/.gitignore b/.gitignore index fd137a50473..39ff95c50ee 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,6 @@ public/assets/ public/uploads.* public/uploads/ shared/artifacts/ -shared/tmp/artifacts-uploads/ -shared/tmp/artifacts-cache/ rails_best_practices_output.html /tags tmp/ diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb index 848e0bcbde1..b4e0fc5772d 100644 --- a/app/uploaders/artifact_uploader.rb +++ b/app/uploaders/artifact_uploader.rb @@ -9,11 +9,11 @@ class ArtifactUploader < CarrierWave::Uploader::Base end def self.artifacts_upload_path - File.expand_path('shared/tmp/artifacts-uploads/', Rails.root) + File.expand_path('shared/artifacts/tmp/uploads/', Rails.root) end def self.artifacts_cache_path - File.expand_path('shared/tmp/artifacts-cache/', Rails.root) + File.expand_path('shared/artifacts/tmp/cache/', Rails.root) end def initialize(build, field) diff --git a/doc/install/installation.md b/doc/install/installation.md index 0ece8bd9315..8028e51dbcd 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -248,8 +248,6 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Change the permissions of the directory where CI artifacts are stored sudo chmod -R u+rwX shared/artifacts/ - sudo chmod -R u+rwX shared/tmp/artifacts-uploads/ - sudo chmod -R u+rwX shared/tmp/artifacts-cache/ # Copy the example Unicorn config sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index b980cd8391e..077537959d7 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -134,7 +134,7 @@ module API end def require_gitlab_workhorse! - unless headers['Gitlab-Git-Http-Server'].present? || headers['GitLab-Git-HTTP-Server'].present? + unless env['HTTP_GITLAB_WORKHORSE'].present? forbidden!('Request should be executed via GitLab Workhorse') end end diff --git a/shared/artifacts/tmp/cache/.gitkeep b/shared/artifacts/tmp/cache/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/shared/artifacts/tmp/uploads/.gitkeep b/shared/artifacts/tmp/uploads/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/shared/tmp/artifacts-cache/.gitkeep b/shared/tmp/artifacts-cache/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/shared/tmp/artifacts-uploads/.gitkeep b/shared/tmp/artifacts-uploads/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 92ea25a3723..0076730ef2f 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -132,7 +132,7 @@ describe Ci::API::API do let(:post_url) { ci_api("/builds/#{build.id}/artifacts") } let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") } let(:get_url) { ci_api("/builds/#{build.id}/artifacts") } - let(:headers) { { "Gitlab-Git-Http-Server" => "1.0" } } + let(:headers) { { "GitLab-Workhorse" => "1.0" } } let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) } describe "POST /builds/:id/artifacts/authorize" do -- cgit v1.2.1 From 7e4e3fb3b6177d2863580786e9ef6ee3a4ccf037 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 9 Nov 2015 12:55:32 +0100 Subject: Fix nginx config to use @gitlab-workhorse --- lib/support/nginx/gitlab | 8 ++++---- lib/support/nginx/gitlab-ssl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index e511d5e4b4b..0a7a4118077 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -134,16 +134,16 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 47b1ec8cb0c..b463d5b6aa9 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -181,16 +181,16 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-git-http-server block - error_page 418 = @gitlab-git-http-server; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; return 418; } -- cgit v1.2.1 From db3213fc1c653b20783f9a41074eaf17132010de Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 9 Nov 2015 22:01:26 +0100 Subject: Use normal file upload mechanism to upload artifacts --- lib/api/helpers.rb | 16 ++++++++++------ lib/ci/api/builds.rb | 16 +++++++--------- spec/requests/ci/api/builds_spec.rb | 25 +++++++++++++++++-------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 077537959d7..92540ccf2b1 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -294,19 +294,23 @@ module API # file helpers - def uploaded_file!(uploads_path) - required_attributes! [:file] + def uploaded_file!(field, uploads_path) + if params[field] + bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename) + return params[field] + end # sanitize file paths - # this requires for all paths to exist + # this requires all paths to exist + required_attributes! %W(#{field}.path) uploads_path = File.realpath(uploads_path) - file_path = File.realpath(params[:file]) + file_path = File.realpath(params["#{field}.path"]) bad_request!('Bad file path') unless file_path.start_with?(uploads_path) UploadedFile.new( file_path, - params[:filename], - params[:filetype] || 'application/octet-stream', + params["#{field}.name"], + params["#{field}.type"] || 'application/octet-stream', ) end diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 622849c4b11..dab0df12635 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -53,7 +53,7 @@ module Ci # Parameters: # id (required) - The ID of a build # token (required) - The build authorization token - # size (optional) - the size of uploaded file + # filesize (optional) - the size of uploaded file # Example Request: # POST /builds/:id/artifacts/authorize post ":id/artifacts/authorize" do @@ -77,18 +77,16 @@ module Ci # Parameters: # id (required) - The ID of a build # token (required) - The build authorization token + # file (required) - The uploaded file + # Parameters (accelerated by GitLab Workhorse): + # file.path - path to locally stored body (generated by Workhorse) + # file.name - real filename as send in Content-Disposition + # file.type - real content type as send in Content-Type # Headers: - # Content-Type - File content type - # Content-Disposition - File media type and real name # BUILD-TOKEN (required) - The build authorization token, the same as token # Body: # The file content # - # Parameters (set by GitLab Workhorse): - # file - path to locally stored body (generated by Workhorse) - # filename - real filename as send in Content-Disposition - # filetype - real content type as send in Content-Type - # filesize - real file size as send in Content-Length # Example Request: # POST /builds/:id/artifacts post ":id/artifacts" do @@ -98,7 +96,7 @@ module Ci authenticate_build_token!(build) forbidden!('build is not running') unless build.running? - file = uploaded_file!(ArtifactUploader.artifacts_upload_path) + file = uploaded_file!(:file, ArtifactUploader.artifacts_upload_path) file_to_large! unless file.size < max_artifacts_size if build.update_attributes(artifacts_file: file) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 0076730ef2f..233c15f87fe 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -194,8 +194,14 @@ describe Ci::API::API do build.run! end - it do - upload_artifacts(file_upload, headers_with_token) + it "uses regual file post" do + upload_artifacts(file_upload, headers_with_token, false) + expect(response.status).to eq(201) + expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) + end + + it "uses accelerated file post" do + upload_artifacts(file_upload, headers_with_token, true) expect(response.status).to eq(201) expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename) end @@ -263,12 +269,15 @@ describe Ci::API::API do end end - def upload_artifacts(file, headers = {}) - params = { - file: file.path, - filename: file.original_filename, - } - post post_url, params, headers + def upload_artifacts(file, headers = {}, accelerated = true) + if accelerated + post post_url, { + 'file.path' => file.path, + 'file.name' => file.original_filename + }, headers + else + post post_url, { file: file }, headers + end end end -- cgit v1.2.1 From 99c05363ea8ff53cfa9620b626325773d17bb575 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 07:19:31 -0800 Subject: Remove CSS property preventing hard tabs from rendering in Chromium 45 This is to workaround a bug in Chromium 45 (https://code.google.com/p/chromium/issues/detail?id=446434), which is the default browser in Ubuntu 14.04 and older. Closes #3220 --- CHANGELOG | 1 + app/assets/stylesheets/framework/typography.scss | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 668ea87b28f..c6bacab6a5e 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.2.0 (unreleased) + - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index e6558a23858..ba0312ba0db 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -173,7 +173,6 @@ * */ body { - text-rendering:optimizeLegibility; -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px; } -- cgit v1.2.1 From 802b8fceb444f02fb8c2f67c5c433832baa33be7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 16:27:50 +0100 Subject: Fix graph description and text Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/graphs/ci/_builds.haml | 4 ++-- features/steps/project/graph.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml index 84247455403..8fca07114fa 100644 --- a/app/views/projects/graphs/ci/_builds.haml +++ b/app/views/projects/graphs/ci/_builds.haml @@ -17,13 +17,13 @@ .prepend-top-default %p.light - Builds chart for last month + Builds for last month (#{date_from_to(Date.today - 30.days, Date.today)}) %canvas#monthChart{height: 200} .prepend-top-default %p.light - Builds chart for last year + Builds for last year %canvas#yearChart.padded{height: 250} - [:week, :month, :year].each do |scope| diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb index 4abd5288d51..98f31f3b76a 100644 --- a/features/steps/project/graph.rb +++ b/features/steps/project/graph.rb @@ -25,9 +25,9 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps step 'page should have CI graphs' do expect(page).to have_content 'Overall' - expect(page).to have_content 'Builds chart for last week' - expect(page).to have_content 'Builds chart for last month' - expect(page).to have_content 'Builds chart for last year' + expect(page).to have_content 'Builds for last week' + expect(page).to have_content 'Builds for last month' + expect(page).to have_content 'Builds for last year' expect(page).to have_content 'Commit duration in minutes for last 30 commits' end -- cgit v1.2.1 From 78966d4ca635506ee5323a88423275bc44d84a49 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 07:36:10 -0800 Subject: Bump rugged version to be in line with gitlab_git requirements --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1688ea4d9d8..aae1ec7610c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -611,7 +611,7 @@ GEM sexp_processor (~> 4.1) rubyntlm (0.5.2) rubypants (0.2.0) - rugged (0.22.2) + rugged (0.23.3) safe_yaml (1.0.4) sanitize (2.1.0) nokogiri (>= 1.4.4) -- cgit v1.2.1 From ffa8d84a6ef27dea6d87650390821d42f3d1b38a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 07:56:05 -0800 Subject: Bump charlock_holmes version --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index aae1ec7610c..dca8606806a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -107,7 +107,7 @@ GEM json (>= 1.7) celluloid (0.16.0) timers (~> 4.0.0) - charlock_holmes (0.6.9.4) + charlock_holmes (0.7.3) chunky_png (1.3.4) cliver (0.3.2) coderay (1.1.0) -- cgit v1.2.1 From b0452f26c0c3274d9d16c4b7e6f170737de4e31e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 08:16:33 -0800 Subject: Remove duplicate CHANGELOG etnries [ci skip] --- CHANGELOG | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 668ea87b28f..168c4b50e4a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,8 +3,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) - Improved performance of finding users by one of their Email addresses - Improved performance of replacing references in comments - Show last project commit to default branch on project home page -- cgit v1.2.1 From 58429d9b26b2f2b62fecab012fce0ebe36571129 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 18:34:05 +0100 Subject: Add method complexity check to CI Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 7 +++++++ Gemfile | 1 + Gemfile.lock | 4 ++++ lib/tasks/flog.rake | 24 ++++++++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 lib/tasks/flog.rake diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cf6d28b01af..476dabe5e3d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -73,3 +73,10 @@ brakeman: tags: - ruby - mysql + +flog: + script: + - bundle exec rake flog + tags: + - ruby + - mysql diff --git a/Gemfile b/Gemfile index c73aa26bd0a..91a93215336 100644 --- a/Gemfile +++ b/Gemfile @@ -259,6 +259,7 @@ group :development, :test do gem 'rubocop', '~> 0.28.0', require: false gem 'coveralls', '~> 0.8.2', require: false gem 'simplecov', '~> 0.10.0', require: false + gem 'flog', require: false gem 'benchmark-ips', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index dca8606806a..0fc22829cfd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -194,6 +194,9 @@ GEM ffi (1.9.10) fission (0.5.0) CFPropertyList (~> 2.2) + flog (4.3.2) + ruby_parser (~> 3.1, > 3.1.0) + sexp_processor (~> 4.4) flowdock (0.7.0) httparty (~> 0.7) multi_json @@ -821,6 +824,7 @@ DEPENDENCIES enumerize (~> 0.7.0) factory_girl_rails (~> 4.3.0) ffaker (~> 2.0.0) + flog fog (~> 1.25.0) font-awesome-rails (~> 4.2) foreman diff --git a/lib/tasks/flog.rake b/lib/tasks/flog.rake new file mode 100644 index 00000000000..4cb19c0c937 --- /dev/null +++ b/lib/tasks/flog.rake @@ -0,0 +1,24 @@ +desc 'Code complexity analyze via flog' +task :flog do + output = %x(bundle exec flog -m app/ lib/gitlab) + exit_code = 0 + output = output.lines + + # Skip total complexity score + output.shift + + # Skip some trash info + output.shift + + output.each do |line| + score, method = line.split(" ") + score = score.to_i + + if score > 40 + exit_code = 1 + puts "High complexity in #{method}. Score: #{score}" + end + end + + exit exit_code +end -- cgit v1.2.1 From afcced01e579cb75d876c4612fc09e87d1502da6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 10 Nov 2015 13:15:18 -0500 Subject: Remove text-rendering property Closes #3220 [ci skip] --- app/assets/stylesheets/framework/layout.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index c7b3b60e769..b91c15d8910 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -5,7 +5,6 @@ html { body { padding-top: $header-height; - text-rendering: geometricPrecision; } } -- cgit v1.2.1 From 97380977221865308d5336da0ea0d49e5c45d03a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 07:52:35 -0800 Subject: Fix avatars not showing in Atom feeds and project issues when Gravatar disabled Fix for https://github.com/gitlabhq/gitlabhq/pull/9783 --- CHANGELOG | 1 + app/helpers/events_helper.rb | 2 +- app/helpers/issues_helper.rb | 2 +- app/views/projects/commits/show.atom.builder | 2 +- app/views/projects/notes/_note.html.haml | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d82cab6f36c..9d2af96edec 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.2.0 (unreleased) - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) + - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Improved performance of finding users by one of their Email addresses diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 6f69c2a9f32..51b872b7a95 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -198,7 +198,7 @@ module EventsHelper xml.link href: event_link xml.title truncate(event_title, length: 80) xml.updated event.created_at.xmlschema - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email)) xml.author do |author| xml.name event.author_name xml.email event.author_email diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index fda18e7b316..beb083d82dc 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -74,7 +74,7 @@ module IssuesHelper issue.project, issue) xml.title truncate(issue.title, length: 80) xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email)) xml.author do |author| xml.name issue.author_name xml.email issue.author_email diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 3854ad5d611..268b9b815ee 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_commit_url(@project.namespace, @project, id: commit.id) xml.title truncate(commit.title, length: 80) xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(commit.author_email) + xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) xml.author do |author| xml.name commit.author_name xml.email commit.author_email diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 5d184730796..88808301985 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -2,7 +2,7 @@ .timeline-entry-inner .timeline-icon %a{href: user_path(note.author)} - %img.avatar.s40{src: avatar_icon(note.author), alt: ''} + = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40' .timeline-content .note-header - if note_editable?(note) -- cgit v1.2.1 From 5aa142212fc4fb7765aad801c1a25696fb752fba Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Nov 2015 00:27:01 -0800 Subject: Fix Drone CI service template not saving properly Closes #3419 --- CHANGELOG | 1 + app/models/project_services/drone_ci_service.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index d82cab6f36c..7567e25b891 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.2.0 (unreleased) - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) + - Fix Drone CI service template not saving properly (Stan Hu) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Improved performance of finding users by one of their Email addresses diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index c73c4b058a1..c240213200d 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -32,7 +32,6 @@ class DroneCiService < CiService def compose_service_hook hook = service_hook || build_service_hook - hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join hook.enable_ssl_verification = enable_ssl_verification hook.save end -- cgit v1.2.1 From 88836e2ab6664f0cf7b3dc2fa0d4f3d798aeaaa6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 22:31:16 +0200 Subject: Fix bottom position of scroll buttons in build log page --- app/assets/stylesheets/pages/builds.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 74dc3e321c1..da9965f007a 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -21,7 +21,7 @@ .autoscroll-container { position: fixed; - bottom: 10px; + bottom: 20px; right: 20px; z-index: 100; } @@ -34,7 +34,7 @@ a { display: block; - margin-bottom: 5px; + margin-bottom: 10px; } } -- cgit v1.2.1 From ca25289b78e1b49148438831ad6bf165fa0ce56e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 10 Nov 2015 15:44:23 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2b96a0a171b..9cfcd23a9fc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,7 +34,9 @@ v 8.2.0 (unreleased) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Prevent redirect loop when home_page_url is set to the root URL - - Ability to add release notes (markdown text and attachments) to git tags + - Fix incoming email config defaults + - Make color of "Accept Merge Request" button consistent with current build status + - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) v 8.1.3 - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu) @@ -42,7 +44,7 @@ v 8.1.3 - Use issue editor as cross reference comment author when issue is edited with a new mention - Add Facebook authentication -v 8.1.1 +v 8.1.2 - Fix cloning Wiki repositories via HTTP (Stan Hu) - Add migration to remove satellites directory - Fix specific runners visibility @@ -52,6 +54,9 @@ v 8.1.1 - Fix CI badge - Allow developer to manage builds +v 8.1.1 + - Removed, see 8.1.2 + v 8.1.0 - Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu) - Fix duplicate repositories in GitHub import page (Stan Hu) -- cgit v1.2.1 From eef129bd4ad41665cf5c1f1631e80b2f1bb3e4af Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 10 Nov 2015 22:21:15 +0100 Subject: Final fixes --- config/initializers/1_settings.rb | 2 +- doc/ci/yaml/README.md | 2 +- lib/ci/api/builds.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b39e263e39a..302124bd977 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -186,7 +186,7 @@ Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_br Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) -Settings.gitlab_ci['max_artifacts_size'] ||= 100 +Settings.gitlab_ci['max_artifacts_size'] ||= 100 # in megabytes # # Reply by email diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 11065fee012..5d35d1da4ee 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -286,7 +286,7 @@ artifacts: The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download. -This feature requires GitLab Runner v 0.7.0. +This feature requires GitLab Runner v0.7.0 or higher. ## Validate the .gitlab-ci.yml Each instance of GitLab CI has an embedded debug tool called Lint. diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index dab0df12635..0a586672807 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -69,7 +69,7 @@ module Ci end status 200 - { temp_path: ArtifactUploader.artifacts_upload_path } + { TempPath: ArtifactUploader.artifacts_upload_path } end # Upload artifacts to build - Runners only -- cgit v1.2.1 From 752d528019fc9f9c58d458380a6594d358458b4d Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 14 Sep 2015 12:07:50 -0500 Subject: Fix trailing space issue with merge requests and issues. Fixes #2514 --- CHANGELOG | 1 + app/controllers/projects/issues_controller.rb | 4 +++- app/controllers/projects/merge_requests_controller.rb | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9cfcd23a9fc..939213a526b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -30,6 +30,7 @@ v 8.2.0 (unreleased) - Fix incoming email config defaults - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page + - Fix trailing whitespace issue in merge request/issue title v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index e767efbdc0c..e74c2905e48 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -158,10 +158,12 @@ class Projects::IssuesController < Projects::ApplicationController end def issue_params - params.require(:issue).permit( + permitted = params.require(:issue).permit( :title, :assignee_id, :position, :description, :milestone_id, :state_event, :task_num, label_ids: [] ) + params[:issue][:title].strip! if params[:issue][:title] + permitted end def bulk_update_params diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index b0788a2d073..188f0cc4cea 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -276,11 +276,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_request_params - params.require(:merge_request).permit( + permitted = params.require(:merge_request).permit( :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description, :task_num, label_ids: [] ) + params[:merge_request][:title].strip! if params[:merge_request][:title] + permitted end # Make sure merge requests created before 8.0 -- cgit v1.2.1 From 73f8763e6a249c4c4be731673556e7e2d625ee42 Mon Sep 17 00:00:00 2001 From: Andy Brandt Date: Tue, 10 Nov 2015 15:42:17 -0600 Subject: fix build link --- app/views/projects/builds/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 3374d5432a5..37f33edd478 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -168,7 +168,7 @@ %td = ci_icon_for_status(build.status) %td - = link_to namespace_project_build_path(@project.namespace, @project, @build) do + = link_to namespace_project_build_path(@project.namespace, @project, build) do - if build.name = build.name - else -- cgit v1.2.1 From d70f1f35b13411adf184fafb750ae8d8fb6badea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 10 Nov 2015 23:00:05 +0100 Subject: Fix tests --- spec/requests/ci/api/builds_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 233c15f87fe..7886a6feca2 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -144,13 +144,13 @@ describe Ci::API::API do it "using token as parameter" do post authorize_url, { token: build.project.token }, headers expect(response.status).to eq(200) - expect(json_response["temp_path"]).to_not be_nil + expect(json_response["TempPath"]).to_not be_nil end it "using token as header" do post authorize_url, {}, headers_with_token expect(response.status).to eq(200) - expect(json_response["temp_path"]).to_not be_nil + expect(json_response["TempPath"]).to_not be_nil end end -- cgit v1.2.1 From 58074ab7da9c24c030054e6303f9020f1d1f6f83 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Tue, 10 Nov 2015 22:48:13 +0100 Subject: Allow to define cache in `.gitlab-ci.yml` --- CHANGELOG | 1 + doc/ci/yaml/README.md | 66 ++++++++++++++++++++++ lib/ci/gitlab_ci_yaml_processor.rb | 30 ++++++++-- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 82 ++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e54e7329ca1..1454f70858c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.2.0 (unreleased) - Show merge request CI status on merge requests index page - Extend yml syntax for only and except to support specifying repository path - Enable shared runners to all new projects + - Allow to define cache in `.gitlab-ci.yml` - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - [API] Add ability to fetch the commit ID of the last commit that actually touched a file diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 5d35d1da4ee..3e6071a2ee7 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -56,6 +56,7 @@ There are a few `keywords` that can't be used as job names: | types | optional | Alias for `stages` | | before_script | optional | Define commands prepended for each job's script | | variables | optional | Define build variables | +| cache | optional | Define list of files that should be cached between subsequent runs | ### image and services This allows to specify a custom Docker image and a list of services that can be used for time of the build. @@ -110,6 +111,19 @@ These variables can be later used in all executed commands and scripts. The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them. +### cache +`cache` is used to specify list of files and directories which should be cached between builds. + +**The global setting allows to specify default cached files for all jobs.** + +To cache all git untracked files and files in `binaries`: +``` +cache: + untracked: true + paths: + - binaries/ +``` + ## Jobs `.gitlab-ci.yml` allows you to specify an unlimited number of jobs. Each job has to have a unique `job_name`, which is not one of the keywords mentioned above. @@ -142,6 +156,7 @@ job_name: | allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status | | when | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` | | artifacts | optional | Define list build artifacts | +| cache | optional | Define list of files that should be cached between subsequent runs | ### script `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`. @@ -288,6 +303,57 @@ The artifacts will be send after the build success to GitLab and will be accessi This feature requires GitLab Runner v0.7.0 or higher. +### cache +`cache` is used to specify list of files and directories which should be cached between builds. + +1. Cache all files in `binaries` and `.config`: +``` +rspec: + script: test + cache: + paths: + - binaries/ + - .config +``` + +2. Cache all git untracked files: +``` +rspec: + script: test + cache: + untracked: true +``` + +3. Cache all git untracked files and files in `binaries`: +``` +rspec: + script: test + cache: + untracked: true + paths: + - binaries/ +``` + +4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`: + +``` +cache: + paths: + - my/files + +rspec: + script: test + cache: + paths: + - binaries/ +``` + +The cache is provided on best effort basis, so don't expect that cache will be present. +For implementation details please check GitLab Runner. + +This feature requires GitLab Runner v0.7.0 or higher. + + ## Validate the .gitlab-ci.yml Each instance of GitLab CI has an embedded debug tool called Lint. You can find the link to the Lint in the project's settings page or use short url `/lint`. diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 2e2209031ee..3beafcad117 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -4,10 +4,10 @@ module Ci DEFAULT_STAGES = %w(build test deploy) DEFAULT_STAGE = 'test' - ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables] - ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts] + ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache] + ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache] - attr_reader :before_script, :image, :services, :variables, :path + attr_reader :before_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) @config = YAML.load(config) @@ -46,6 +46,7 @@ module Ci @services = @config[:services] @stages = @config[:stages] || @config[:types] @variables = @config[:variables] || {} + @cache = @config[:cache] @config.except!(*ALLOWED_YAML_KEYS) # anything that doesn't have script is considered as unknown @@ -78,7 +79,8 @@ module Ci options: { image: job[:image] || @image, services: job[:services] || @services, - artifacts: job[:artifacts] + artifacts: job[:artifacts], + cache: job[:cache] || @cache, }.compact } end @@ -112,6 +114,16 @@ module Ci raise ValidationError, "variables should be a map of key-valued strings" end + if @cache + if @cache[:untracked] && !validate_boolean(@cache[:untracked]) + raise ValidationError, "cache:untracked parameter should be an boolean" + end + + if @cache[:paths] && !validate_array_of_strings(@cache[:paths]) + raise ValidationError, "cache:paths parameter should be an array of strings" + end + end + @jobs.each do |name, job| validate_job!(name, job) end @@ -160,6 +172,16 @@ module Ci raise ValidationError, "#{name} job: except parameter should be an array of strings" end + if job[:cache] + if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked]) + raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean" + end + + if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths]) + raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings" + end + end + if job[:artifacts] if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 29fc2713821..7d90f9877c6 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -333,6 +333,60 @@ module Ci end end + describe "Caches" do + it "returns cache when defined globally" do + config = YAML.dump({ + cache: { paths: ["logs/", "binaries/"], untracked: true }, + rspec: { + script: "rspec" + } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + paths: ["logs/", "binaries/"], + untracked: true, + ) + end + + it "returns cache when defined in a job" do + config = YAML.dump({ + rspec: { + cache: { paths: ["logs/", "binaries/"], untracked: true }, + script: "rspec" + } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + paths: ["logs/", "binaries/"], + untracked: true, + ) + end + + it "overwrite cache when defined for a job and globally" do + config = YAML.dump({ + cache: { paths: ["logs/", "binaries/"], untracked: true }, + rspec: { + script: "rspec", + cache: { paths: ["test/"], untracked: false }, + } + }) + + config_processor = GitlabCiYamlProcessor.new(config) + + expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1) + expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq( + paths: ["test/"], + untracked: false, + ) + end + end + describe "Artifacts" do it "returns artifacts when defined" do config = YAML.dump({ @@ -542,6 +596,34 @@ module Ci GitlabCiYamlProcessor.new(config) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings") end + + it "returns errors if cache:untracked is not an array of strings" do + config = YAML.dump({ cache: { untracked: "string" }, rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked parameter should be an boolean") + end + + it "returns errors if cache:paths is not an array of strings" do + config = YAML.dump({ cache: { paths: "string" }, rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings") + end + + it "returns errors if job cache:untracked is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:untracked parameter should be an boolean") + end + + it "returns errors if job cache:paths is not an array of strings" do + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } }) + expect do + GitlabCiYamlProcessor.new(config) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:paths parameter should be an array of strings") + end end end end -- cgit v1.2.1 From a3f64b53ecb0b40c94eb0fe32b662239fff6961f Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 11 Nov 2015 13:09:23 +0100 Subject: Set changelog item to right release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 36612126c67..69a293d70d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,7 @@ v 8.2.0 (unreleased) - Fix incoming email config defaults - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page + - Accept COPYING as licence file (Zeger-Jan van de Weg) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) @@ -155,7 +156,6 @@ v 8.0.4 - Fix "Assign All" button on Runner admin page - Fix search in Files - Add full project namespace to payload of system webhooks (Ricardo Band) - - Accept COPYING as licence file (Zeger-Jan van de Weg) v 8.0.3 - Fix URL shown in Slack notifications -- cgit v1.2.1 From e189666a6d233a76a8f2e29919048e59b137823c Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 11 Nov 2015 13:31:42 +0100 Subject: Add CHANGELOG line to the right release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index cc68f489d41..3439bf5d757 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,7 @@ v 8.2.0 (unreleased) - Fix incoming email config defaults - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page + - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) @@ -155,7 +156,6 @@ v 8.0.4 - Fix "Assign All" button on Runner admin page - Fix search in Files - Add full project namespace to payload of system webhooks (Ricardo Band) - - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) v 8.0.3 - Fix URL shown in Slack notifications -- cgit v1.2.1 From 26fab9cb95bda08a044f5500ef0b74854b293a54 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 11 Nov 2015 13:40:47 +0000 Subject: Fix typo in email comment: hone -> one --- app/views/layouts/notify.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index a02dd955186..3ca4c340406 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -42,8 +42,8 @@ - else #{link_to "View it on GitLab", @target_url}. %br - -# Don't link the host is the line below, hone link in the email is easier to quickly click than two. + -# Don't link the host is the line below, one link in the email is easier to quickly click than two. You're receiving this email because of your account on #{Gitlab.config.gitlab.host}. If you'd like to receive fewer emails, you can adjust your notification settings. - = email_action @target_url + = email_action @target_url \ No newline at end of file -- cgit v1.2.1 From 7eb502c036f412e9e802600cd67c6520a1f3bff1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 15:17:12 +0100 Subject: Change "recent" scopes to sort by "id" These scopes can just sort by the "id" column in descending order to achieve the same result. An added benefit is being able to perform a backwards index scan (depending on the rest of the final query) instead of having to actually sort data. --- app/models/concerns/issuable.rb | 2 +- app/models/event.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5e964f04ef5..492a026add9 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -24,7 +24,7 @@ module Issuable scope :authored, ->(user) { where(author_id: user) } scope :assigned_to, ->(u) { where(assignee_id: u.id)} - scope :recent, -> { order("created_at DESC") } + scope :recent, -> { reorder(id: :desc) } scope :assigned, -> { where("assignee_id IS NOT NULL") } scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } diff --git a/app/models/event.rb b/app/models/event.rb index 47600c57e35..bf64ac29d32 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -45,7 +45,7 @@ class Event < ActiveRecord::Base after_create :reset_project_activity # Scopes - scope :recent, -> { order(created_at: :desc) } + scope :recent, -> { reorder(id: :desc) } scope :code_push, -> { where(action: PUSHED) } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } scope :with_associations, -> { includes(project: :namespace) } -- cgit v1.2.1 From 6748dd2fd0b2cf694661e910c6c1bb6ac2be69a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 11 Nov 2015 15:30:23 +0100 Subject: Allow flog failure for now Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 + lib/tasks/flog.rake | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 476dabe5e3d..062fda322a6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -80,3 +80,4 @@ flog: tags: - ruby - mysql + allow_failure: true diff --git a/lib/tasks/flog.rake b/lib/tasks/flog.rake index 4cb19c0c937..3bfe999ae74 100644 --- a/lib/tasks/flog.rake +++ b/lib/tasks/flog.rake @@ -2,6 +2,7 @@ desc 'Code complexity analyze via flog' task :flog do output = %x(bundle exec flog -m app/ lib/gitlab) exit_code = 0 + minimum_score = 70 output = output.lines # Skip total complexity score @@ -14,7 +15,7 @@ task :flog do score, method = line.split(" ") score = score.to_i - if score > 40 + if score > minimum_score exit_code = 1 puts "High complexity in #{method}. Score: #{score}" end -- cgit v1.2.1 From 98c954ee815823f27cf021b34684c2c3d5bb0917 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 11 Nov 2015 16:29:00 +0100 Subject: Add flay: tool to find duplicate code Signed-off-by: Dmitriy Zaporozhets --- .flayignore | 1 + .gitlab-ci.yml | 8 ++++++++ Gemfile | 1 + Gemfile.lock | 4 ++++ lib/tasks/flay.rake | 9 +++++++++ 5 files changed, 23 insertions(+) create mode 100644 .flayignore create mode 100644 lib/tasks/flay.rake diff --git a/.flayignore b/.flayignore new file mode 100644 index 00000000000..9c9875d4f9e --- /dev/null +++ b/.flayignore @@ -0,0 +1 @@ +*.erb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 062fda322a6..128faa07db8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -81,3 +81,11 @@ flog: - ruby - mysql allow_failure: true + +flay: + script: + - bundle exec rake flay + tags: + - ruby + - mysql + allow_failure: true diff --git a/Gemfile b/Gemfile index 91a93215336..67aef0c3a8d 100644 --- a/Gemfile +++ b/Gemfile @@ -260,6 +260,7 @@ group :development, :test do gem 'coveralls', '~> 0.8.2', require: false gem 'simplecov', '~> 0.10.0', require: false gem 'flog', require: false + gem 'flay', require: false gem 'benchmark-ips', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 0fc22829cfd..5f95637966e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -194,6 +194,9 @@ GEM ffi (1.9.10) fission (0.5.0) CFPropertyList (~> 2.2) + flay (2.6.1) + ruby_parser (~> 3.0) + sexp_processor (~> 4.0) flog (4.3.2) ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.4) @@ -824,6 +827,7 @@ DEPENDENCIES enumerize (~> 0.7.0) factory_girl_rails (~> 4.3.0) ffaker (~> 2.0.0) + flay flog fog (~> 1.25.0) font-awesome-rails (~> 4.2) diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake new file mode 100644 index 00000000000..5efffc2cdac --- /dev/null +++ b/lib/tasks/flay.rake @@ -0,0 +1,9 @@ +desc 'Code duplication analyze via flay' +task :flay do + output = %x(bundle exec flay app/ lib/gitlab/) + + if output.include? "Similar code found" + puts output + exit 1 + end +end -- cgit v1.2.1 From c6a0f109cd393e951f4d700b06490d819e6792ba Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 11 Nov 2015 15:42:27 +0000 Subject: refactored code as projects only have one owner. Kept some refactoring in place (has_owners concern) --- app/models/ability.rb | 52 +++++++++++++++++++-------------------- app/models/concerns/has_owners.rb | 4 +-- app/models/group.rb | 6 +---- app/models/member.rb | 13 +++++----- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 5beead0b75d..b46c4943bf5 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -6,17 +6,17 @@ class Ability return [] if user.blocked? case subject.class.name - when "Project" then project_abilities(user, subject) - when "Issue" then issue_abilities(user, subject) - when "Note" then note_abilities(user, subject) - when "ProjectSnippet" then project_snippet_abilities(user, subject) - when "PersonalSnippet" then personal_snippet_abilities(user, subject) - when "MergeRequest" then merge_request_abilities(user, subject) - when "Group" then group_abilities(user, subject) - when "Namespace" then namespace_abilities(user, subject) - when "GroupMember" then group_member_abilities(user, subject) - when "ProjectMember" then project_member_abilities(user, subject) - else [] + when "Project" then project_abilities(user, subject) + when "Issue" then issue_abilities(user, subject) + when "Note" then note_abilities(user, subject) + when "ProjectSnippet" then project_snippet_abilities(user, subject) + when "PersonalSnippet" then personal_snippet_abilities(user, subject) + when "MergeRequest" then merge_request_abilities(user, subject) + when "Group" then group_abilities(user, subject) + when "Namespace" then namespace_abilities(user, subject) + when "GroupMember" then group_member_abilities(user, subject) + when "ProjectMember" then project_member_abilities(user, subject) + else [] end.concat(global_abilities(user)) end @@ -232,17 +232,17 @@ class Ability # Only group masters and group owners can create new projects in group if group.has_master?(user) || group.has_owner?(user) || user.admin? rules.push(*[ - :create_projects, - ]) + :create_projects, + ]) end # Only group owner and administrators can admin group if group.has_owner?(user) || user.admin? rules.push(*[ - :admin_group, - :admin_namespace, - :admin_group_member - ]) + :admin_group, + :admin_namespace, + :admin_group_member + ]) end rules.flatten @@ -254,9 +254,9 @@ class Ability # Only namespace owner and administrators can admin it if namespace.owner == user || user.admin? rules.push(*[ - :create_projects, - :admin_namespace - ]) + :create_projects, + :admin_namespace + ]) end rules.flatten @@ -323,12 +323,12 @@ class Ability project = subject.project can_manage = project_abilities(user, project).include?(:admin_project_member) - if can_manage && (user != target_user) + if can_manage && user != target_user && target_user != project.owner rules << :update_project_member rules << :destroy_project_member end - if !project.last_owner?(user) && (can_manage || (user == target_user)) + if user == target_user && target_user != project.owner rules << :destroy_project_member end rules @@ -336,10 +336,10 @@ class Ability def abilities @abilities ||= begin - abilities = Six.new - abilities << self - abilities - end + abilities = Six.new + abilities << self + abilities + end end private diff --git a/app/models/concerns/has_owners.rb b/app/models/concerns/has_owners.rb index 735b2071721..07f3428b481 100644 --- a/app/models/concerns/has_owners.rb +++ b/app/models/concerns/has_owners.rb @@ -6,10 +6,10 @@ module HasOwners extend ActiveSupport::Concern def owners - @owners ||= my_members.owners.includes(:user).map(&:user) + @owners ||= members.owners.includes(:user).map(&:user) end - def my_members + def members raise NotImplementedError, "Expected my_members to be defined in #{self.class.name}" end diff --git a/app/models/group.rb b/app/models/group.rb index c9806f6fd6f..9503d8b0f1c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -22,7 +22,7 @@ class Group < Namespace include HasOwners has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' - alias_method :my_members, :group_members + alias_method :members, :group_members has_many :users, through: :group_members validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } @@ -91,10 +91,6 @@ class Group < Namespace add_user(user, Gitlab::Access::MASTER, current_user) end - def members - group_members - end - def avatar_type unless self.avatar.image? self.errors.add :avatar, "only images allowed" diff --git a/app/models/member.rb b/app/models/member.rb index c565ee6bbce..30160c813e1 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -30,13 +30,13 @@ class Member < ActiveRecord::Base validates :user, presence: true, unless: :invite? validates :source, presence: true - validates :user_id, uniqueness: { scope: [:source_type, :source_id], + validates :user_id, uniqueness: { scope: [:source_type, :source_id], message: "already exists in source", allow_nil: true } validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true - validates :invite_email, presence: { if: :invite? }, - email: { strict_mode: true, allow_nil: true }, - uniqueness: { scope: [:source_type, :source_id], allow_nil: true } + validates :invite_email, presence: { if: :invite? }, + email: { strict_mode: true, allow_nil: true }, + uniqueness: { scope: [:source_type, :source_id], allow_nil: true } scope :invite, -> { where(user_id: nil) } scope :non_invite, -> { where("user_id IS NOT NULL") } @@ -94,8 +94,7 @@ class Member < ActiveRecord::Base def can_update_member?(current_user, member) !current_user || current_user.can?(:update_group_member, member) || - (member.respond_to?(:project) && - current_user.can?(:update_project_member, member)) + current_user.can?(:update_project_member, member) end end @@ -105,7 +104,7 @@ class Member < ActiveRecord::Base def accept_invite!(new_user) return false unless invite? - + self.invite_token = nil self.invite_accepted_at = Time.now.utc -- cgit v1.2.1 From fd1b5d6479ca05420dfd9f13ac6af0f53b5b858d Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 11 Nov 2015 15:54:28 +0000 Subject: updated exception --- app/models/concerns/has_owners.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/has_owners.rb b/app/models/concerns/has_owners.rb index 07f3428b481..53ef6e939dd 100644 --- a/app/models/concerns/has_owners.rb +++ b/app/models/concerns/has_owners.rb @@ -10,7 +10,7 @@ module HasOwners end def members - raise NotImplementedError, "Expected my_members to be defined in #{self.class.name}" + raise NotImplementedError, "Expected members to be defined in #{self.class.name}" end def add_owner(user, current_user = nil) -- cgit v1.2.1 From d59a82118128cb8ecd14008f59e6cd73a1b0a71b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 11 Nov 2015 11:02:52 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3da194510bd..71300769fe6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,12 +32,12 @@ v 8.2.0 (unreleased) - Fix incoming email config defaults - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page + - Make color of "Accept Merge Request" button consistent with current build status v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) - Prevent redirect loop when home_page_url is set to the root URL - Fix incoming email config defaults - - Make color of "Accept Merge Request" button consistent with current build status - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) v 8.1.3 -- cgit v1.2.1 From e0c64fac68b4b3acc48300956146b85e03b426ce Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Wed, 11 Nov 2015 16:29:29 -0600 Subject: Refactor for style issues --- app/controllers/projects/branches_controller.rb | 3 +-- app/models/repository.rb | 24 ++++++++++-------------- app/views/projects/branches/_branch.html.haml | 11 ++++++----- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index c3cd7642dd2..87884420952 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -10,8 +10,7 @@ class Projects::BranchesController < Projects::ApplicationController @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) - @max_commits = @branches.reduce(0) do - |memo, branch| + @max_commits = @branches.reduce(0) do |memo, branch| diverging_commit_counts = repository.diverging_commit_counts(branch) [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max end diff --git a/app/models/repository.rb b/app/models/repository.rb index 9b270bc9d18..0e2d4ea1fb8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -148,8 +148,7 @@ class Repository end def diverging_commit_counts(branch) - branch_cache_key = ('diverging_commit_counts_' + branch.name).to_sym - cache.fetch(branch_cache_key) do + cache.fetch(:"diverging_commit_counts_#{branch.name}") do number_commits_behind = commits_between(branch.name, root_ref).size number_commits_ahead = commits_between(root_ref, branch.name).size @@ -158,14 +157,13 @@ class Repository end def cache_keys - %i(size branch_names tag_names commit_count readme - contribution_guide changelog license) + %i(size branch_names tag_names commit_count + readme version contribution_guide changelog license) end def branch_cache_keys - branches.map do - |branch| - ('diverging_commit_counts_' + branch.name).to_sym + branches.map do |branch| + :"diverging_commit_counts_#{branch.name}" end end @@ -177,7 +175,7 @@ class Repository end branches.each do |branch| - unless cache.exist?(('diverging_commit_counts_' + branch.name).to_sym) + unless cache.exist?(:"diverging_commit_counts_#{branch.name}") send(:diverging_commit_counts, branch) end end @@ -188,14 +186,12 @@ class Repository cache.expire(key) end - branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) - end + expire_branch_cache end def expire_branch_cache branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + cache.expire(:"diverging_commit_counts_#{branch.name}") end end @@ -206,8 +202,8 @@ class Repository end branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) - send(:diverging_commit_counts, branch) + cache.expire(:"diverging_commit_counts_#{branch.name}") + diverging_commit_counts(branch) end end diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 9ddb10a1c74..a4202d1120d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -1,7 +1,8 @@ - commit = @repository.commit(branch.target) - bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0 -- number_commits_behind = @repository.diverging_commit_counts(branch)[:behind] -- number_commits_ahead = @repository.diverging_commit_counts(branch)[:ahead] +- diverging_commit_counts = @repository.diverging_commit_counts(branch) +- number_commits_behind = diverging_commit_counts[:behind] +- number_commits_ahead = diverging_commit_counts[:ahead] %li(class="js-branch-#{branch.name}") %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do @@ -33,13 +34,13 @@ = icon("trash-o") - if branch.name != @repository.root_ref - .divergence-graph{ :title => "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } + .divergence-graph{ title: "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } .graph-side - .bar.bar-behind{ :style => "width: #{number_commits_behind * bar_graph_width_factor}%" } + .bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" } %span.count.count-behind= number_commits_behind .graph-separator .graph-side - .bar.bar-ahead{ :style => "width: #{number_commits_ahead * bar_graph_width_factor}%" } + .bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" } %span.count.count-ahead= number_commits_ahead -- cgit v1.2.1 From 1974087114f3f365d16547c8a5c3ec2e03a66104 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 12 Nov 2015 13:16:35 +0800 Subject: Avoid render edit_form in each notes. Use RJS to render edit note feature. Before: ``` Rendered projects/notes/_note.html.haml (27.9ms) Rendered projects/_zen.html.haml (0.3ms) Rendered projects/notes/_hints.html.haml (0.7ms) Rendered projects/_md_preview.html.haml (3.9ms) Rendered projects/notes/_edit_form.html.haml (6.9ms) Rendered projects/notes/_note.html.haml (17.7ms) Rendered projects/_zen.html.haml (0.3ms) Rendered projects/notes/_hints.html.haml (0.6ms) Rendered projects/_md_preview.html.haml (3.4ms) Rendered projects/notes/_edit_form.html.haml (7.0ms) ``` After: ``` Rendered projects/notes/_note.html.haml (13.8ms) Rendered projects/notes/_note.html.haml (7.1ms) Rendered projects/notes/_note.html.haml (9.5ms) Rendered projects/notes/_note.html.haml (8.5ms) ``` This change reduce at least 6ms * N ('N' - number of notes). --- app/assets/javascripts/notes.js.coffee | 13 ++++++------- app/controllers/projects/notes_controller.rb | 7 ++++++- app/views/projects/notes/_note.html.haml | 5 +---- app/views/projects/notes/_notes_with_form.html.haml | 2 +- app/views/projects/notes/edit.js.erb | 2 ++ config/routes.rb | 2 +- features/steps/project/issues/issues.rb | 2 +- spec/features/notes_on_merge_requests_spec.rb | 6 ------ spec/features/task_lists_spec.rb | 3 --- 9 files changed, 18 insertions(+), 24 deletions(-) create mode 100644 app/views/projects/notes/edit.js.erb diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ea75c656bcc..b0682f16845 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -29,7 +29,6 @@ class @Notes $(document).on "ajax:success", "form.edit_note", @updateNote # Edit note link - $(document).on "click", ".js-note-edit", @showEditForm $(document).on "click", ".note-edit-cancel", @cancelEdit # Reopen and close actions for Issue/MR combined with note form submit @@ -67,7 +66,6 @@ class @Notes $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" $(document).off "ajax:success", "form.edit_note" - $(document).off "click", ".js-note-edit" $(document).off "click", ".note-edit-cancel" $(document).off "click", ".js-note-delete" $(document).off "click", ".js-note-attachment-delete" @@ -287,13 +285,14 @@ class @Notes Adds a hidden div with the original content of the note to fill the edit note form with if the user cancels ### - showEditForm: (e) -> - e.preventDefault() - note = $(this).closest(".note") + showEditForm: (note, formHTML) -> + nodeText = note.find(".note-text"); + nodeText.hide() + note.find('.note-edit-form').remove() + nodeText.after(formHTML) note.find(".note-body > .note-text").hide() note.find(".note-header").hide() - base_form = note.find(".note-edit-form") - form = base_form.clone().insertAfter(base_form) + form = note.find(".note-edit-form") form.addClass('current-note-edit-form gfm-form') form.find('.div-dropzone').remove() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 41cd08c93c6..0c98e2f1bfd 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :delete_attachment] + before_action :find_current_user_notes, except: [:destroy, :edit, :delete_attachment] def index current_fetched_at = Time.now.to_i @@ -29,6 +29,11 @@ class Projects::NotesController < Projects::ApplicationController end end + def edit + @note = note + render layout: false + end + def update @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 5d184730796..d0e5286cbdd 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -7,7 +7,7 @@ .note-header - if note_editable?(note) .note-actions - = link_to '#', title: 'Edit comment', class: 'js-note-edit' do + = link_to edit_namespace_project_note_path(note.project.namespace, note.project, note), title: 'Edit comment', remote: true, class: 'js-note-edit' do = icon('pencil-square-o') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'js-note-delete danger' do @@ -59,9 +59,6 @@ .note-text = preserve do = markdown(note.note, {no_header_anchors: true}) - - unless note.system? - -# System notes can't be edited - = render 'projects/notes/edit_form', note: note - if note.attachment.url .note-attachment diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 04222b8f7c4..91cefa6d14d 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -7,4 +7,4 @@ = render "projects/notes/form", view: params[:view] :javascript - new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}") + window._notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}") diff --git a/app/views/projects/notes/edit.js.erb b/app/views/projects/notes/edit.js.erb new file mode 100644 index 00000000000..2599bad5d6e --- /dev/null +++ b/app/views/projects/notes/edit.js.erb @@ -0,0 +1,2 @@ +$note = $('.note-row-<%= @note.id %>:visible'); +_notes.showEditForm($note, '<%= escape_javascript(render('edit_form', note: @note)) %>'); diff --git a/config/routes.rb b/config/routes.rb index 2028ea938e4..6b053124fea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -658,7 +658,7 @@ Gitlab::Application.routes.draw do end end - resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do + resources :notes, constraints: { id: /\d+/ } do member do delete :delete_attachment end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..be134b9c2bb 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -219,7 +219,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'The code block should be unchanged' do - expect(page).to have_content("```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```") + expect(page).to have_content("Command [1]: /usr/local/bin/git , see [text](doc/text)") end step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index d7cb3b2e86e..16d5a03e88c 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -65,12 +65,6 @@ describe 'Comments', feature: true do end describe 'when editing a note', js: true do - it 'should contain the hidden edit form' do - page.within("#note_#{note.id}") do - is_expected.to have_css('.note-edit-form', visible: false) - end - end - describe 'editing the note' do before do find('.note').hover diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index fca3c77fc64..6cbe685a93b 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -51,7 +51,6 @@ feature 'Task Lists', feature: true do expect(page).to have_selector(container) expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") - expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector('form.js-issuable-update') expect(page).to have_selector('a.btn-close') end @@ -90,7 +89,6 @@ feature 'Task Lists', feature: true do expect(page).to have_selector('.note .js-task-list-container') expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox') - expect(page).to have_selector('.note .js-task-list-container .js-task-list-field') end it 'is only editable by author' do @@ -127,7 +125,6 @@ feature 'Task Lists', feature: true do expect(page).to have_selector(container) expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") - expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector('form.js-issuable-update') expect(page).to have_selector('a.btn-close') end -- cgit v1.2.1 From 77dd561796adb94236422fb9da50482271fadbb1 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 12 Nov 2015 08:43:30 +0000 Subject: fixing rubocop indents --- app/models/ability.rb | 22 +++++++++++----------- app/models/member.rb | 4 +++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index b46c4943bf5..6526cc6bc6a 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -6,17 +6,17 @@ class Ability return [] if user.blocked? case subject.class.name - when "Project" then project_abilities(user, subject) - when "Issue" then issue_abilities(user, subject) - when "Note" then note_abilities(user, subject) - when "ProjectSnippet" then project_snippet_abilities(user, subject) - when "PersonalSnippet" then personal_snippet_abilities(user, subject) - when "MergeRequest" then merge_request_abilities(user, subject) - when "Group" then group_abilities(user, subject) - when "Namespace" then namespace_abilities(user, subject) - when "GroupMember" then group_member_abilities(user, subject) - when "ProjectMember" then project_member_abilities(user, subject) - else [] + when "Project" then project_abilities(user, subject) + when "Issue" then issue_abilities(user, subject) + when "Note" then note_abilities(user, subject) + when "ProjectSnippet" then project_snippet_abilities(user, subject) + when "PersonalSnippet" then personal_snippet_abilities(user, subject) + when "MergeRequest" then merge_request_abilities(user, subject) + when "Group" then group_abilities(user, subject) + when "Namespace" then namespace_abilities(user, subject) + when "GroupMember" then group_member_abilities(user, subject) + when "ProjectMember" then project_member_abilities(user, subject) + else [] end.concat(global_abilities(user)) end diff --git a/app/models/member.rb b/app/models/member.rb index 30160c813e1..ab5a8e6228d 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -36,7 +36,9 @@ class Member < ActiveRecord::Base validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true validates :invite_email, presence: { if: :invite? }, email: { strict_mode: true, allow_nil: true }, - uniqueness: { scope: [:source_type, :source_id], allow_nil: true } + uniqueness: { scope: [:source_type, + :source_id], + allow_nil: true } scope :invite, -> { where(user_id: nil) } scope :non_invite, -> { where("user_id IS NOT NULL") } -- cgit v1.2.1 From e11bfa6b86c5b1b838e7e438bcaa599df668f7be Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 12 Nov 2015 08:44:00 +0000 Subject: fixing rubocop indents --- app/models/member.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/member.rb b/app/models/member.rb index ab5a8e6228d..a7c599b3598 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -35,7 +35,8 @@ class Member < ActiveRecord::Base allow_nil: true } validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true validates :invite_email, presence: { if: :invite? }, - email: { strict_mode: true, allow_nil: true }, + email: { strict_mode: true, + allow_nil: true }, uniqueness: { scope: [:source_type, :source_id], allow_nil: true } -- cgit v1.2.1 From 2e4a673cbc1a43532e8aa096e4ab5ca034b804f7 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 12 Nov 2015 17:19:03 +0800 Subject: Add caching for ApplicationSetting, Ci::ApplicationSetting. ApplicationSetting.current was called in every pages, cache it and expires it after it updated. This changes will avoid a SQL query in every pages (~0.3 - 0.5ms). ```SQL SELECT "application_settings".* FROM "application_settings" ORDER BY "application_settings"."id" DESC LIMIT 1 ``` --- app/models/application_setting.rb | 8 +++++++- app/models/ci/application_setting.rb | 10 ++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 266045f7afa..5a9e55a95f4 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -68,8 +68,14 @@ class ApplicationSetting < ActiveRecord::Base end end + after_commit do + Rails.cache.write('application_setting.last', self) + end + def self.current - ApplicationSetting.last + Rails.cache.fetch('application_setting.last') do + ApplicationSetting.last + end end def self.create_from_defaults diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 0cf496f7d81..4ab3e2dcbb3 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -12,9 +12,15 @@ module Ci class ApplicationSetting < ActiveRecord::Base extend Ci::Model - + + after_commit do + Rails.cache.write('ci_application_setting.last', self) + end + def self.current - Ci::ApplicationSetting.last + Rails.cache.fetch('ci_application_setting.last') do + Ci::ApplicationSetting.last + end end def self.create_from_defaults -- cgit v1.2.1 From e073b09f1f0d7b37ece6ecb3e7e485eb3f5e2e6f Mon Sep 17 00:00:00 2001 From: Jon Cairns Date: Tue, 3 Nov 2015 09:25:26 +0000 Subject: Add missing "omniauth" prefix to option in docs [ci skip] Changes block_auto_created_users to omniauth_block_auto_created_users, otherwise the option is ignored. Fixes #3319. --- CHANGELOG | 1 + doc/integration/omniauth.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 588909d2578..932b1b2fa13 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.2.0 (unreleased) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. - [API] Add ability to fetch the commit ID of the last commit that actually touched a file + - Fix omniauth documentation setting for omnibus configuration (Jon Cairns) v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index c5cecbc2f2d..3348ada0157 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -36,7 +36,7 @@ If you want to change these settings: ``` gitlab_rails['omniauth_enabled'] = true gitlab_rails['omniauth_allow_single_sign_on'] = false - gitlab_rails['block_auto_created_users'] = true + gitlab_rails['omniauth_block_auto_created_users'] = true ``` * **For installations from source** -- cgit v1.2.1 From 56ea71a3b19396b01b3b8495337fbf22c534524f Mon Sep 17 00:00:00 2001 From: James Lopez Date: Thu, 12 Nov 2015 12:29:01 +0000 Subject: fixing rubocop - random code not related to the changes --- app/models/member.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/models/member.rb b/app/models/member.rb index a7c599b3598..eed9f2537e9 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -35,11 +35,15 @@ class Member < ActiveRecord::Base allow_nil: true } validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true validates :invite_email, presence: { if: :invite? }, - email: { strict_mode: true, - allow_nil: true }, - uniqueness: { scope: [:source_type, - :source_id], - allow_nil: true } + email: { + strict_mode: true, + allow_nil: true + }, + uniqueness: { + scope: [:source_type, + :source_id], + allow_nil: true + } scope :invite, -> { where(user_id: nil) } scope :non_invite, -> { where("user_id IS NOT NULL") } -- cgit v1.2.1 From 3c96ea766a60e15272f9d9ad1d331c9372465d6a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 12:05:27 +0100 Subject: Fix changelog item Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 62def42d959..bcd4b6a21dd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -36,6 +36,7 @@ v 8.2.0 (unreleased) - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - Make color of "Accept Merge Request" button consistent with current build status + - Ability to add release notes (markdown text and attachments) to git tags (aka Releases) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From 2ac1193720907bccc1f321e9c56fb5ca2b0a3a1c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 10 Nov 2015 12:33:21 +0100 Subject: Add release notes documentation Signed-off-by: Dmitriy Zaporozhets --- doc/workflow/README.md | 1 + doc/workflow/releases.md | 20 ++++++++++++++++++++ doc/workflow/releases/new_tag.png | Bin 0 -> 154755 bytes doc/workflow/releases/tags.png | Bin 0 -> 165449 bytes 4 files changed, 21 insertions(+) create mode 100644 doc/workflow/releases.md create mode 100644 doc/workflow/releases/new_tag.png create mode 100644 doc/workflow/releases/tags.png diff --git a/doc/workflow/README.md b/doc/workflow/README.md index 5b8d72dfd34..a2653704c33 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -13,5 +13,6 @@ - [Project users](add-user/add-user.md) - [Protected branches](protected_branches.md) - [Web Editor](web_editor.md) +- [Releases](releases.md) - [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) diff --git a/doc/workflow/releases.md b/doc/workflow/releases.md new file mode 100644 index 00000000000..0eca220e2cf --- /dev/null +++ b/doc/workflow/releases.md @@ -0,0 +1,20 @@ +# Releases + +You can turn any git tag into release by just adding a release notes to it. +Release notes behaves like any other markdown form in GitLab so you can write text and drag-n-drop files to it. +Release notes are stored in GitLab database. + +There are several ways to add release notes: + +* In UI when you create new git tag with GitLab +* In UI when you add release notes to existing git tag +* with GitLab API + +## New tag page with release notes text area + +![new_tag](releases/new_tag.png) + +## Tags page with button to add or edit release notes for existing git tag + +![tags](releases/tags.png) + diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png new file mode 100644 index 00000000000..e2b64bfe17f Binary files /dev/null and b/doc/workflow/releases/new_tag.png differ diff --git a/doc/workflow/releases/tags.png b/doc/workflow/releases/tags.png new file mode 100644 index 00000000000..aca91906c68 Binary files /dev/null and b/doc/workflow/releases/tags.png differ -- cgit v1.2.1 From a5ab56fd9191e23dfa60707fa42802342c1563f8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 15:41:13 +0100 Subject: Move git tags API to separate file Signed-off-by: Dmitriy Zaporozhets --- doc/api/repositories.md | 74 ---------------------------- doc/api/tags.md | 75 ++++++++++++++++++++++++++++ lib/api/api.rb | 1 + lib/api/repositories.rb | 35 ------------- lib/api/tags.rb | 44 +++++++++++++++++ spec/requests/api/repositories_spec.rb | 75 ---------------------------- spec/requests/api/tags_spec.rb | 89 ++++++++++++++++++++++++++++++++++ 7 files changed, 209 insertions(+), 184 deletions(-) create mode 100644 doc/api/tags.md create mode 100644 lib/api/tags.rb create mode 100644 spec/requests/api/tags_spec.rb diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 33167453802..b6cca5d4e2a 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -1,79 +1,5 @@ # Repositories -## List project repository tags - -Get a list of repository tags from a project, sorted by name in reverse alphabetical order. - -``` -GET /projects/:id/repository/tags -``` - -Parameters: - -- `id` (required) - The ID of a project - -```json -[ - { - "commit": { - "author_name": "John Smith", - "author_email": "john@example.com", - "authored_date": "2012-05-28T04:42:42-07:00", - "committed_date": "2012-05-28T04:42:42-07:00", - "committer_name": "Jack Smith", - "committer_email": "jack@example.com", - "id": "2695effb5807a22ff3d138d593fd856244e155e7", - "message": "Initial commit", - "parents_ids": [ - "2a4b78934375d7f53875269ffd4f45fd83a84ebe" - ] - }, - "name": "v1.0.0", - "message": null - } -] -``` - -## Create a new tag - -Creates new tag in the repository that points to the supplied ref. - -``` -POST /projects/:id/repository/tags -``` - -Parameters: - -- `id` (required) - The ID of a project -- `tag_name` (required) - The name of a tag -- `ref` (required) - Create tag using commit SHA, another tag name, or branch name. -- `message` (optional) - Creates annotated tag. - -```json -{ - "commit": { - "author_name": "John Smith", - "author_email": "john@example.com", - "authored_date": "2012-05-28T04:42:42-07:00", - "committed_date": "2012-05-28T04:42:42-07:00", - "committer_name": "Jack Smith", - "committer_email": "jack@example.com", - "id": "2695effb5807a22ff3d138d593fd856244e155e7", - "message": "Initial commit", - "parents_ids": [ - "2a4b78934375d7f53875269ffd4f45fd83a84ebe" - ] - }, - "name": "v1.0.0", - "message": null -} -``` -The message will be `nil` when creating a lightweight tag otherwise -it will contain the annotation. - -It returns 200 if the operation succeed. In case of an error, -405 with an explaining error message is returned. - ## List repository tree Get a list of repository files and directories in a project. diff --git a/doc/api/tags.md b/doc/api/tags.md new file mode 100644 index 00000000000..dc69a0c6fbc --- /dev/null +++ b/doc/api/tags.md @@ -0,0 +1,75 @@ +# Tags + +## List project repository tags + +Get a list of repository tags from a project, sorted by name in reverse alphabetical order. + +``` +GET /projects/:id/repository/tags +``` + +Parameters: + +- `id` (required) - The ID of a project + +```json +[ + { + "commit": { + "author_name": "John Smith", + "author_email": "john@example.com", + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00", + "committer_name": "Jack Smith", + "committer_email": "jack@example.com", + "id": "2695effb5807a22ff3d138d593fd856244e155e7", + "message": "Initial commit", + "parents_ids": [ + "2a4b78934375d7f53875269ffd4f45fd83a84ebe" + ] + }, + "name": "v1.0.0", + "message": null + } +] +``` + +## Create a new tag + +Creates new tag in the repository that points to the supplied ref. + +``` +POST /projects/:id/repository/tags +``` + +Parameters: + +- `id` (required) - The ID of a project +- `tag_name` (required) - The name of a tag +- `ref` (required) - Create tag using commit SHA, another tag name, or branch name. +- `message` (optional) - Creates annotated tag. + +```json +{ + "commit": { + "author_name": "John Smith", + "author_email": "john@example.com", + "authored_date": "2012-05-28T04:42:42-07:00", + "committed_date": "2012-05-28T04:42:42-07:00", + "committer_name": "Jack Smith", + "committer_email": "jack@example.com", + "id": "2695effb5807a22ff3d138d593fd856244e155e7", + "message": "Initial commit", + "parents_ids": [ + "2a4b78934375d7f53875269ffd4f45fd83a84ebe" + ] + }, + "name": "v1.0.0", + "message": null +} +``` +The message will be `nil` when creating a lightweight tag otherwise +it will contain the annotation. + +It returns 200 if the operation succeed. In case of an error, +405 with an explaining error message is returned. diff --git a/lib/api/api.rb b/lib/api/api.rb index 40671e2517c..fe1bf8a4816 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -52,5 +52,6 @@ module API mount Labels mount Settings mount Keys + mount Tags end end diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 20d568cf462..d7c48639eba 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -16,41 +16,6 @@ module API end end - # Get a project repository tags - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/repository/tags - get ":id/repository/tags" do - present user_project.repo.tags.sort_by(&:name).reverse, - with: Entities::RepoTag, project: user_project - end - - # Create tag - # - # Parameters: - # id (required) - The ID of a project - # tag_name (required) - The name of the tag - # ref (required) - Create tag from commit sha or branch - # message (optional) - Specifying a message creates an annotated tag. - # Example Request: - # POST /projects/:id/repository/tags - post ':id/repository/tags' do - authorize_push_project - message = params[:message] || nil - result = CreateTagService.new(user_project, current_user). - execute(params[:tag_name], params[:ref], message) - - if result[:status] == :success - present result[:tag], - with: Entities::RepoTag, - project: user_project - else - render_api_error!(result[:message], 400) - end - end - # Get a project repository tree # # Parameters: diff --git a/lib/api/tags.rb b/lib/api/tags.rb new file mode 100644 index 00000000000..da962bd402a --- /dev/null +++ b/lib/api/tags.rb @@ -0,0 +1,44 @@ +module API + # Releases API + class Tags < Grape::API + before { authenticate! } + before { authorize! :download_code, user_project } + + resource :projects do + # Get a project repository tags + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # GET /projects/:id/repository/tags + get ":id/repository/tags" do + present user_project.repo.tags.sort_by(&:name).reverse, + with: Entities::RepoTag, project: user_project + end + + # Create tag + # + # Parameters: + # id (required) - The ID of a project + # tag_name (required) - The name of the tag + # ref (required) - Create tag from commit sha or branch + # message (optional) - Specifying a message creates an annotated tag. + # Example Request: + # POST /projects/:id/repository/tags + post ':id/repository/tags' do + authorize_push_project + message = params[:message] || nil + result = CreateTagService.new(user_project, current_user). + execute(params[:tag_name], params[:ref], message) + + if result[:status] == :success + present result[:tag], + with: Entities::RepoTag, + project: user_project + else + render_api_error!(result[:message], 400) + end + end + end + end +end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index faf6b77a462..4911cdd9da6 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -11,81 +11,6 @@ describe API::API, api: true do let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } - describe "GET /projects/:id/repository/tags" do - it "should return an array of project tags" do - get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name) - end - end - - describe 'POST /projects/:id/repository/tags' do - context 'lightweight tags' do - it 'should create a new tag' do - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v7.0.1', - ref: 'master' - - expect(response.status).to eq(201) - expect(json_response['name']).to eq('v7.0.1') - end - end - - context 'annotated tag' do - it 'should create a new annotated tag' do - # Identity must be set in .gitconfig to create annotated tag. - repo_path = project.repository.path_to_repo - system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name})) - system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email})) - - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v7.1.0', - ref: 'master', - message: 'Release 7.1.0' - - expect(response.status).to eq(201) - expect(json_response['name']).to eq('v7.1.0') - expect(json_response['message']).to eq('Release 7.1.0') - end - end - - it 'should deny for user without push access' do - post api("/projects/#{project.id}/repository/tags", user2), - tag_name: 'v1.9.0', - ref: '621491c677087aa243f165eab467bfdfbee00be1' - expect(response.status).to eq(403) - end - - it 'should return 400 if tag name is invalid' do - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v 1.0.0', - ref: 'master' - expect(response.status).to eq(400) - expect(json_response['message']).to eq('Tag name invalid') - end - - it 'should return 400 if tag already exists' do - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v8.0.0', - ref: 'master' - expect(response.status).to eq(201) - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v8.0.0', - ref: 'master' - expect(response.status).to eq(400) - expect(json_response['message']).to eq('Tag already exists') - end - - it 'should return 400 if ref name is invalid' do - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'mytag', - ref: 'foo' - expect(response.status).to eq(400) - expect(json_response['message']).to eq('Invalid reference name') - end - end - describe "GET /projects/:id/repository/tree" do context "authorized user" do before { project.team << [user2, :reporter] } diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb new file mode 100644 index 00000000000..6ddd7030cea --- /dev/null +++ b/spec/requests/api/tags_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' +require 'mime/types' + +describe API::API, api: true do + include ApiHelpers + include RepoHelpers + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } + let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } + + + describe "GET /projects/:id/repository/tags" do + it "should return an array of project tags" do + get api("/projects/#{project.id}/repository/tags", user) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name) + end + end + + describe 'POST /projects/:id/repository/tags' do + context 'lightweight tags' do + it 'should create a new tag' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v7.0.1', + ref: 'master' + + expect(response.status).to eq(201) + expect(json_response['name']).to eq('v7.0.1') + end + end + + context 'annotated tag' do + it 'should create a new annotated tag' do + # Identity must be set in .gitconfig to create annotated tag. + repo_path = project.repository.path_to_repo + system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name})) + system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email})) + + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v7.1.0', + ref: 'master', + message: 'Release 7.1.0' + + expect(response.status).to eq(201) + expect(json_response['name']).to eq('v7.1.0') + expect(json_response['message']).to eq('Release 7.1.0') + end + end + + it 'should deny for user without push access' do + post api("/projects/#{project.id}/repository/tags", user2), + tag_name: 'v1.9.0', + ref: '621491c677087aa243f165eab467bfdfbee00be1' + expect(response.status).to eq(403) + end + + it 'should return 400 if tag name is invalid' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v 1.0.0', + ref: 'master' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Tag name invalid') + end + + it 'should return 400 if tag already exists' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v8.0.0', + ref: 'master' + expect(response.status).to eq(201) + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v8.0.0', + ref: 'master' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Tag already exists') + end + + it 'should return 400 if ref name is invalid' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'mytag', + ref: 'foo' + expect(response.status).to eq(400) + expect(json_response['message']).to eq('Invalid reference name') + end + end +end -- cgit v1.2.1 From d343d9d8c2f3db0ca5f6fab8ad19e0f79f66e138 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 16:26:23 +0100 Subject: Add grape routing print Signed-off-by: Dmitriy Zaporozhets --- lib/tasks/grape.rake | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/tasks/grape.rake diff --git a/lib/tasks/grape.rake b/lib/tasks/grape.rake new file mode 100644 index 00000000000..9980e0b7984 --- /dev/null +++ b/lib/tasks/grape.rake @@ -0,0 +1,8 @@ +namespace :grape do + desc 'Print compiled grape routes' + task routes: :environment do + API::API.routes.each do |route| + puts route + end + end +end -- cgit v1.2.1 From c119a737935f1e1e8ae7dec814be8accb8980e1d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 16:26:39 +0100 Subject: Add releases api Signed-off-by: Dmitriy Zaporozhets --- lib/api/entities.rb | 4 ++++ lib/api/tags.rb | 19 ++++++++++++++++++- spec/requests/api/tags_spec.rb | 15 ++++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 20cadae2291..400900bc407 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -341,5 +341,9 @@ module API expose :user_oauth_applications expose :after_sign_out_path end + + class Release < Grape::Entity + expose :tag, :description + end end end diff --git a/lib/api/tags.rb b/lib/api/tags.rb index da962bd402a..06fa1d23fd6 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -1,5 +1,5 @@ module API - # Releases API + # Git Tags API class Tags < Grape::API before { authenticate! } before { authorize! :download_code, user_project } @@ -39,6 +39,23 @@ module API render_api_error!(result[:message], 400) end end + + # Add release notes to tag + # + # Parameters: + # id (required) - The ID of a project + # tag (required) - The name of the tag + # description (required) - Release notes with markdown support + # Example Request: + # PUT /projects/:id/repository/tags + put ':id/repository/:tag/release', requirements: { tag: /.*/ } do + authorize_push_project + required_attributes! [:description] + release = user_project.releases.find_or_initialize_by(tag: params[:tag]) + release.update_attributes(description: params[:description]) + + present release, with: Entities::Release + end end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 6ddd7030cea..72df9007ee8 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -11,7 +11,6 @@ describe API::API, api: true do let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } - describe "GET /projects/:id/repository/tags" do it "should return an array of project tags" do get api("/projects/#{project.id}/repository/tags", user) @@ -86,4 +85,18 @@ describe API::API, api: true do expect(json_response['message']).to eq('Invalid reference name') end end + + describe 'PUT /projects/:id/repository/:tag/release' do + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + + it 'should create description for existing git tag' do + put api("/projects/#{project.id}/repository/#{tag_name}/release", user), + description: description + + expect(response.status).to eq(200) + expect(json_response['tag']).to eq(tag_name) + expect(json_response['description']).to eq(description) + end + end end -- cgit v1.2.1 From ba34045f3120fc52ce714d49778a24c647248500 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 17:04:18 +0100 Subject: Expose release notes to tags api Signed-off-by: Dmitriy Zaporozhets --- lib/api/entities.rb | 6 ++++++ spec/requests/api/tags_spec.rb | 30 +++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 400900bc407..7528e718c6f 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -112,6 +112,12 @@ module API options[:project].repository.commit(repo_obj.target) end end + + expose :release do |repo_obj, options| + if options[:project] + options[:project].releases.find_by(tag: repo_obj.name) + end + end end class RepoObject < Grape::Entity diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 72df9007ee8..4362e0ec28d 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -12,11 +12,31 @@ describe API::API, api: true do let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) } describe "GET /projects/:id/repository/tags" do - it "should return an array of project tags" do - get api("/projects/#{project.id}/repository/tags", user) - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name) + let(:tag_name) { project.repository.tag_names.sort.reverse.first } + let(:description) { 'Awesome release!' } + + context 'without releases' do + it "should return an array of project tags" do + get api("/projects/#{project.id}/repository/tags", user) + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(tag_name) + end + end + + context 'with releases' do + before do + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: description) + get api("/projects/#{project.id}/repository/tags", user) + end + + it "should return an array of project tags with release info" do + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(tag_name) + expect(json_response.first['release']['description']).to eq(description) + end end end -- cgit v1.2.1 From 8f53094f0f22fe208f6f6e0d39037188bf14a937 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 23:52:02 +0100 Subject: Add API docs and correctly expose release api Signed-off-by: Dmitriy Zaporozhets --- doc/api/tags.md | 31 +++++++++++++++++++++++++++++++ lib/api/entities.rb | 50 +++++++++++++++++++++++++------------------------- lib/api/tags.rb | 2 +- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index dc69a0c6fbc..0113d4ba049 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -28,6 +28,10 @@ Parameters: "2a4b78934375d7f53875269ffd4f45fd83a84ebe" ] }, + "release": { + "tag": "1.0.0", + "description": "Amazing release. Wow" + }, "name": "v1.0.0", "message": null } @@ -48,6 +52,7 @@ Parameters: - `tag_name` (required) - The name of a tag - `ref` (required) - Create tag using commit SHA, another tag name, or branch name. - `message` (optional) - Creates annotated tag. +- `release_description` (optional) - Add release notes to the git tag and store it in the GitLab database. ```json { @@ -64,6 +69,10 @@ Parameters: "2a4b78934375d7f53875269ffd4f45fd83a84ebe" ] }, + "release": { + "tag": "1.0.0", + "description": "Amazing release. Wow" + }, "name": "v1.0.0", "message": null } @@ -73,3 +82,25 @@ it will contain the annotation. It returns 200 if the operation succeed. In case of an error, 405 with an explaining error message is returned. + + +## New release + +Add release notes to the existing git tag + +``` +PUT /projects/:id/repository/:tag/release +``` + +Parameters: + +- `id` (required) - The ID of a project +- `tag` (required) - The name of a tag +- `description` (required) - Release notes with markdown support + +```json +{ + "tag": "1.0.0", + "description": "Amazing release. Wow" +} +``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 7528e718c6f..3ca632dfae9 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -95,31 +95,6 @@ module API end end - class RepoTag < Grape::Entity - expose :name - expose :message do |repo_obj, _options| - if repo_obj.respond_to?(:message) - repo_obj.message - else - nil - end - end - - expose :commit do |repo_obj, options| - if repo_obj.respond_to?(:commit) - repo_obj.commit - elsif options[:project] - options[:project].repository.commit(repo_obj.target) - end - end - - expose :release do |repo_obj, options| - if options[:project] - options[:project].releases.find_by(tag: repo_obj.name) - end - end - end - class RepoObject < Grape::Entity expose :name @@ -351,5 +326,30 @@ module API class Release < Grape::Entity expose :tag, :description end + + class RepoTag < Grape::Entity + expose :name + expose :message do |repo_obj, _options| + if repo_obj.respond_to?(:message) + repo_obj.message + else + nil + end + end + + expose :commit do |repo_obj, options| + if repo_obj.respond_to?(:commit) + repo_obj.commit + elsif options[:project] + options[:project].repository.commit(repo_obj.target) + end + end + + expose :release, using: Entities::Release do |repo_obj, options| + if options[:project] + options[:project].releases.find_by(tag: repo_obj.name) + end + end + end end end diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 06fa1d23fd6..673342dd447 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -29,7 +29,7 @@ module API authorize_push_project message = params[:message] || nil result = CreateTagService.new(user_project, current_user). - execute(params[:tag_name], params[:ref], message) + execute(params[:tag_name], params[:ref], message, params[:release_description]) if result[:status] == :success present result[:tag], -- cgit v1.2.1 From d6db451732a5ed9f31f3a345dd936abc1c07c6cf Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 12 Nov 2015 23:54:22 +0100 Subject: Add api test for creating tag with release info Signed-off-by: Dmitriy Zaporozhets --- spec/requests/api/tags_spec.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 4362e0ec28d..cc9a5f47582 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -52,6 +52,19 @@ describe API::API, api: true do end end + context 'lightweight tags with release notes' do + it 'should create a new tag' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v7.0.1', + ref: 'master', + release_description: 'Wow' + + expect(response.status).to eq(201) + expect(json_response['name']).to eq('v7.0.1') + expect(json_response['release']['description']).to eq('Wow') + end + end + context 'annotated tag' do it 'should create a new annotated tag' do # Identity must be set in .gitconfig to create annotated tag. -- cgit v1.2.1 From e5cc197a993fd0fbaafd7eeac6253fe38b8a96ad Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:06:42 +0100 Subject: Split huge method MergeRequests::UpdateService#execute Signed-off-by: Dmitriy Zaporozhets --- app/services/merge_requests/update_service.rb | 47 ++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index 61f7d2bbe89..d2849e5193f 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -35,35 +35,38 @@ module MergeRequests ) end - if merge_request.previous_changes.include?('target_branch') - create_branch_change_note(merge_request, 'target', - merge_request.previous_changes['target_branch'].first, - merge_request.target_branch) - end + handle_changes(merge_request) + merge_request.create_new_cross_references!(current_user) + execute_hooks(merge_request, 'update') + end - if merge_request.previous_changes.include?('milestone_id') - create_milestone_note(merge_request) - end + merge_request + end - if merge_request.previous_changes.include?('assignee_id') - create_assignee_note(merge_request) - notification_service.reassigned_merge_request(merge_request, current_user) - end + def handle_changes(merge_request) + if merge_request.previous_changes.include?('target_branch') + create_branch_change_note(merge_request, 'target', + merge_request.previous_changes['target_branch'].first, + merge_request.target_branch) + end - if merge_request.previous_changes.include?('title') - create_title_change_note(merge_request, merge_request.previous_changes['title'].first) - end + if merge_request.previous_changes.include?('milestone_id') + create_milestone_note(merge_request) + end - if merge_request.previous_changes.include?('target_branch') || - merge_request.previous_changes.include?('source_branch') - merge_request.mark_as_unchecked - end + if merge_request.previous_changes.include?('assignee_id') + create_assignee_note(merge_request) + notification_service.reassigned_merge_request(merge_request, current_user) + end - merge_request.create_new_cross_references!(current_user) - execute_hooks(merge_request, 'update') + if merge_request.previous_changes.include?('title') + create_title_change_note(merge_request, merge_request.previous_changes['title'].first) end - merge_request + if merge_request.previous_changes.include?('target_branch') || + merge_request.previous_changes.include?('source_branch') + merge_request.mark_as_unchecked + end end end end -- cgit v1.2.1 From 7f0b4ce16f14129f9c3f94b4a83fa8a6426cd4ba Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:12:18 +0100 Subject: Split complex method SystemHooksService#build_event_data Signed-off-by: Dmitriy Zaporozhets --- app/services/system_hooks_service.rb | 72 +++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 30 deletions(-) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 9a5fe4af9dd..8b5143e1eb7 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -33,17 +33,7 @@ class SystemHooksService ) end when Project - owner = model.owner - - data.merge!({ - name: model.name, - path: model.path, - path_with_namespace: model.path_with_namespace, - project_id: model.id, - owner_name: owner.name, - owner_email: owner.respond_to?(:email) ? owner.email : "", - project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase - }) + data.merge!(project_data(model)) when User data.merge!({ name: model.name, @@ -51,16 +41,7 @@ class SystemHooksService user_id: model.id }) when ProjectMember - 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, - access_level: model.human_access, - project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase - }) + data.merge!(project_member_data(model)) when Group owner = model.owner @@ -72,15 +53,7 @@ class SystemHooksService owner_email: owner.respond_to?(:email) ? owner.email : nil, ) when GroupMember - data.merge!( - group_name: model.group.name, - group_path: model.group.path, - group_id: model.group.id, - user_name: model.user.name, - user_email: model.user.email, - user_id: model.user.id, - group_access: model.human_access, - ) + data.merge!(group_member_data(model)) end end @@ -96,4 +69,43 @@ class SystemHooksService "#{model.class.name.downcase}_#{event.to_s}" end end + + def project_data(model) + owner = model.owner + + { + name: model.name, + path: model.path, + path_with_namespace: model.path_with_namespace, + project_id: model.id, + owner_name: owner.name, + owner_email: owner.respond_to?(:email) ? owner.email : "", + project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase + } + end + + def project_member_data(model) + { + 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, + access_level: model.human_access, + project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase + } + end + + def group_member_data(model) + { + group_name: model.group.name, + group_path: model.group.path, + group_id: model.group.id, + user_name: model.user.name, + user_email: model.user.email, + user_id: model.user.id, + group_access: model.human_access, + } + end end -- cgit v1.2.1 From 857710634c516cea8516e73132027a361364211c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:13:45 +0100 Subject: Split complex method Issues::UpdateService#execute Signed-off-by: Dmitriy Zaporozhets --- app/services/issues/update_service.rb | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 551325e4cab..aa1fd79d22d 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -22,24 +22,27 @@ module Issues issue, issue.labels - old_labels, old_labels - issue.labels) end - if issue.previous_changes.include?('milestone_id') - create_milestone_note(issue) - end - - if issue.previous_changes.include?('assignee_id') - create_assignee_note(issue) - notification_service.reassigned_issue(issue, current_user) - end - - if issue.previous_changes.include?('title') - create_title_change_note(issue, issue.previous_changes['title'].first) - end - + handle_changes(issue) issue.create_new_cross_references!(current_user) execute_hooks(issue, 'update') end issue end + + def handle_changes(issue) + if issue.previous_changes.include?('milestone_id') + create_milestone_note(issue) + end + + if issue.previous_changes.include?('assignee_id') + create_assignee_note(issue) + notification_service.reassigned_issue(issue, current_user) + end + + if issue.previous_changes.include?('title') + create_title_change_note(issue, issue.previous_changes['title'].first) + end + end end end -- cgit v1.2.1 From bcc82511966aba863a0a912302e8cfe3368ebc32 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:15:33 +0100 Subject: Split complex method EventsHelper#event_feed_url. Signed-off-by: Dmitriy Zaporozhets --- app/helpers/events_helper.rb | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 51b872b7a95..dde83ff36b5 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -108,19 +108,23 @@ module EventsHelper end end elsif event.push? - if event.push_with_commits? && event.md_ref? - if event.commits_count > 1 - namespace_project_compare_url(event.project.namespace, event.project, - from: event.commit_from, to: - event.commit_to) - else - namespace_project_commit_url(event.project.namespace, event.project, - id: event.commit_to) - end + push_event_feed_url(event) + end + end + + def push_event_feed_url(event) + if event.push_with_commits? && event.md_ref? + if event.commits_count > 1 + namespace_project_compare_url(event.project.namespace, event.project, + from: event.commit_from, to: + event.commit_to) else - namespace_project_commits_url(event.project.namespace, event.project, - event.ref_name) + namespace_project_commit_url(event.project.namespace, event.project, + id: event.commit_to) end + else + namespace_project_commits_url(event.project.namespace, event.project, + event.ref_name) end end -- cgit v1.2.1 From ea6a78ef5cb665d6f3b06d71fa693ffa895b4b1a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:44:16 +0100 Subject: Split complex Gitlab::InlineDiff::processing method Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/inline_diff.rb | 55 +++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb index 99e7b529ba9..420c3099ce7 100644 --- a/lib/gitlab/inline_diff.rb +++ b/lib/gitlab/inline_diff.rb @@ -16,15 +16,7 @@ module Gitlab # Skip inline diff if empty line was replaced with content next if first_line == "-\n" - first_the_same_symbols = 0 - (0..max_length + 1).each do |i| - first_the_same_symbols = i - 1 - if first_line[i] != second_line[i] && i > 0 - break - end - end - - first_token = first_line[0..first_the_same_symbols][1..-1] + first_token = find_first_token(first_line, second_line) start = first_token + START if first_token.empty? @@ -36,25 +28,50 @@ module Gitlab diff_arr[index+2].sub!(first_token, first_token => start) end - last_the_same_symbols = 0 - (1..max_length + 1).each do |i| - last_the_same_symbols = -i - shortest_line = second_line.size > first_line.size ? first_line : second_line - if ( first_line[-i] != second_line[-i] ) || "#{first_token}#{START}".size == shortest_line[1..-i].size - break - end - end - last_the_same_symbols += 1 - last_token = first_line[last_the_same_symbols..-1] + last_token = find_last_token(first_line, second_line, first_token) + # This is tricky: escape backslashes so that `sub` doesn't interpret them # as backreferences. Regexp.escape does NOT do the right thing. replace_token = FINISH + last_token.gsub(/\\/, '\&\&') diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token) diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token) end + diff_arr end + def find_first_token(first_line, second_line) + max_length = [first_line.size, second_line.size].max + first_the_same_symbols = 0 + + (0..max_length + 1).each do |i| + first_the_same_symbols = i - 1 + + if first_line[i] != second_line[i] && i > 0 + break + end + end + + first_line[0..first_the_same_symbols][1..-1] + end + + def find_last_token(first_line, second_line, first_token) + max_length = [first_line.size, second_line.size].max + last_the_same_symbols = 0 + + (1..max_length + 1).each do |i| + last_the_same_symbols = -i + shortest_line = second_line.size > first_line.size ? first_line : second_line + + if (first_line[-i] != second_line[-i]) || "#{first_token}#{START}".size == shortest_line[1..-i].size + break + end + end + + last_the_same_symbols += 1 + first_line[last_the_same_symbols..-1] + end + def _indexes_of_changed_lines(diff_arr) chain_of_first_symbols = "" diff_arr.each_with_index do |line, i| -- cgit v1.2.1 From c903f23f3c0b5654c2883cdf63593e53a8898a79 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 00:55:20 +0100 Subject: Split complex methods in GoogleCodeImport::Importer Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/google_code_import/importer.rb | 93 ++++++++++++++++--------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 03c410726a5..87fee28dc01 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -30,7 +30,7 @@ module Gitlab def user_map @user_map ||= begin - user_map = Hash.new do |hash, user| + user_map = Hash.new do |hash, user| # Replace ... by \.\.\., so `johnsm...@gmail.com` isn't autolinked. Client.mask_email(user).sub("...", "\\.\\.\\.") end @@ -76,18 +76,7 @@ module Gitlab attachments = format_attachments(raw_issue["id"], 0, issue_comment["attachments"]) body = format_issue_body(author, date, content, attachments) - - labels = [] - raw_issue["labels"].each do |label| - name = nice_label_name(label) - labels << name - - unless @known_labels.include?(name) - create_label(name) - @known_labels << name - end - end - labels << nice_status_name(raw_issue["status"]) + labels = import_issue_labels(raw_issue) assignee_id = nil if raw_issue.has_key?("owner") @@ -110,6 +99,7 @@ module Gitlab assignee_id: assignee_id, state: raw_issue["state"] == "closed" ? "closed" : "opened" ) + issue.add_labels_by_names(labels) if issue.iid != raw_issue["id"] @@ -120,6 +110,23 @@ module Gitlab end end + def import_issue_labels(raw_issue) + labels = [] + + raw_issue["labels"].each do |label| + name = nice_label_name(label) + labels << name + + unless @known_labels.include?(name) + create_label(name) + @known_labels << name + end + end + + labels << nice_status_name(raw_issue["status"]) + labels + end + def import_issue_comments(issue, comments) Note.transaction do while raw_comment = comments.shift @@ -172,7 +179,7 @@ module Gitlab "#5cb85c" when "Status: Started" "#8e44ad" - + when "Priority: Critical" "#ffcfcf" when "Priority: High" @@ -181,7 +188,7 @@ module Gitlab "#fff5cc" when "Priority: Low" "#cfe9ff" - + when "Type: Defect" "#d9534f" when "Type: Enhancement" @@ -249,8 +256,8 @@ module Gitlab end if raw_updates.has_key?("cc") - cc = raw_updates["cc"].map do |l| - deleted = l.start_with?("-") + cc = raw_updates["cc"].map do |l| + deleted = l.start_with?("-") l = l[1..-1] if deleted l = user_map[l] l = "~~#{l}~~" if deleted @@ -261,8 +268,8 @@ module Gitlab end if raw_updates.has_key?("labels") - labels = raw_updates["labels"].map do |l| - deleted = l.start_with?("-") + labels = raw_updates["labels"].map do |l| + deleted = l.start_with?("-") l = l[1..-1] if deleted l = nice_label_name(l) l = "~~#{l}~~" if deleted @@ -278,45 +285,39 @@ module Gitlab if raw_updates.has_key?("blockedOn") blocked_ons = raw_updates["blockedOn"].map do |raw_blocked_on| - name, id = raw_blocked_on.split(":", 2) - - deleted = name.start_with?("-") - name = name[1..-1] if deleted - - text = - if name == project.import_source - "##{id}" - else - "#{project.namespace.path}/#{name}##{id}" - end - text = "~~#{text}~~" if deleted - text + format_blocking_updates(raw_blocked_on) end + updates << "*Blocked on: #{blocked_ons.join(", ")}*" end if raw_updates.has_key?("blocking") blockings = raw_updates["blocking"].map do |raw_blocked_on| - name, id = raw_blocked_on.split(":", 2) - - deleted = name.start_with?("-") - name = name[1..-1] if deleted - - text = - if name == project.import_source - "##{id}" - else - "#{project.namespace.path}/#{name}##{id}" - end - text = "~~#{text}~~" if deleted - text + format_blocking_updates(raw_blocked_on) end + updates << "*Blocking: #{blockings.join(", ")}*" end updates end + def format_blocking_updates(raw_blocked_on) + name, id = raw_blocked_on.split(":", 2) + + deleted = name.start_with?("-") + name = name[1..-1] if deleted + + text = + if name == project.import_source + "##{id}" + else + "#{project.namespace.path}/#{name}##{id}" + end + text = "~~#{text}~~" if deleted + text + end + def format_attachments(issue_id, comment_id, raw_attachments) return [] unless raw_attachments @@ -325,7 +326,7 @@ module Gitlab filename = attachment["fileName"] link = "https://storage.googleapis.com/google-code-attachments/#{@repo.name}/issue-#{issue_id}/comment-#{comment_id}/#{filename}" - + text = "[#{filename}](#{link})" text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/i text -- cgit v1.2.1 From c71892e1a87ece19edf17f1d61c71d4b99953bc7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 01:07:33 +0100 Subject: Even more refactoring to inline_diff.rb Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/inline_diff.rb | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb index 420c3099ce7..72d0b493be0 100644 --- a/lib/gitlab/inline_diff.rb +++ b/lib/gitlab/inline_diff.rb @@ -17,29 +17,36 @@ module Gitlab next if first_line == "-\n" first_token = find_first_token(first_line, second_line) - start = first_token + START - - if first_token.empty? - # In case if we remove string of spaces in commit - diff_arr[index+1].sub!("-", "-" => "-#{START}") - diff_arr[index+2].sub!("+", "+" => "+#{START}") - else - diff_arr[index+1].sub!(first_token, first_token => start) - diff_arr[index+2].sub!(first_token, first_token => start) - end + apply_first_token(diff_arr, index, first_token) last_token = find_last_token(first_line, second_line, first_token) - - # This is tricky: escape backslashes so that `sub` doesn't interpret them - # as backreferences. Regexp.escape does NOT do the right thing. - replace_token = FINISH + last_token.gsub(/\\/, '\&\&') - diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token) - diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token) + apply_last_token(diff_arr, index, last_token) end diff_arr end + def apply_first_token(diff_arr, index, first_token) + start = first_token + START + + if first_token.empty? + # In case if we remove string of spaces in commit + diff_arr[index+1].sub!("-", "-" => "-#{START}") + diff_arr[index+2].sub!("+", "+" => "+#{START}") + else + diff_arr[index+1].sub!(first_token, first_token => start) + diff_arr[index+2].sub!(first_token, first_token => start) + end + end + + def apply_last_token(diff_arr, index, last_token) + # This is tricky: escape backslashes so that `sub` doesn't interpret them + # as backreferences. Regexp.escape does NOT do the right thing. + replace_token = FINISH + last_token.gsub(/\\/, '\&\&') + diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token) + diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token) + end + def find_first_token(first_line, second_line) max_length = [first_line.size, second_line.size].max first_the_same_symbols = 0 -- cgit v1.2.1 From c613f2d9a85c60bdf34fdafdda41dd5ee1e32904 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 01:08:10 +0100 Subject: Don't allow flog failure any more Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 128faa07db8..141e7ba41de 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -80,7 +80,6 @@ flog: tags: - ruby - mysql - allow_failure: true flay: script: -- cgit v1.2.1 From 3d0efa8e0a359c84485a0fd7a3317745bf5648b8 Mon Sep 17 00:00:00 2001 From: Minsik Yoon Date: Thu, 22 Oct 2015 09:55:35 +0900 Subject: Add ignore white space option in merge request diff fix this issue(https://gitlab.com/gitlab-org/gitlab-ce/issues/1393). Add ignore whitespace optoin to Commits Compare view --- CHANGELOG | 1 + app/controllers/projects/compare_controller.rb | 3 ++- app/models/merge_request.rb | 2 +- app/models/merge_request_diff.rb | 16 +++++++++++- app/services/compare_service.rb | 4 +-- .../projects/merge_requests/show/_diffs.html.haml | 2 +- doc/workflow/merge_requests.md | 12 +++++++++ doc/workflow/merge_requests/commit_compare.png | Bin 0 -> 89631 bytes doc/workflow/merge_requests/merge_request_diff.png | Bin 0 -> 120422 bytes .../merge_request_diff_without_whitespace.png | Bin 0 -> 98887 bytes lib/gitlab/compare_result.rb | 4 +-- .../projects/compare_controller_spec.rb | 16 ++++++++++++ .../projects/merge_requests_controller_spec.rb | 28 +++++++++++++++++++++ 13 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 doc/workflow/merge_requests/commit_compare.png create mode 100644 doc/workflow/merge_requests/merge_request_diff.png create mode 100644 doc/workflow/merge_requests/merge_request_diff_without_whitespace.png diff --git a/CHANGELOG b/CHANGELOG index 62def42d959..b8fe3b1c2f6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -36,6 +36,7 @@ v 8.2.0 (unreleased) - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - Make color of "Accept Merge Request" button consistent with current build status + - Add ignore white space option in merge request diff and commit and compare view v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 71aaad1fad6..55134e11d15 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -12,9 +12,10 @@ class Projects::CompareController < Projects::ApplicationController def show base_ref = Addressable::URI.unescape(params[:from]) @ref = head_ref = Addressable::URI.unescape(params[:to]) + diff_options = { ignore_whitespace_change: true } if params[:w] == '1' compare_result = CompareService.new. - execute(@project, head_ref, @project, base_ref) + execute(@project, head_ref, @project, base_ref, diff_options) if compare_result @commits = Commit.decorate(compare_result.commits, @project) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 85f37e49e62..e81d65c2330 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -40,7 +40,7 @@ class MergeRequest < ActiveRecord::Base after_create :create_merge_request_diff after_update :update_merge_request_diff - delegate :commits, :diffs, to: :merge_request_diff, prefix: nil + delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 6575d0bc81f..c499a4b5b4c 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -19,7 +19,7 @@ class MergeRequestDiff < ActiveRecord::Base # Prevent store of diff if commits amount more then 500 COMMITS_SAFE_SIZE = 500 - attr_reader :commits, :diffs + attr_reader :commits, :diffs, :diffs_no_whitespace belongs_to :merge_request @@ -47,6 +47,20 @@ class MergeRequestDiff < ActiveRecord::Base @diffs ||= (load_diffs(st_diffs) || []) end + def diffs_no_whitespace + # Get latest sha of branch from source project + source_sha = merge_request.source_project.commit(source_branch).sha + + compare_result = Gitlab::CompareResult.new( + Gitlab::Git::Compare.new( + merge_request.target_project.repository.raw_repository, + merge_request.target_branch, + source_sha, + ), { ignore_whitespace_change: true } + ) + @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs)) + end + def commits @commits ||= load_commits(st_commits || []) end diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb index bfe6a3dc4be..ec581658fc1 100644 --- a/app/services/compare_service.rb +++ b/app/services/compare_service.rb @@ -3,7 +3,7 @@ require 'securerandom' # Compare 2 branches for one repo or between repositories # and return Gitlab::CompareResult object that responds to commits and diffs class CompareService - def execute(source_project, source_branch, target_project, target_branch) + def execute(source_project, source_branch, target_project, target_branch, diff_options = {}) source_commit = source_project.commit(source_branch) return unless source_commit @@ -25,7 +25,7 @@ class CompareService target_project.repository.raw_repository, target_branch, source_sha, - ) + ), diff_options ) end end diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index 626970f39be..d9cfc3d7ae9 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -1,5 +1,5 @@ - if @merge_request_diff.collected? - = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.project + = render "projects/diffs/diffs", diffs: params[:w] == '1' ? @merge_request.diffs_no_whitespace : @merge_request.diffs, project: @merge_request.project - elsif @merge_request_diff.empty? .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} - else diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md index 751e19da7f1..6d57b5d98cd 100644 --- a/doc/workflow/merge_requests.md +++ b/doc/workflow/merge_requests.md @@ -38,3 +38,15 @@ To check out a particular merge request: ``` $ git checkout origin/merge-requests/1 ``` + +## Ignore whitespace changes in Merge Request diff view + +![MR diff](merge_requests/merge_request_diff.png) + +It you add `w=1` option to URL, you can see diff without whitespace changes. + +![MR diff without whitespace](merge_requests/merge_request_diff_without_whitespace.png) + +It is also working on commits compare view. + +![Commit Compare](merge_requests/commit_compare.png) diff --git a/doc/workflow/merge_requests/commit_compare.png b/doc/workflow/merge_requests/commit_compare.png new file mode 100644 index 00000000000..46b3a56a59b Binary files /dev/null and b/doc/workflow/merge_requests/commit_compare.png differ diff --git a/doc/workflow/merge_requests/merge_request_diff.png b/doc/workflow/merge_requests/merge_request_diff.png new file mode 100644 index 00000000000..ed08ae91bec Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff.png differ diff --git a/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png new file mode 100644 index 00000000000..67d67a64d12 Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png differ diff --git a/lib/gitlab/compare_result.rb b/lib/gitlab/compare_result.rb index d72391dade5..0d696a1ee28 100644 --- a/lib/gitlab/compare_result.rb +++ b/lib/gitlab/compare_result.rb @@ -2,8 +2,8 @@ module Gitlab class CompareResult attr_reader :commits, :diffs - def initialize(compare) - @commits, @diffs = compare.commits, compare.diffs + def initialize(compare, diff_options = {}) + @commits, @diffs = compare.commits, compare.diffs(nil, diff_options) end end end diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 2a447248b70..be19f1abc53 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -23,6 +23,22 @@ describe Projects::CompareController do expect(assigns(:commits).length).to be >= 1 end + it 'compare should show some diffs with ignore whitespace change option' do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + from: '08f22f25', + to: '66eceea0', + w: 1) + + expect(response).to be_success + expect(assigns(:diffs).length).to be >= 1 + expect(assigns(:commits).length).to be >= 1 + # without whitespace option, there are more than 2 diff_splits + diff_splits = assigns(:diffs)[0].diff.split("\n") + expect(diff_splits.length).to be <= 2 + end + describe 'non-existent refs' do it 'invalid source ref' do get(:show, diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index b8db8591709..3e5e1fa87ae 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -147,6 +147,34 @@ describe Projects::MergeRequestsController do end end + describe 'GET diffs with ignore_whitespace_change' do + def go(format: 'html') + get :diffs, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: merge_request.iid, + format: format, + w: 1 + end + + context 'as html' do + it 'renders the diff template' do + go + + expect(response).to render_template('diffs') + end + end + + context 'as json' do + it 'renders the diffs template to a string' do + go format: 'json' + + expect(response).to render_template('projects/merge_requests/show/_diffs') + expect(JSON.parse(response.body)).to have_key('html') + end + end + end + describe 'GET commits' do def go(format: 'html') get :commits, -- cgit v1.2.1 From 3c16fb93fcfb7dc50a46a39cbdd545ddfdab0bc1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 09:45:21 +0100 Subject: Move spec to proper place and fix unused variable Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/inline_diff.rb | 1 - spec/lib/gitlab/diff/inline_diff_spec.rb | 39 -------------------------------- spec/lib/gitlab/inline_diff_spec.rb | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 40 deletions(-) delete mode 100644 spec/lib/gitlab/diff/inline_diff_spec.rb create mode 100644 spec/lib/gitlab/inline_diff_spec.rb diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb index 72d0b493be0..44507bde25d 100644 --- a/lib/gitlab/inline_diff.rb +++ b/lib/gitlab/inline_diff.rb @@ -11,7 +11,6 @@ module Gitlab indexes.each do |index| first_line = diff_arr[index+1] second_line = diff_arr[index+2] - max_length = [first_line.size, second_line.size].max # Skip inline diff if empty line was replaced with content next if first_line == "-\n" diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/diff/inline_diff_spec.rb deleted file mode 100644 index 2e0a05088cc..00000000000 --- a/spec/lib/gitlab/diff/inline_diff_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe Gitlab::InlineDiff do - describe '#processing' do - let(:diff) do - < Date: Mon, 9 Nov 2015 16:48:03 +0100 Subject: Expose CI enable option in project features - Enable CI by default for all new projects --- app/controllers/projects/application_controller.rb | 2 +- app/controllers/projects_controller.rb | 3 ++- app/helpers/projects_helper.rb | 2 +- app/models/project.rb | 23 ++++++++++++++++------ app/services/git_push_service.rb | 2 +- app/services/projects/fork_service.rb | 2 +- app/views/layouts/nav/_project_settings.html.haml | 2 +- app/views/projects/edit.html.haml | 11 ++++++++++- app/views/projects/graphs/_head.html.haml | 2 +- config/gitlab.yml.example | 1 + config/initializers/1_settings.rb | 1 + doc/api/projects.md | 6 ++++++ lib/api/entities.rb | 2 +- lib/api/projects.rb | 6 ++++++ spec/factories/ci/projects.rb | 12 +++++++---- spec/models/project_spec.rb | 9 ++++++--- spec/requests/api/projects_spec.rb | 7 +++++-- spec/requests/api/services_spec.rb | 1 + spec/services/projects/create_service_spec.rb | 22 +++++++++++++++++++++ spec/services/projects/fork_service_spec.rb | 2 +- 20 files changed, 93 insertions(+), 25 deletions(-) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 519d6d6127e..d3f926b62bc 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -29,7 +29,7 @@ class Projects::ApplicationController < ApplicationController private def ci_enabled - return render_404 unless @project.gitlab_ci? + return render_404 unless @project.builds_enabled? end def ci_project diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 00d13a83ce8..30b166334a9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -213,7 +213,8 @@ class ProjectsController < ApplicationController params.require(:project).permit( :name, :path, :description, :issues_tracker, :tag_list, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, - :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar + :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, + :builds_enabled ) end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 5301c2ccf76..690ae2090db 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -117,7 +117,7 @@ module ProjectsHelper nav_tabs << :merge_requests end - if project.gitlab_ci? && can?(current_user, :read_build, project) + if project.builds_enabled? && can?(current_user, :read_build, project) nav_tabs << :builds end diff --git a/app/models/project.rb b/app/models/project.rb index bdb22e49bb5..3e72a9a46a0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -52,6 +52,7 @@ class Project < ActiveRecord::Base default_value_for :visibility_level, gitlab_config_features.visibility_level default_value_for :issues_enabled, gitlab_config_features.issues default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests + default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wall_enabled, false default_value_for :snippets_enabled, gitlab_config_features.snippets @@ -457,10 +458,6 @@ class Project < ActiveRecord::Base list.find { |service| service.to_param == name } end - def gitlab_ci? - gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present? - end - def ci_services services.select { |service| service.category == :ci } end @@ -782,9 +779,23 @@ class Project < ActiveRecord::Base ) end - def enable_ci + # TODO: this should be migrated to Project table, + # the same as issues_enabled + def builds_enabled + gitlab_ci_service && gitlab_ci_service.active + end + + def builds_enabled? + builds_enabled + end + + def builds_enabled=(value) service = gitlab_ci_service || create_gitlab_ci_service - service.active = true + service.active = value service.save end + + def enable_ci + self.builds_enabled = true + end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 3de7bb9dcaa..ccb6b97858c 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -60,7 +60,7 @@ class GitPushService # If CI was disabled but .gitlab-ci.yml file was pushed # we enable CI automatically - if !project.gitlab_ci? && gitlab_ci_yaml?(newrev) + if !project.builds_enabled? && gitlab_ci_yaml?(newrev) project.enable_ci end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 46374a3909a..5da1c7afd92 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -17,7 +17,7 @@ module Projects new_project = CreateService.new(current_user, new_params).execute if new_project.persisted? - if @project.gitlab_ci? + if @project.builds_enabled? new_project.enable_ci settings = @project.gitlab_ci_project.attributes.select do |attr_name, value| diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index a59939ccd31..377a99e719a 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -34,7 +34,7 @@ %span Protected Branches - - if @project.gitlab_ci? + - if @project.builds_enabled? = nav_link(controller: :runners) do = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do = icon('cog fw') diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index afbf88b5507..3ebc175648e 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -57,7 +57,16 @@ = f.check_box :merge_requests_enabled %strong Merge Requests %br - %span.descr Submit changes to be merged upstream. + %span.descr Submit changes to be merged upstream + + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :builds_enabled do + = f.check_box :builds_enabled + %strong Builds + %br + %span.descr Test and deploy your changes before merge .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index e0d06a14bf4..03d0733f913 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -3,7 +3,7 @@ = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do = link_to 'Commits', commits_namespace_project_graph_path - - if @project.gitlab_ci? + - if @project.builds_enabled? = nav_link(action: :ci) do = link_to ci_namespace_project_graph_path do Continuous Integration diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 20894ebcdc9..5bd98e3e42d 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -84,6 +84,7 @@ production: &base merge_requests: true wiki: true snippets: false + builds: true ## Webhook settings # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 302124bd977..847d9f21898 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -171,6 +171,7 @@ Settings.gitlab.default_projects_features['issues'] = true if Settings.g Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? +Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil? Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil? Settings.gitlab['restricted_signup_domains'] ||= [] diff --git a/doc/api/projects.md b/doc/api/projects.md index 96485857035..755cc6525c2 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -60,6 +60,7 @@ Parameters: "path_with_namespace": "diaspora/diaspora-client", "issues_enabled": true, "merge_requests_enabled": true, + "builds_enabled": true, "wiki_enabled": true, "snippets_enabled": false, "created_at": "2013-09-30T13: 46: 02Z", @@ -101,6 +102,7 @@ Parameters: "path_with_namespace": "brightbox/puppet", "issues_enabled": true, "merge_requests_enabled": true, + "builds_enabled": true, "wiki_enabled": true, "snippets_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -191,6 +193,7 @@ Parameters: "path_with_namespace": "diaspora/diaspora-project-site", "issues_enabled": true, "merge_requests_enabled": true, + "builds_enabled": true, "wiki_enabled": true, "snippets_enabled": false, "created_at": "2013-09-30T13: 46: 02Z", @@ -312,6 +315,7 @@ Parameters: - `description` (optional) - short project description - `issues_enabled` (optional) - `merge_requests_enabled` (optional) +- `builds_enabled` (optional) - `wiki_enabled` (optional) - `snippets_enabled` (optional) - `public` (optional) - if `true` same as setting visibility_level = 20 @@ -334,6 +338,7 @@ Parameters: - `default_branch` (optional) - 'master' by default - `issues_enabled` (optional) - `merge_requests_enabled` (optional) +- `builds_enabled` (optional) - `wiki_enabled` (optional) - `snippets_enabled` (optional) - `public` (optional) - if `true` same as setting visibility_level = 20 @@ -357,6 +362,7 @@ Parameters: - `default_branch` (optional) - `issues_enabled` (optional) - `merge_requests_enabled` (optional) +- `builds_enabled` (optional) - `wiki_enabled` (optional) - `snippets_enabled` (optional) - `public` (optional) - if `true` same as setting visibility_level = 20 diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 20cadae2291..73acf66935a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -62,7 +62,7 @@ module API expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :name, :name_with_namespace expose :path, :path_with_namespace - expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at + expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_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? } diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 67ee66a2058..2b4ada6e2eb 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -75,6 +75,7 @@ module API # description (optional) - short project description # issues_enabled (optional) # merge_requests_enabled (optional) + # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) # namespace_id (optional) - defaults to user namespace @@ -90,6 +91,7 @@ module API :description, :issues_enabled, :merge_requests_enabled, + :builds_enabled, :wiki_enabled, :snippets_enabled, :namespace_id, @@ -117,6 +119,7 @@ module API # default_branch (optional) - 'master' by default # issues_enabled (optional) # merge_requests_enabled (optional) + # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 @@ -132,6 +135,7 @@ module API :default_branch, :issues_enabled, :merge_requests_enabled, + :builds_enabled, :wiki_enabled, :snippets_enabled, :public, @@ -172,6 +176,7 @@ module API # description (optional) - short project description # issues_enabled (optional) # merge_requests_enabled (optional) + # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 @@ -185,6 +190,7 @@ module API :default_branch, :issues_enabled, :merge_requests_enabled, + :builds_enabled, :wiki_enabled, :snippets_enabled, :public, diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb index 1183a190353..11cb8c9eeaa 100644 --- a/spec/factories/ci/projects.rb +++ b/spec/factories/ci/projects.rb @@ -31,16 +31,20 @@ FactoryGirl.define do factory :ci_project_without_token, class: Ci::Project do default_ref 'master' - gl_project factory: :empty_project - shared_runners_enabled false factory :ci_project do token 'iPWx6WM4lhHNedGfBpPJNP' end - factory :ci_public_project do - public true + initialize_with do + # TODO: + # this is required, because builds_enabled is initialized when Project is created + # and this create gitlab_ci_project if builds is set to true + # here we take created gitlab_ci_project and update it's attributes + ci_project = create(:empty_project).ensure_gitlab_ci_project + ci_project.update_attributes(attributes) + ci_project end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f93935ebe3b..8d7e6e76766 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -415,12 +415,15 @@ describe Project do it { expect(project.ci_commit(commit.sha)).to eq(commit) } end - describe :enable_ci do + describe :builds_enabled do let(:project) { create :project } - before { project.enable_ci } + before { project.builds_enabled = true } - it { expect(project.gitlab_ci?).to be_truthy } + subject { project.builds_enabled } + + it { is_expected.to eq(project.gitlab_ci_service.active) } + it { expect(project.builds_enabled?).to be_truthy } it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index e9de9e0826d..9fc294118ae 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -88,8 +88,11 @@ describe API::API, api: true do end it 'returns projects in the correct order when ci_enabled_first parameter is passed' do - [project, project2, project3].each{ |project| project.build_missing_services } - project2.gitlab_ci_service.update(active: true) + [project, project2, project3].each do |project| + project.builds_enabled = false + project.build_missing_services + end + project2.builds_enabled = true get api('/projects', user), { ci_enabled_first: 'true' } expect(response.status).to eq(200) expect(json_response).to be_an Array diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index c0226605a23..b180d2fec77 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -46,6 +46,7 @@ describe API::API, api: true do delete api("/projects/#{project.id}/services/#{dashed_service}", user) expect(response.status).to eq(200) + project.send(service_method).reload expect(project.send(service_method).activated?).to be_falsey end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 25277f07482..e81c4edb7d8 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -70,6 +70,28 @@ describe Projects::CreateService do end end + context 'builds_enabled global setting' do + let(:project) { create_project(@user, @opts) } + + subject { project.builds_enabled? } + + context 'global builds_enabled false does not enable CI by default' do + before do + @opts.merge!(builds_enabled: false) + end + + it { is_expected.to be_falsey } + end + + context 'global builds_enabled true does enable CI by default' do + before do + @opts.merge!(builds_enabled: true) + end + + it { is_expected.to be_truthy } + end + end + context 'restricted visibility level' do before do stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 65a8c81204d..e397b2b9b4a 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -46,7 +46,7 @@ describe Projects::ForkService do it "fork and enable CI for fork" do @from_project.enable_ci @to_project = fork_project(@from_project, @to_user) - expect(@to_project.gitlab_ci?).to be_truthy + expect(@to_project.builds_enabled?).to be_truthy end end end -- cgit v1.2.1 From e53a56aceae6e79412a853ac3cfd1e47570135eb Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 12 Nov 2015 16:52:22 +0100 Subject: Fix broken tests --- app/models/ci/project.rb | 24 ----- spec/models/ci/project_spec.rb | 16 +--- spec/requests/ci/api/builds_spec.rb | 4 +- spec/requests/ci/api/commits_spec.rb | 2 +- spec/requests/ci/api/projects_spec.rb | 106 +++++++-------------- spec/requests/ci/api/triggers_spec.rb | 2 +- .../ci/create_trigger_request_service_spec.rb | 2 +- 7 files changed, 43 insertions(+), 113 deletions(-) diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 4e806ca1a68..f81417ba270 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -66,30 +66,6 @@ module Ci class << self include Ci::CurrentSettings - def base_build_script - <<-eos - git submodule update --init - ls -la - eos - end - - def parse(project) - params = { - gitlab_id: project.id, - default_ref: project.default_branch || 'master', - email_add_pusher: current_application_settings.add_pusher, - email_only_broken_builds: current_application_settings.all_broken_builds, - } - - project = Ci::Project.new(params) - project.build_missing_services - project - end - - def already_added?(project) - where(gitlab_id: project.id).any? - end - def unassigned(runner) joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \ "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}"). diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 490c6a67982..4546cbfad1e 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -28,8 +28,8 @@ require 'spec_helper' describe Ci::Project do - let(:gl_project) { FactoryGirl.create :empty_project } - let(:project) { FactoryGirl.create :ci_project, gl_project: gl_project } + let(:project) { FactoryGirl.create :ci_project } + let(:gl_project) { project.gl_project } subject { project } it { is_expected.to have_many(:runner_projects) } @@ -194,18 +194,6 @@ describe Ci::Project do end end - describe 'Project.parse' do - let(:project) { FactoryGirl.create :project } - - subject { Ci::Project.parse(project) } - - it { is_expected.to be_valid } - it { is_expected.to be_kind_of(Ci::Project) } - it { expect(subject.name).to eq(project.name_with_namespace) } - it { expect(subject.gitlab_id).to eq(project.id) } - it { expect(subject.gitlab_url).to eq(project.web_url) } - end - describe :repo_url_with_auth do let(:project) { FactoryGirl.create :ci_project } subject { project.repo_url_with_auth } diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 7886a6feca2..392dc2021fc 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -5,7 +5,7 @@ describe Ci::API::API do let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:gl_project) { project.gl_project } before do stub_ci_commit_to_return_yaml_file @@ -14,7 +14,7 @@ describe Ci::API::API do describe "Builds API for runners" do let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } - let(:shared_gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: shared_project) } + let(:shared_gl_project) { shared_project.gl_project } before do FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb index 6049135fd10..aa51ba95bca 100644 --- a/spec/requests/ci/api/commits_spec.rb +++ b/spec/requests/ci/api/commits_spec.rb @@ -4,7 +4,7 @@ describe Ci::API::API, 'Commits' do include ApiHelpers let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) } + let(:gl_project) { project.gl_project } let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } let(:options) do diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 53f7f91cc1f..893fd168d3e 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -41,8 +41,8 @@ describe Ci::API::API do describe "GET /projects/owned" do let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)} let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)} - let!(:project1) { FactoryGirl.create(:ci_project, gl_project: gl_project1) } - let!(:project2) { FactoryGirl.create(:ci_project, gl_project: gl_project2) } + let!(:project1) { gl_project1.ensure_gitlab_ci_project } + let!(:project2) { gl_project2.ensure_gitlab_ci_project } before do project1.gl_project.team << [user, :developer] @@ -180,87 +180,53 @@ describe Ci::API::API do end end - describe "POST /projects" do - let(:gl_project) { FactoryGirl.create :empty_project } - let(:project_info) do - { - gitlab_id: gl_project.id - } - end - - let(:invalid_project_info) { {} } + describe "POST /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:ci_project) } + let(:runner) { FactoryGirl.create(:ci_runner) } - context "with valid project info" do - before do - options.merge!(project_info) - end + it "should add the project to the runner" do + project.gl_project.team << [user, :master] + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + expect(response.status).to eq(201) - it "should create a project with valid data" do - post ci_api("/projects"), options - expect(response.status).to eq(201) - expect(json_response['name']).to eq(gl_project.name_with_namespace) - end + project.reload + expect(project.runners.first.id).to eq(runner.id) end - context "with invalid project info" do - before do - options.merge!(invalid_project_info) - end + it "should fail if it tries to link a non-existing project or runner" do + post ci_api("/projects/#{project.id}/runners/non-existing"), options + expect(response.status).to eq(404) - it "should error with invalid data" do - post ci_api("/projects"), options - expect(response.status).to eq(400) - end + post ci_api("/projects/non-existing/runners/#{runner.id}"), options + expect(response.status).to eq(404) end - describe "POST /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } - - it "should add the project to the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(201) - - project.reload - expect(project.runners.first.id).to eq(runner.id) - end - - it "should fail if it tries to link a non-existing project or runner" do - post ci_api("/projects/#{project.id}/runners/non-existing"), options - expect(response.status).to eq(404) - - post ci_api("/projects/non-existing/runners/#{runner.id}"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end + it "non-manager is not authorized" do + allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + expect(response.status).to eq(401) end + end - describe "DELETE /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } + describe "DELETE /projects/:id/runners/:id" do + let(:project) { FactoryGirl.create(:ci_project) } + let(:runner) { FactoryGirl.create(:ci_runner) } - it "should remove the project from the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + it "should remove the project from the runner" do + project.gl_project.team << [user, :master] + post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(project.runners).to be_present - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(200) + expect(project.runners).to be_present + delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + expect(response.status).to eq(200) - project.reload - expect(project.runners).to be_empty - end + project.reload + expect(project.runners).to be_empty + end - it "non-manager is not authorized" do - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end + it "non-manager is not authorized" do + delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options + expect(response.status).to eq(401) end end end diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index 93617fc4b3f..a2b436d5811 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -6,7 +6,7 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } let!(:gl_project) { FactoryGirl.create(:project) } - let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) } + let!(:project) { gl_project.ensure_gitlab_ci_project } let!(:project2) { FactoryGirl.create(:ci_project) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index fcafae38644..2ef4bb50a57 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService do let(:service) { Ci::CreateTriggerRequestService.new } let(:gl_project) { create(:project) } - let(:project) { create(:ci_project, gl_project: gl_project) } + let(:project) { gl_project.ensure_gitlab_ci_project } let(:trigger) { create(:ci_trigger, project: project) } before do -- cgit v1.2.1 From f36698663cc3a87cc5deec07c693020d97e174f7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 13 Nov 2015 11:15:17 +0100 Subject: Bump the GITLAB_WORKHORSE version to 0.4.1 [ci skip] --- CHANGELOG | 1 + GITLAB_WORKHORSE | 1 + 2 files changed, 2 insertions(+) create mode 100644 GITLAB_WORKHORSE diff --git a/CHANGELOG b/CHANGELOG index c766e8ea98a..1bd60728e6f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ v 8.2.0 (unreleased) - Send build name and stage in CI notification e-mail - Extend yml syntax for only and except to support specifying repository path - Enable shared runners to all new projects + - Bump GitLab-Workhorse to 0.4.1 - Allow to define cache in `.gitlab-ci.yml` - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page diff --git a/GITLAB_WORKHORSE b/GITLAB_WORKHORSE new file mode 100644 index 00000000000..267577d47e4 --- /dev/null +++ b/GITLAB_WORKHORSE @@ -0,0 +1 @@ +0.4.1 -- cgit v1.2.1 From fea2f214370420cdb86336881bfbcb24a994f6c6 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 13 Nov 2015 14:41:19 +0100 Subject: Fix caching breaking test of build artifacts --- spec/requests/ci/api/builds_spec.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 392dc2021fc..c2be045099d 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -160,15 +160,13 @@ describe Ci::API::API do end it "using token as parameter" do - settings = Gitlab::CurrentSettings::current_application_settings - settings.update_attributes(max_artifacts_size: 0) + stub_application_setting(max_artifacts_size: 0) post authorize_url, { token: build.project.token, filesize: 100 }, headers expect(response.status).to eq(413) end it "using token as header" do - settings = Gitlab::CurrentSettings::current_application_settings - settings.update_attributes(max_artifacts_size: 0) + stub_application_setting(max_artifacts_size: 0) post authorize_url, { filesize: 100 }, headers_with_token expect(response.status).to eq(413) end @@ -220,8 +218,7 @@ describe Ci::API::API do end it do - settings = Gitlab::CurrentSettings::current_application_settings - settings.update_attributes(max_artifacts_size: 0) + stub_application_setting(max_artifacts_size: 0) upload_artifacts(file_upload, headers_with_token) expect(response.status).to eq(413) end -- cgit v1.2.1 From 2c13723150b9095bb5b1482c62bab7817fd9a49e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 13 Nov 2015 12:58:43 -0500 Subject: Update doc/install/installation.md for 8-2-stable [ci skip] --- doc/install/installation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 8028e51dbcd..698f54cabc7 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -211,9 +211,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-1-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-2-stable gitlab -**Note:** You can change `8-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It -- cgit v1.2.1 From b8ab012544e584210b8b41219f85babdab091f59 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 13 Nov 2015 12:59:03 -0500 Subject: Update doc/install/installation.md for gitlab-workhorse [ci skip] --- doc/install/installation.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 698f54cabc7..1479bdcb76c 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -128,11 +128,10 @@ Install the Bundler Gem: ## 3. Go -Since GitLab 8.0, Git HTTP requests are handled by gitlab-git-http-server. -This is a small daemon written in Go. -To install gitlab-git-http-server we need a Go compiler. -The instructions below assume you use 64-bit Linux. You can find -downloads for other platforms at the [Go download +Since GitLab 8.0, Git HTTP requests are handled by gitlab-workhorse (formerly +gitlab-git-http-server). This is a small daemon written in Go. To install +gitlab-workhorse we need a Go compiler. The instructions below assume you +use 64-bit Linux. You can find downloads for other platforms at the [Go download page](https://golang.org/dl). curl -O --progress https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz @@ -323,16 +322,16 @@ GitLab Shell is an SSH access and repository management software developed speci **Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)". -### Install gitlab-git-http-server +### Install gitlab-workhorse cd /home/git - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git - cd gitlab-git-http-server - sudo -u git -H git checkout 0.3.0 + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git + cd gitlab-workhorse + sudo -u git -H git checkout 0.4.1 sudo -u git -H make ### Initialize Database and Activate Advanced Features - + # Go to Gitlab installation folder cd /home/git/gitlab -- cgit v1.2.1 From a060a8dad5b44d3da20485123937ddf89169f8c7 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 13 Nov 2015 13:06:52 -0500 Subject: Update doc/update/8.1-to-8.2.md [ci skip] --- doc/update/8.1-to-8.2.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 3772f624e98..73d899f9c2e 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -2,7 +2,8 @@ **NOTE:** GitLab 8.0 introduced several significant changes related to installation and configuration which *are not duplicated here*. Be sure you're -already running a working version of 8.0 before proceeding with this guide. +already running a working version of at least 8.0 before proceeding with this +guide. ### 0. Double-check your Git version @@ -67,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.5 +sudo -u git -H git checkout v2.6.6 ``` ### 5. Replace gitlab-git-http-server with gitlab-workhorse @@ -80,7 +81,7 @@ from GitLab 8.1. cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse -sudo -u git -H git checkout 0.3.1 +sudo -u git -H git checkout 0.4.1 sudo -u git -H make ``` @@ -165,12 +166,12 @@ To make sure you didn't miss anything run a more thorough check: If all items are green, then congratulations, the upgrade is complete! -## Things went south? Revert to previous version (8.0) +## Things went south? Revert to previous version (8.1) ### 1. Revert the code to the previous version -Follow the [upgrade guide from 7.14 to 8.0](7.14-to-8.0.md), except for the database migration -(The backup is already migrated to the previous version) +Follow the [upgrade guide from 8.0 to 8.1](8.0-to-8.1.md), except for the +database migration (the backup is already migrated to the previous version). ### 2. Restore from the backup -- cgit v1.2.1 From 47b4b91ecd26066a04cc4b506dd62ccaed67bbdf Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 13 Nov 2015 13:10:34 -0500 Subject: Reorder monthly release tasks for RC1 day It didn't make sense to merge CE master into EE master, and *then* do the RC1 tasks (e.g., install/update docs) because then the RC1 won't have the updated docs. --- doc/release/monthly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 4925816daaa..d347f58ba0f 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -37,9 +37,9 @@ template are explained below: ### Xth: (6 working days before the 22nd) -- [ ] Merge CE `master` into EE `master` via merge request (#LINK) - [ ] Determine QA person and notify this person - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary +- [ ] Merge CE `master` into EE `master` via merge request (#LINK) - [ ] Create CE and EE RC1 versions (#LINK) - [ ] Build RC1 packages -- cgit v1.2.1 From a237999f000526b3db5b0b5a72a665adcff29522 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 19:22:46 +0100 Subject: Annotate models Signed-off-by: Dmitriy Zaporozhets --- app/models/application_setting.rb | 4 +++ app/models/ci/application_setting.rb | 2 +- app/models/ci/build.rb | 14 +++++++-- app/models/ci/commit.rb | 25 ++++++++-------- app/models/ci/event.rb | 2 +- app/models/ci/project.rb | 4 +-- app/models/ci/runner.rb | 2 +- app/models/ci/runner_project.rb | 2 +- app/models/ci/service.rb | 2 +- app/models/ci/trigger.rb | 2 +- app/models/ci/trigger_request.rb | 2 +- app/models/ci/variable.rb | 2 +- app/models/ci/web_hook.rb | 2 +- app/models/commit_status.rb | 33 ++++++++++++++++++++++ app/models/generic_commit_status.rb | 33 ++++++++++++++++++++++ app/models/group.rb | 1 + app/models/hooks/project_hook.rb | 25 ++++++++-------- app/models/hooks/service_hook.rb | 25 ++++++++-------- app/models/hooks/system_hook.rb | 25 ++++++++-------- app/models/hooks/web_hook.rb | 25 ++++++++-------- app/models/label.rb | 1 + app/models/merge_request.rb | 1 + app/models/namespace.rb | 1 + 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_service.rb | 2 +- app/models/release.rb | 12 ++++++++ app/models/user.rb | 1 + spec/factories/labels.rb | 1 + spec/factories/merge_requests.rb | 1 + spec/factories/releases.rb | 12 ++++++++ spec/models/application_setting_spec.rb | 4 +++ spec/models/ci/commit_spec.rb | 25 ++++++++-------- spec/models/ci/project_spec.rb | 4 +-- spec/models/ci/runner_project_spec.rb | 2 +- spec/models/ci/runner_spec.rb | 2 +- spec/models/ci/service_spec.rb | 2 +- spec/models/ci/trigger_spec.rb | 12 ++++++++ spec/models/ci/variable_spec.rb | 2 +- spec/models/ci/web_hook_spec.rb | 2 +- spec/models/commit_status_spec.rb | 33 ++++++++++++++++++++++ spec/models/generic_commit_status_spec.rb | 33 ++++++++++++++++++++++ spec/models/group_spec.rb | 1 + spec/models/label_spec.rb | 1 + spec/models/merge_request_spec.rb | 1 + spec/models/namespace_spec.rb | 1 + spec/models/release_spec.rb | 12 ++++++++ spec/models/user_spec.rb | 2 ++ 48 files changed, 311 insertions(+), 96 deletions(-) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 6d2711268e1..9e70247ef51 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -23,6 +23,10 @@ # after_sign_out_path :string(255) # session_expire_delay :integer default(10080), not null # import_sources :text +# help_page_text :text +# admin_notification_email :string(255) +# shared_runners_enabled :boolean default(TRUE), not null +# max_artifacts_size :integer default(100), not null # class ApplicationSetting < ActiveRecord::Base diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 4ab3e2dcbb3..1307fa0b472 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: application_settings +# Table name: ci_application_settings # # id :integer not null, primary key # all_broken_builds :boolean diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 0ec7e210321..e78b154084b 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: builds +# Table name: ci_builds # # id :integer not null, primary key # project_id :integer @@ -11,16 +11,24 @@ # updated_at :datetime # started_at :datetime # runner_id :integer -# commit_id :integer # coverage :float +# commit_id :integer # commands :text # job_id :integer # name :string(255) +# deploy :boolean default(FALSE) # options :text # allow_failure :boolean default(FALSE), not null # stage :string(255) -# deploy :boolean default(FALSE) # trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text # module Ci diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index e58420d82d4..33b57173928 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -1,18 +1,19 @@ # == Schema Information # -# Table name: commits +# Table name: ci_commits # -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# gl_project_id :integer # module Ci diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb index cac3a7a49c1..8c39be42677 100644 --- a/app/models/ci/event.rb +++ b/app/models/ci/event.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: events +# Table name: ci_events # # id :integer not null, primary key # project_id :integer diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 4e806ca1a68..b3e05e42e9a 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -1,9 +1,9 @@ # == Schema Information # -# Table name: projects +# Table name: ci_projects # # id :integer not null, primary key -# name :string(255) not null +# name :string(255) # timeout :integer default(3600), not null # created_at :datetime # updated_at :datetime diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index b719ad3c87e..89710485811 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runners +# Table name: ci_runners # # id :integer not null, primary key # token :string(255) diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 44453ee4b41..3f4fc43873e 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runner_projects +# Table name: ci_runner_projects # # id :integer not null, primary key # runner_id :integer not null diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb index ed5e3f940b6..8063c51e82b 100644 --- a/app/models/ci/service.rb +++ b/app/models/ci/service.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index fe224b7dc70..b73c35d5ae5 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: triggers +# Table name: ci_triggers # # id :integer not null, primary key # token :string(255) diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 29cd9553394..9973d2e5ade 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: trigger_requests +# Table name: ci_trigger_requests # # id :integer not null, primary key # trigger_id :integer not null diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 7a542802fa6..b3d2b809e03 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: variables +# Table name: ci_variables # # id :integer not null, primary key # project_id :integer not null diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb index 8f03b0625da..7ca16a1bde8 100644 --- a/app/models/ci/web_hook.rb +++ b/app/models/ci/web_hook.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: web_hooks +# Table name: ci_web_hooks # # id :integer not null, primary key # url :string(255) not null diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index d346c5d35d2..e70f4d37184 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,3 +1,36 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + class CommitStatus < ActiveRecord::Base self.table_name = 'ci_builds' diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index fa54e3540d0..12c934e2494 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -1,3 +1,36 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + class GenericCommitStatus < CommitStatus before_validation :set_default_values diff --git a/app/models/group.rb b/app/models/group.rb index 34904af3b5b..793a3b5ef2e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # require 'carrierwave/orm/activerecord' diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index ca7066b959a..337b3097126 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -2,18 +2,19 @@ # # Table name: web_hooks # -# id :integer not null, primary key -# url :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null +# id :integer not null, primary key +# url :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# type :string(255) default("ProjectHook") +# service_id :integer +# push_events :boolean default(TRUE), not null +# issues_events :boolean default(FALSE), not null +# merge_requests_events :boolean default(FALSE), not null +# tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null +# enable_ssl_verification :boolean default(TRUE) # class ProjectHook < WebHook diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index b55e217975f..09bb3ee52a2 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -2,18 +2,19 @@ # # Table name: web_hooks # -# id :integer not null, primary key -# url :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null +# id :integer not null, primary key +# url :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# type :string(255) default("ProjectHook") +# service_id :integer +# push_events :boolean default(TRUE), not null +# issues_events :boolean default(FALSE), not null +# merge_requests_events :boolean default(FALSE), not null +# tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null +# enable_ssl_verification :boolean default(TRUE) # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 6fb2d421026..2f63c59b07e 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -2,18 +2,19 @@ # # Table name: web_hooks # -# id :integer not null, primary key -# url :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null +# id :integer not null, primary key +# url :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# type :string(255) default("ProjectHook") +# service_id :integer +# push_events :boolean default(TRUE), not null +# issues_events :boolean default(FALSE), not null +# merge_requests_events :boolean default(FALSE), not null +# tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null +# enable_ssl_verification :boolean default(TRUE) # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index a078accbdbd..d6c6f415c4a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -2,18 +2,19 @@ # # Table name: web_hooks # -# id :integer not null, primary key -# url :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) default("ProjectHook") -# service_id :integer -# push_events :boolean default(TRUE), not null -# issues_events :boolean default(FALSE), not null -# merge_requests_events :boolean default(FALSE), not null -# tag_push_events :boolean default(FALSE) -# note_events :boolean default(FALSE), not null +# id :integer not null, primary key +# url :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# type :string(255) default("ProjectHook") +# service_id :integer +# push_events :boolean default(TRUE), not null +# issues_events :boolean default(FALSE), not null +# merge_requests_events :boolean default(FALSE), not null +# tag_push_events :boolean default(FALSE) +# note_events :boolean default(FALSE), not null +# enable_ssl_verification :boolean default(TRUE) # class WebHook < ActiveRecord::Base diff --git a/app/models/label.rb b/app/models/label.rb index 1bb4b5f55cf..b306aecbac1 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -8,6 +8,7 @@ # project_id :integer # created_at :datetime # updated_at :datetime +# template :boolean default(FALSE) # class Label < ActiveRecord::Base diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index e81d65c2330..2eb03b8ba5b 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -20,6 +20,7 @@ # position :integer default(0) # locked_at :datetime # updated_by_id :integer +# merge_error :string(255) # require Rails.root.join("app/models/commit") diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 5782e649f8b..20b92e68d61 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # class Namespace < ActiveRecord::Base diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb index f17993d9f3b..0df03890efb 100644 --- a/app/models/project_services/ci/hip_chat_service.rb +++ b/app/models/project_services/ci/hip_chat_service.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index fd193301001..d31dd6899c1 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb index ee8e4988826..7064bfe78db 100644 --- a/app/models/project_services/ci/slack_service.rb +++ b/app/models/project_services/ci/slack_service.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) diff --git a/app/models/release.rb b/app/models/release.rb index e196b84eb18..89f70278af5 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: releases +# +# id :integer not null, primary key +# tag :string(255) +# description :text +# project_id :integer +# created_at :datetime +# updated_at :datetime +# + class Release < ActiveRecord::Base belongs_to :project diff --git a/app/models/user.rb b/app/models/user.rb index 67fef1c1e6a..9ffadcf4468 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -54,6 +54,7 @@ # public_email :string(255) default(""), not null # dashboard :integer default(0) # project_view :integer default(0) +# consumed_timestep :integer # layout :integer default(0) # diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb index 6829387c660..8b12ee11af5 100644 --- a/spec/factories/labels.rb +++ b/spec/factories/labels.rb @@ -8,6 +8,7 @@ # project_id :integer # created_at :datetime # updated_at :datetime +# template :boolean default(FALSE) # # Read about factories at https://github.com/thoughtbot/factory_girl diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 6080d0ccdef..729a49c9f72 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -20,6 +20,7 @@ # position :integer default(0) # locked_at :datetime # updated_by_id :integer +# merge_error :string(255) # FactoryGirl.define do diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 80d6bbee6c7..43d09b17534 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: releases +# +# id :integer not null, primary key +# tag :string(255) +# description :text +# project_id :integer +# created_at :datetime +# updated_at :datetime +# + # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index f01fe8bd398..dfbac7b4004 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -23,6 +23,10 @@ # after_sign_out_path :string(255) # session_expire_delay :integer default(10080), not null # import_sources :text +# help_page_text :text +# admin_notification_email :string(255) +# shared_runners_enabled :boolean default(TRUE), not null +# max_artifacts_size :integer default(100), not null # require 'spec_helper' diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 44dbd083f06..a13f6458cac 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -1,18 +1,19 @@ # == Schema Information # -# Table name: commits +# Table name: ci_commits # -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 490c6a67982..a0864442d97 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -1,9 +1,9 @@ # == Schema Information # -# Table name: projects +# Table name: ci_projects # # id :integer not null, primary key -# name :string(255) not null +# name :string(255) # timeout :integer default(3600), not null # created_at :datetime # updated_at :datetime diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb index 0218d484130..37682c6ea0c 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runner_projects +# Table name: ci_runner_projects # # id :integer not null, primary key # runner_id :integer not null diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index f8a51c29dc2..9a1233b9095 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: runners +# Table name: ci_runners # # id :integer not null, primary key # token :string(255) diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 2df70e88888..36cda988eb4 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: services +# Table name: ci_services # # id :integer not null, primary key # type :string(255) diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 19c14ef2da2..b8aa3c1e777 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: ci_triggers +# +# id :integer not null, primary key +# token :string(255) +# project_id :integer not null +# deleted_at :datetime +# created_at :datetime +# updated_at :datetime +# + require 'spec_helper' describe Ci::Trigger do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index d034a6c7b9f..a515f5881ff 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: variables +# Table name: ci_variables # # id :integer not null, primary key # project_id :integer not null diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index bf9481ab81d..2865482a212 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -1,6 +1,6 @@ # == Schema Information # -# Table name: web_hooks +# Table name: ci_web_hooks # # id :integer not null, primary key # url :string(255) not null diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index c96a606fdaa..dca0715eed8 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -1,3 +1,36 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + require 'spec_helper' describe CommitStatus do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index f442fa5fbe5..c86314c454c 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -1,3 +1,36 @@ +# == Schema Information +# +# Table name: ci_builds +# +# id :integer not null, primary key +# project_id :integer +# status :string(255) +# finished_at :datetime +# trace :text +# created_at :datetime +# updated_at :datetime +# started_at :datetime +# runner_id :integer +# coverage :float +# commit_id :integer +# commands :text +# job_id :integer +# name :string(255) +# deploy :boolean default(FALSE) +# options :text +# allow_failure :boolean default(FALSE), not null +# stage :string(255) +# trigger_request_id :integer +# stage_idx :integer +# tag :boolean +# ref :string(255) +# user_id :integer +# type :string(255) +# target_url :string(255) +# description :string(255) +# artifacts_file :text +# + require 'spec_helper' describe GenericCommitStatus do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 0f23e81ace9..bbfc5535eec 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 6518213d71c..511ee8cbd96 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -8,6 +8,7 @@ # project_id :integer # created_at :datetime # updated_at :datetime +# template :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index eed2cbc5412..90af75ff0e3 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -20,6 +20,7 @@ # position :integer default(0) # locked_at :datetime # updated_by_id :integer +# merge_error :string(255) # require 'spec_helper' diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 1d72a9503ae..a98b9cb7321 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -11,6 +11,7 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) +# public :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 527005b2b69..72ecb442a36 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: releases +# +# id :integer not null, primary key +# tag :string(255) +# description :text +# project_id :integer +# created_at :datetime +# updated_at :datetime +# + require 'rails_helper' RSpec.describe Release, type: :model do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 49e0bfdd2ec..7d716c23120 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -54,6 +54,8 @@ # public_email :string(255) default(""), not null # dashboard :integer default(0) # project_view :integer default(0) +# consumed_timestep :integer +# layout :integer default(0) # require 'spec_helper' -- cgit v1.2.1 From 4bb99677b1ddee84ab0433efe081de8025e5aa53 Mon Sep 17 00:00:00 2001 From: Alec Cooper Date: Thu, 12 Nov 2015 21:38:26 -0500 Subject: Relative links in the README file shown on the repository homepage should point to the default branch, not to master --- CHANGELOG | 1 + lib/gitlab/markdown/relative_link_filter.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1bd60728e6f..5d22e950914 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -39,6 +39,7 @@ v 8.2.0 (unreleased) - Improve Continuous Integration graphs page - Make color of "Accept Merge Request" button consistent with current build status - Add ignore white space option in merge request diff and commit and compare view + - Relative links from a repositories README.md now link to the default branch v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index 6ee3d1ce039..632be4d7542 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -51,7 +51,7 @@ module Gitlab relative_url_root, context[:project].path_with_namespace, path_type(file_path), - ref || 'master', # assume that if no ref exists we can point to master + ref || context[:project].default_branch, # if no ref exists, point to the default branch file_path ].compact.join('/').squeeze('/').chomp('/') -- cgit v1.2.1 From 9337da7864756116f3865e14d46227ffe857d0e5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 15 Nov 2015 21:37:13 -0500 Subject: Revert "Merge pull request #9812 from chrspeich/hide-tabs-lone-auth-provider" This reverts commit 84999611d8f7894219eb4ebc76555c79b1f14794, reversing changes made to 0d9fb211f3f842d10e1c57dcb9d3d42a9c11cd0b. --- app/views/devise/shared/_signin_box.html.haml | 46 +++++++++++---------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index 9a1331b2549..41ad2c231d4 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -7,34 +7,26 @@ %h3 Sign in .login-body - if form_based_providers.any? - - if form_based_providers.count >= 2 || signin_enabled? - %ul.nav.nav-tabs - - if crowd_enabled? - %li.active - = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab' - - @ldap_servers.each_with_index do |server, i| - %li{class: (:active if i.zero? && !crowd_enabled?)} - = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab' - - if signin_enabled? - %li - = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' - .tab-content - - if crowd_enabled? - %div.tab-pane.active{id: "tab-crowd"} - = render 'devise/sessions/new_crowd' - - @ldap_servers.each_with_index do |server, i| - %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)} - = render 'devise/sessions/new_ldap', server: server - - if signin_enabled? - %div#tab-signin.tab-pane - = render 'devise/sessions/new_base' - - else + %ul.nav.nav-tabs - if crowd_enabled? - = render 'devise/sessions/new_crowd' - - elsif @ldap_servers.any? - = render 'devise/sessions/new_ldap', server: @ldap_servers.first - - elsif signin_enabled? - = render 'devise/sessions/new_base' + %li.active + = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab' + - @ldap_servers.each_with_index do |server, i| + %li{class: (:active if i.zero? && !crowd_enabled?)} + = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab' + - if signin_enabled? + %li + = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab' + .tab-content + - if crowd_enabled? + %div.tab-pane.active{id: "tab-crowd"} + = render 'devise/sessions/new_crowd' + - @ldap_servers.each_with_index do |server, i| + %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)} + = render 'devise/sessions/new_ldap', server: server + - if signin_enabled? + %div#tab-signin.tab-pane + = render 'devise/sessions/new_base' - elsif signin_enabled? = render 'devise/sessions/new_base' -- cgit v1.2.1 From 743d66e4da5c11f638d12a7f7ec6354631ee2b90 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 10:11:16 +0100 Subject: Improve english text Signed-off-by: Dmitriy Zaporozhets --- doc/api/tags.md | 2 +- doc/workflow/releases.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index 0113d4ba049..b5b90cf6b82 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -40,7 +40,7 @@ Parameters: ## Create a new tag -Creates new tag in the repository that points to the supplied ref. +Creates a new tag in the repository that points to the supplied ref. ``` POST /projects/:id/repository/tags diff --git a/doc/workflow/releases.md b/doc/workflow/releases.md index 0eca220e2cf..6176784fc57 100644 --- a/doc/workflow/releases.md +++ b/doc/workflow/releases.md @@ -1,14 +1,14 @@ # Releases -You can turn any git tag into release by just adding a release notes to it. -Release notes behaves like any other markdown form in GitLab so you can write text and drag-n-drop files to it. -Release notes are stored in GitLab database. +You can turn any git tag into a release, by adding a note to it. +Release notes behave like any other markdown form in GitLab so you can write text and drag-n-drop files to it. +Release notes are stored in the database of GitLab. There are several ways to add release notes: -* In UI when you create new git tag with GitLab -* In UI when you add release notes to existing git tag -* with GitLab API +* In the interface, when you create a new git tag with GitLab +* In the interface, by adding a note to an existing git tag +* with the GitLab API ## New tag page with release notes text area -- cgit v1.2.1 From 9e0d443f9e420a347cb88508c9afebf17454fac1 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 16 Nov 2015 12:18:31 +0200 Subject: Disabling cache for test environment --- config/environments/test.rb | 2 ++ config/initializers/session_store.rb | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/config/environments/test.rb b/config/environments/test.rb index 2d5e7addcd3..955540837d3 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -30,4 +30,6 @@ Gitlab::Application.configure do config.active_support.deprecation = :stderr config.eager_load = false + + config.cache_store = :null_store end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 04ed9e90df5..d7c5432da76 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -9,12 +9,14 @@ begin rescue end -Gitlab::Application.config.session_store( - :redis_store, # Using the cookie_store would enable session replay attacks. - servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store - key: '_gitlab_session', - secure: Gitlab.config.gitlab.https, - httponly: true, - expire_after: Settings.gitlab['session_expire_delay'] * 60, - path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root -) +unless Rails.env.test? + Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store + key: '_gitlab_session', + secure: Gitlab.config.gitlab.https, + httponly: true, + expire_after: Settings.gitlab['session_expire_delay'] * 60, + path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root + ) +end -- cgit v1.2.1 From 14032d8eb1a60ae5920286249c1044be2fa27278 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 12 Oct 2015 16:42:14 +0200 Subject: Add support for git lfs. --- .gitignore | 1 + app/controllers/projects_controller.rb | 5 +- app/models/lfs_object.rb | 8 + app/models/lfs_objects_project.rb | 8 + app/models/project.rb | 12 + app/uploaders/lfs_object_uploader.rb | 29 + config/gitlab.yml.example | 10 +- config/initializers/1_settings.rb | 7 + config/routes.rb | 2 +- db/migrate/20151103134857_create_lfs_objects.rb | 10 + .../20151103134958_create_lfs_objects_projects.rb | 12 + .../20151104105513_add_file_to_lfs_objects.rb | 5 + ...0151114113410_add_index_for_lfs_oid_and_size.rb | 6 + db/schema.rb | 22 +- lib/gitlab/backend/grack_auth.rb | 5 +- lib/gitlab/git_access.rb | 6 +- lib/gitlab/lfs/response.rb | 308 ++++++++++ lib/gitlab/lfs/router.rb | 95 +++ lib/support/nginx/gitlab | 9 +- lib/support/nginx/gitlab-ssl | 9 +- spec/factories/lfs_objects.rb | 12 + spec/factories/lfs_objects_projects.rb | 8 + spec/lib/gitlab/lfs/lfs_router_spec.rb | 650 +++++++++++++++++++++ 23 files changed, 1226 insertions(+), 13 deletions(-) create mode 100644 app/models/lfs_object.rb create mode 100644 app/models/lfs_objects_project.rb create mode 100644 app/uploaders/lfs_object_uploader.rb create mode 100644 db/migrate/20151103134857_create_lfs_objects.rb create mode 100644 db/migrate/20151103134958_create_lfs_objects_projects.rb create mode 100644 db/migrate/20151104105513_add_file_to_lfs_objects.rb create mode 100644 db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb create mode 100644 lib/gitlab/lfs/response.rb create mode 100644 lib/gitlab/lfs/router.rb create mode 100644 spec/factories/lfs_objects.rb create mode 100644 spec/factories/lfs_objects_projects.rb create mode 100644 spec/lib/gitlab/lfs/lfs_router_spec.rb diff --git a/.gitignore b/.gitignore index 39ff95c50ee..f5b6427ca03 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ rails_best_practices_output.html tmp/ vendor/bundle/* builds/* +shared/* diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 30b166334a9..23453195e85 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -72,8 +72,7 @@ class ProjectsController < ApplicationController def remove_fork return access_denied! unless can?(current_user, :remove_fork_project, @project) - if @project.forked? - @project.forked_project_link.destroy + if @project.unlink_fork flash[:notice] = 'The fork relationship has been removed.' end end @@ -243,7 +242,7 @@ class ProjectsController < ApplicationController project.repository_exists? && !project.empty_repo? end - # Override get_id from ExtractsPath, which returns the branch and file path + # Override get_id from ExtractsPath, which returns the branch and file path # for the blob/tree, which in this case is just the root of the default branch. def get_id project.repository.root_ref diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb new file mode 100644 index 00000000000..3c1426f59d0 --- /dev/null +++ b/app/models/lfs_object.rb @@ -0,0 +1,8 @@ +class LfsObject < ActiveRecord::Base + has_many :lfs_objects_projects, dependent: :destroy + has_many :projects, through: :lfs_objects_projects + + validates :oid, presence: true, uniqueness: true + + mount_uploader :file, LfsObjectUploader +end diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb new file mode 100644 index 00000000000..0fd5f089db9 --- /dev/null +++ b/app/models/lfs_objects_project.rb @@ -0,0 +1,8 @@ +class LfsObjectsProject < ActiveRecord::Base + belongs_to :project + belongs_to :lfs_object + + validates :lfs_object_id, presence: true + validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" } + validates :project_id, presence: true +end diff --git a/app/models/project.rb b/app/models/project.rb index 3e72a9a46a0..9ea0d15497a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -124,6 +124,8 @@ class Project < ActiveRecord::Base has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :releases, dependent: :destroy + has_many :lfs_objects_projects, dependent: :destroy + has_many :lfs_objects, through: :lfs_objects_projects has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id @@ -798,4 +800,14 @@ class Project < ActiveRecord::Base def enable_ci self.builds_enabled = true end + + def unlink_fork + if forked? + forked_from_project.lfs_objects.find_each do |lfs_object| + lfs_object.projects << self + end + + forked_project_link.destroy + end + end end diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb new file mode 100644 index 00000000000..28085b31083 --- /dev/null +++ b/app/uploaders/lfs_object_uploader.rb @@ -0,0 +1,29 @@ +# encoding: utf-8 + +class LfsObjectUploader < CarrierWave::Uploader::Base + storage :file + + def store_dir + "#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}" + end + + def cache_dir + "#{Gitlab.config.lfs.storage_path}/tmp/cache" + end + + def move_to_cache + true + end + + def move_to_store + true + end + + def exists? + file.try(:exists?) + end + + def filename + model.oid[4..-1] + end +end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 5bd98e3e42d..8fdb2603ce8 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -124,6 +124,12 @@ production: &base # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + ## Git LFS + lfs: + enabled: false + # The location where LFS objects are stored (default: shared/lfs-objects). + # storage_path: shared/lfs-objects + ## Gravatar ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html gravatar: @@ -317,8 +323,6 @@ production: &base # path: /mnt/gitlab # Default: shared - - # # 4. Advanced settings # ========================== @@ -419,6 +423,8 @@ test: <<: *base gravatar: enabled: true + lfs: + enabled: false gitlab: host: localhost port: 80 diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 847d9f21898..6b7990c0ab0 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -199,6 +199,13 @@ Settings.incoming_email['ssl'] = false if Settings.incoming_email['ssl']. Settings.incoming_email['start_tls'] = false if Settings.incoming_email['start_tls'].nil? Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mailbox'].nil? +# +# Git LFS +# +Settings['lfs'] ||= Settingslogic.new({}) +Settings.lfs['enabled'] = false if Settings.lfs['enabled'].nil? +Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root) + # # Gravatar # diff --git a/config/routes.rb b/config/routes.rb index 095c562be8d..bd85f4e3c69 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,7 +93,7 @@ Gitlab::Application.routes.draw do end # Enable Grack support - mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post] + mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put] # Help get 'help' => 'help#index' diff --git a/db/migrate/20151103134857_create_lfs_objects.rb b/db/migrate/20151103134857_create_lfs_objects.rb new file mode 100644 index 00000000000..2d04c170a88 --- /dev/null +++ b/db/migrate/20151103134857_create_lfs_objects.rb @@ -0,0 +1,10 @@ +class CreateLfsObjects < ActiveRecord::Migration + def change + create_table :lfs_objects do |t| + t.string :oid, null: false, unique: true + t.integer :size, null: false + + t.timestamps + end + end +end diff --git a/db/migrate/20151103134958_create_lfs_objects_projects.rb b/db/migrate/20151103134958_create_lfs_objects_projects.rb new file mode 100644 index 00000000000..f3f58b931ec --- /dev/null +++ b/db/migrate/20151103134958_create_lfs_objects_projects.rb @@ -0,0 +1,12 @@ +class CreateLfsObjectsProjects < ActiveRecord::Migration + def change + create_table :lfs_objects_projects do |t| + t.integer :lfs_object_id, null: false + t.integer :project_id, null: false + + t.timestamps + end + + add_index :lfs_objects_projects, :project_id + end +end diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb new file mode 100644 index 00000000000..7c57f3f0df6 --- /dev/null +++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb @@ -0,0 +1,5 @@ +class AddFileToLfsObjects < ActiveRecord::Migration + def change + add_column :lfs_objects, :file, :string + end +end diff --git a/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb new file mode 100644 index 00000000000..d10f1f6e605 --- /dev/null +++ b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb @@ -0,0 +1,6 @@ +class AddIndexForLfsOidAndSize < ActiveRecord::Migration + def change + add_index :lfs_objects, :oid + add_index :lfs_objects, [:oid, :size] + end +end diff --git a/db/schema.rb b/db/schema.rb index f631d73f334..a8e8dfe6bbf 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: 20151109100728) do +ActiveRecord::Schema.define(version: 20151114113410) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -422,6 +422,26 @@ ActiveRecord::Schema.define(version: 20151109100728) do add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree + create_table "lfs_objects", force: true do |t| + t.string "oid", null: false, unique: true + t.integer "size", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.string "file" + end + + add_index "lfs_objects", ["oid", "size"], name: "index_lfs_objects_on_oid_and_size", using: :btree + add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", using: :btree + + create_table "lfs_objects_projects", force: true do |t| + t.integer "lfs_object_id", null: false + t.integer "project_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree + create_table "members", force: true do |t| t.integer "access_level", null: false t.integer "source_id", null: false diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 440ef5a3cb3..0d156047ff0 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -33,6 +33,9 @@ module Grack auth! + lfs_response = Gitlab::Lfs::Router.new(project, @user, @request).try_call + return lfs_response unless lfs_response.nil? + if project && authorized_request? # Tell gitlab-workhorse the request is OK, and what the GL_ID is render_grack_auth_ok @@ -72,7 +75,7 @@ module Grack matched_login = /(?^[a-zA-Z]*-ci)-token$/.match(login) if project && matched_login.present? && git_cmd == 'git-upload-pack' - underscored_service = matched_login['s'].underscore + underscored_service = matched_login['s'].underscore if Service.available_services_names.include?(underscored_service) service_method = "#{underscored_service}_service" diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb index c90184d31cf..3ed1eec517c 100644 --- a/lib/gitlab/git_access.rb +++ b/lib/gitlab/git_access.rb @@ -13,7 +13,7 @@ module Gitlab def user return @user if defined?(@user) - @user = + @user = case actor when User actor @@ -125,7 +125,7 @@ module Gitlab def change_access_check(change) oldrev, newrev, ref = change.split(' ') - action = + action = if project.protected_branch?(branch_name(ref)) protected_branch_action(oldrev, newrev, branch_name(ref)) elsif protected_tag?(tag_name(ref)) @@ -148,7 +148,7 @@ module Gitlab build_status_object(false, "You are not allowed to change existing tags on this project.") else # :push_code build_status_object(false, "You are not allowed to push code to this project.") - end + end return status end diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb new file mode 100644 index 00000000000..4202c786466 --- /dev/null +++ b/lib/gitlab/lfs/response.rb @@ -0,0 +1,308 @@ +module Gitlab + module Lfs + class Response + + def initialize(project, user, request) + @origin_project = project + @project = storage_project(project) + @user = user + @env = request.env + @request = request + end + + # Return a response for a download request + # Can be a response to: + # Request from a user to get the file + # Request from gitlab-workhorse which file to serve to the user + def render_download_hypermedia_response(oid) + render_response_to_download do + if check_download_accept_header? + render_lfs_download_hypermedia(oid) + else + render_not_found + end + end + end + + def render_download_object_response(oid) + render_response_to_download do + if check_download_sendfile_header? && check_download_accept_header? + render_lfs_sendfile(oid) + else + render_not_found + end + end + end + + def render_lfs_api_auth + render_response_to_push do + request_body = JSON.parse(@request.body.read) + return render_not_found if request_body.empty? || request_body['objects'].empty? + + response = build_response(request_body['objects']) + [ + 200, + { + "Content-Type" => "application/json; charset=utf-8", + "Cache-Control" => "private", + }, + [JSON.dump(response)] + ] + end + end + + def render_storage_upload_authorize_response(oid, size) + render_response_to_push do + [ + 200, + { "Content-Type" => "application/json; charset=utf-8" }, + [JSON.dump({ + 'StoreLFSPath' => "#{Gitlab.config.lfs.storage_path}/tmp/upload", + 'LfsOid' => oid, + 'LfsSize' => size + })] + ] + end + end + + def render_storage_upload_store_response(oid, size, tmp_file_name) + render_response_to_push do + render_lfs_upload_ok(oid, size, tmp_file_name) + end + end + + private + + def render_not_enabled + [ + 501, + { + "Content-Type" => "application/vnd.git-lfs+json", + }, + [JSON.dump({ + 'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.', + 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", + })] + ] + end + + def render_unauthorized + [ + 401, + { + 'Content-Type' => 'text/plain' + }, + ['Unauthorized'] + ] + end + + def render_not_found + [ + 404, + { + "Content-Type" => "application/vnd.git-lfs+json" + }, + [JSON.dump({ + 'message' => 'Not found.', + 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", + })] + ] + end + + def render_forbidden + [ + 403, + { + "Content-Type" => "application/vnd.git-lfs+json" + }, + [JSON.dump({ + 'message' => 'Access forbidden. Check your access level.', + 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", + })] + ] + end + + def render_lfs_sendfile(oid) + return render_not_found unless oid.present? + + lfs_object = object_for_download(oid) + + if lfs_object && lfs_object.file.exists? + [ + 200, + { + # GitLab-workhorse will forward Content-Type header + "Content-Type" => "application/octet-stream", + "X-Sendfile" => lfs_object.file.path + }, + [] + ] + else + render_not_found + end + end + + def render_lfs_download_hypermedia(oid) + return render_not_found unless oid.present? + + lfs_object = object_for_download(oid) + if lfs_object + [ + 200, + { "Content-Type" => "application/vnd.git-lfs+json" }, + [JSON.dump(download_hypermedia(oid))] + ] + else + render_not_found + end + end + + def render_lfs_upload_ok(oid, size, tmp_file) + if store_file(oid, size, tmp_file) + [ + 200, + { + 'Content-Type' => 'text/plain', + 'Content-Length' => 0 + }, + [] + ] + else + [ + 422, + { 'Content-Type' => 'text/plain' }, + ["Unprocessable entity"] + ] + end + end + + def render_response_to_download + return render_not_enabled unless Gitlab.config.lfs.enabled + + unless @project.public? + return render_unauthorized unless @user + return render_forbidden unless user_can_fetch? + end + + yield + end + + def render_response_to_push + return render_not_enabled unless Gitlab.config.lfs.enabled + return render_unauthorized unless @user + return render_forbidden unless user_can_push? + + yield + end + + def check_download_sendfile_header? + @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile" + end + + def check_download_accept_header? + @env['HTTP_ACCEPT'].to_s == "application/vnd.git-lfs+json; charset=utf-8" + end + + def user_can_fetch? + # Check user access against the project they used to initiate the pull + @user.can?(:download_code, @origin_project) + end + + def user_can_push? + # Check user access against the project they used to initiate the push + @user.can?(:push_code, @origin_project) + end + + def storage_project(project) + if project.forked? + project.forked_from_project + else + project + end + end + + def store_file(oid, size, tmp_file) + tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file) + + object = LfsObject.find_or_create_by(oid: oid, size: size) + if object.file.exists? + success = true + else + success = move_tmp_file_to_storage(object, tmp_file_path) + end + + if success + success = link_to_project(object) + end + + success + ensure + # Ensure that the tmp file is removed + FileUtils.rm_f(tmp_file_path) + end + + def object_for_download(oid) + @project.lfs_objects.find_by(oid: oid) + end + + def move_tmp_file_to_storage(object, path) + File.open(path) do |f| + object.file = f + end + + object.file.store! + object.save + end + + def link_to_project(object) + if object && !object.projects.exists?(@project) + object.projects << @project + object.save + end + end + + def select_existing_objects(objects) + objects_oids = objects.map { |o| o['oid'] } + @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set + end + + def build_response(objects) + selected_objects = select_existing_objects(objects) + + upload_hypermedia(objects, selected_objects) + end + + def download_hypermedia(oid) + { + '_links' => { + 'download' => + { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}", + 'header' => { + 'Accept' => "application/vnd.git-lfs+json; charset=utf-8", + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } + } + } + end + + def upload_hypermedia(all_objects, existing_objects) + all_objects.each do |object| + object['_links'] = hypermedia_links(object) unless existing_objects.include?(object['oid']) + end + + { 'objects' => all_objects } + end + + def hypermedia_links(object) + { + "upload" => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", + 'header' => { 'Authorization' => @env['HTTP_AUTHORIZATION'] } + }.compact + } + end + end + end +end diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb new file mode 100644 index 00000000000..4809e834984 --- /dev/null +++ b/lib/gitlab/lfs/router.rb @@ -0,0 +1,95 @@ +module Gitlab + module Lfs + class Router + def initialize(project, user, request) + @project = project + @user = user + @env = request.env + @request = request + end + + def try_call + return unless @request && @request.path.present? + + case @request.request_method + when 'GET' + get_response + when 'POST' + post_response + when 'PUT' + put_response + else + nil + end + end + + private + + def get_response + path_match = @request.path.match(/\/(info\/lfs|gitlab-lfs)\/objects\/([0-9a-f]{64})$/) + return nil unless path_match + + oid = path_match[2] + return nil unless oid + + case path_match[1] + when "info/lfs" + lfs.render_download_hypermedia_response(oid) + when "gitlab-lfs" + lfs.render_download_object_response(oid) + else + nil + end + end + + def post_response + post_path = @request.path.match(/\/info\/lfs\/objects(\/batch)?$/) + return nil unless post_path + + # Check for Batch API + if post_path[0].ends_with?("/info/lfs/objects/batch") + lfs.render_lfs_api_auth + else + nil + end + end + + def put_response + object_match = @request.path.match(/\/gitlab-lfs\/objects\/([0-9a-f]{64})\/([0-9]+)(|\/authorize){1}$/) + return nil if object_match.nil? + + oid = object_match[1] + size = object_match[2].try(:to_i) + return nil if oid.nil? || size.nil? + + # GitLab-workhorse requests + # 1. Try to authorize the request + # 2. send a request with a header containing the name of the temporary file + if object_match[3] && object_match[3] == '/authorize' + lfs.render_storage_upload_authorize_response(oid, size) + else + tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP']) + return nil unless tmp_file_name + + lfs.render_storage_upload_store_response(oid, size, tmp_file_name) + end + end + + def lfs + return unless @project + + Gitlab::Lfs::Response.new(@project, @user, @request) + end + + def sanitize_tmp_filename(name) + if name.present? + name.gsub!(/^.*(\\|\/)/, '') + name = name.match(/[0-9a-f]{73}/) + name[0] if name + else + nil + end + end + end + end +end diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 0a7a4118077..93f2ad07aeb 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -44,7 +44,7 @@ upstream gitlab-workhorse { ## Normal HTTP host server { - ## Either remove "default_server" from the listen line below, + ## Either remove "default_server" from the listen line below, ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab ## to be served if you visit any address that your server responds to, eg. ## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server; @@ -113,6 +113,13 @@ server { proxy_pass http://gitlab; } + location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; + } + location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index b463d5b6aa9..90749947fa4 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -48,7 +48,7 @@ upstream gitlab-workhorse { ## Redirects all HTTP traffic to the HTTPS host server { - ## Either remove "default_server" from the listen line below, + ## Either remove "default_server" from the listen line below, ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab ## to be served if you visit any address that your server responds to, eg. ## the ip address of the server (http://x.x.x.x/) @@ -160,6 +160,13 @@ server { proxy_pass http://gitlab; } + location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; + } + location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb new file mode 100644 index 00000000000..7fb2d77ca32 --- /dev/null +++ b/spec/factories/lfs_objects.rb @@ -0,0 +1,12 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :lfs_object do + oid "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" + size 499013 + end + + trait :with_file do + file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } + end +end diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb new file mode 100644 index 00000000000..93de6607df8 --- /dev/null +++ b/spec/factories/lfs_objects_projects.rb @@ -0,0 +1,8 @@ +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :lfs_objects_project do + lfs_object + project + end +end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb new file mode 100644 index 00000000000..cebcb5bc887 --- /dev/null +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -0,0 +1,650 @@ +require 'spec_helper' + +describe Gitlab::Lfs::Router do + let(:project) { create(:project) } + let(:public_project) { create(:project, :public) } + let(:forked_project) { fork_project(public_project, user) } + + let(:user) { create(:user) } + let(:user_two) { create(:user) } + let!(:lfs_object) { create(:lfs_object, :with_file) } + + let(:request) { Rack::Request.new(env) } + let(:env) do + { + 'rack.input' => '', + 'REQUEST_METHOD' => 'GET', + } + end + + let(:lfs_router_auth) { new_lfs_router(project, user) } + let(:lfs_router_noauth) { new_lfs_router(project, nil) } + let(:lfs_router_public_auth) { new_lfs_router(public_project, user) } + let(:lfs_router_public_noauth) { new_lfs_router(public_project, nil) } + let(:lfs_router_forked_noauth) { new_lfs_router(forked_project, nil) } + let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user_two) } + + let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" } + let(:sample_size) { 499013 } + + describe 'when lfs is disabled' do + before do + allow(Gitlab.config.lfs).to receive(:enabled).and_return(false) + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" + end + + it 'responds with 501' do + respond_with_disabled = [ 501, + { "Content-Type"=>"application/vnd.git-lfs+json" }, + ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"] + ] + expect(lfs_router_auth.try_call).to match_array(respond_with_disabled) + end + end + + describe 'when fetching lfs object' do + before do + enable_lfs + env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" + end + + describe 'when user is authenticated' do + context 'and user has project download access' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.lfs_objects << lfs_object + project.team << [user, :master] + end + + it "responds with status 200" do + expect(lfs_router_auth.try_call.first).to eq(200) + end + + it "responds with download hypermedia" do + json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) + + expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") + expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth, "Accept" => "application/vnd.git-lfs+json; charset=utf-8") + end + end + + context 'and user does not have project access' do + it "responds with status 403" do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + end + + describe 'when user is unauthenticated' do + context 'and user does not have download access' do + it "responds with status 401" do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + + context 'and user has download access' do + before do + project.team << [user, :master] + end + + it "responds with status 401" do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + end + + describe 'and project is public' do + context 'and project has access to the lfs object' do + before do + public_project.lfs_objects << lfs_object + end + + context 'and user is authenticated' do + it "responds with status 200 and sends download hypermedia" do + expect(lfs_router_public_auth.try_call.first).to eq(200) + json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first) + + expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") + expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + end + end + + context 'and user is unauthenticated' do + it "responds with status 200 and sends download hypermedia" do + expect(lfs_router_public_noauth.try_call.first).to eq(200) + json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first) + + expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") + expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + end + end + end + + context 'and project does not have access to the lfs object' do + it "responds with status 404" do + expect(lfs_router_public_auth.try_call.first).to eq(404) + end + end + end + + describe 'and request comes from gitlab-workhorse' do + before do + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" + end + context 'without user being authorized' do + it "responds with status 401" do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + + context 'with required headers' do + before do + env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile" + end + + context 'when user does not have project access' do + it "responds with status 403" do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + + context 'when user has project access' do + before do + project.lfs_objects << lfs_object + project.team << [user, :master] + end + + it "responds with status 200" do + expect(lfs_router_auth.try_call.first).to eq(200) + end + + it "responds with the file location" do + expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream") + expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path) + end + end + end + + context 'without required headers' do + it "responds with status 403" do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + end + + describe 'from a forked public project' do + before do + env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" + env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" + end + + context "when fetching a lfs object" do + context "and user has project download access" do + before do + public_project.lfs_objects << lfs_object + end + + it "can download the lfs object" do + expect(lfs_router_forked_auth.try_call.first).to eq(200) + json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) + + expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") + expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + end + end + + context "and user is not authenticated but project is public" do + before do + public_project.lfs_objects << lfs_object + end + + it "can download the lfs object" do + expect(lfs_router_forked_auth.try_call.first).to eq(200) + end + end + + context "and user has project download access" do + before do + env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897" + @auth = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) + env["HTTP_AUTHORIZATION"] = @auth + lfs_object_two = create(:lfs_object, :with_file, oid: "91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", size: 1575078) + public_project.lfs_objects << lfs_object_two + end + + it "can get a lfs object that is not in the forked project" do + expect(lfs_router_forked_auth.try_call.first).to eq(200) + + json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) + expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8", "Authorization" => @auth) + end + end + + context "and user has project download access" do + before do + env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b" + lfs_object_three = create(:lfs_object, :with_file, oid: "267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b", size: 127192524) + project.lfs_objects << lfs_object_three + end + + it "cannot get a lfs object that is not in the project" do + expect(lfs_router_forked_auth.try_call.first).to eq(404) + end + end + end + end + end + + describe 'when initiating pushing of the lfs object' do + before do + enable_lfs + env['REQUEST_METHOD'] = 'POST' + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" + end + + describe 'when user is authenticated' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }] + }.to_json + env['rack.input'] = StringIO.new(body) + end + + describe 'when user has project push access' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :master] + end + + context 'when pushing an lfs object that already exists' do + before do + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 and links the object to the project" do + response_body = lfs_router_auth.try_call.last + response = ActiveSupport::JSON.decode(response_body.first) + + expect(response['objects']).to be_kind_of(Array) + expect(response['objects'].first['oid']).to eq(sample_oid) + expect(response['objects'].first['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response['objects'].first).to have_key('_links') + end + end + + context 'when pushing a lfs object that does not exist' do + before do + body = { + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }] + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it "responds with status 200 and upload hypermedia link" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(lfs_object.projects.pluck(:id)).not_to include(project.id) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + end + end + + context 'when pushing one new and one existing lfs object' do + before do + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ] + }.to_json + env['rack.input'] = StringIO.new(body) + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 with upload hypermedia link for the new object" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + + + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + + expect(response_body['objects'].last['oid']).to eq(sample_oid) + expect(response_body['objects'].last['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response_body['objects'].last).to have_key('_links') + end + end + end + + context 'when user does not have push access' do + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + end + + context 'when user is not authenticated' do + context 'when user has push access' do + before do + project.team << [user, :master] + end + + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end + end + + context 'when user does not have push access' do + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end + end + end + end + + describe 'when pushing a lfs object' do + before do + enable_lfs + env['REQUEST_METHOD'] = 'PUT' + end + + describe 'to one project' do + describe 'when user has push access to the project' do + before do + project.team << [user, :master] + end + + describe 'when user is authenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(project) + end + + it 'responds with status 200, location of lfs store and object details' do + json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) + + expect(lfs_router_auth.try_call.first).to eq(200) + expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload") + expect(json_response['LfsOid']).to eq(sample_oid) + expect(json_response['LfsSize']).to eq(sample_size) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(project) + end + + it 'responds with status 200 and lfs object is linked to the project' do + expect(lfs_router_auth.try_call.first).to eq(200) + expect(lfs_object.projects.pluck(:id)).to include(project.id) + end + end + end + + describe 'when user is unauthenticated' do + let(:lfs_router_noauth) { new_lfs_router(project, nil) } + + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(project) + end + + it 'responds with status 401' do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(project) + end + + it 'responds with status 401' do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + + context 'and request is sent with a malformed headers' do + before do + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" + env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd" + end + + it 'does not recognize it as a valid lfs command' do + expect(lfs_router_noauth.try_call).to eq(nil) + end + end + end + end + + describe 'and user does not have push access' do + describe 'when user is authenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(project) + end + + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(project) + end + + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end + end + + describe 'when user is unauthenticated' do + let(:lfs_router_noauth) { new_lfs_router(project, nil) } + + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(project) + end + + it 'responds with 401' do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(project) + end + + it 'responds with 401' do + expect(lfs_router_noauth.try_call.first).to eq(401) + end + end + end + end + end + + describe "to a forked project" do + let(:forked_project) { fork_project(public_project, user) } + + describe 'when user has push access to the project' do + before do + forked_project.team << [user_two, :master] + end + + describe 'when user is authenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(forked_project) + end + + it 'responds with status 200, location of lfs store and object details' do + json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) + + expect(lfs_router_forked_auth.try_call.first).to eq(200) + expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload") + expect(json_response['LfsOid']).to eq(sample_oid) + expect(json_response['LfsSize']).to eq(sample_size) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(forked_project) + end + + it 'responds with status 200 and lfs object is linked to the source project' do + expect(lfs_router_forked_auth.try_call.first).to eq(200) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + end + end + end + + describe 'when user is unauthenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(forked_project) + end + + it 'responds with status 401' do + expect(lfs_router_forked_noauth.try_call.first).to eq(401) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(forked_project) + end + + it 'responds with status 401' do + expect(lfs_router_forked_noauth.try_call.first).to eq(401) + end + end + end + end + + describe 'and user does not have push access' do + describe 'when user is authenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(forked_project) + end + + it 'responds with 403' do + expect(lfs_router_forked_auth.try_call.first).to eq(403) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(forked_project) + end + + it 'responds with 403' do + expect(lfs_router_forked_auth.try_call.first).to eq(403) + end + end + end + + describe 'when user is unauthenticated' do + context 'and request is sent by gitlab-workhorse to authorize the request' do + before do + header_for_upload_authorize(forked_project) + end + + it 'responds with 401' do + expect(lfs_router_forked_noauth.try_call.first).to eq(401) + end + end + + context 'and request is sent by gitlab-workhorse to finalize the upload' do + before do + headers_for_upload_finalize(forked_project) + end + + it 'responds with 401' do + expect(lfs_router_forked_noauth.try_call.first).to eq(401) + end + end + end + end + + describe 'and second project not related to fork or a source project' do + let(:second_project) { create(:project) } + let(:lfs_router_second_project) { new_lfs_router(second_project, user) } + + before do + public_project.lfs_objects << lfs_object + headers_for_upload_finalize(second_project) + end + + context 'when pushing the same lfs object to the second project' do + before do + second_project.team << [user, :master] + end + + it 'responds with 200 and links the lfs object to the project' do + expect(lfs_router_second_project.try_call.first).to eq(200) + expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id) + end + end + end + end + end + + def enable_lfs + allow(Gitlab.config.lfs).to receive(:enabled).and_return(true) + end + + def authorize(user) + ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) + end + + def new_lfs_router(project, user) + Gitlab::Lfs::Router.new(project, user, request) + end + + def header_for_upload_authorize(project) + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize" + end + + def headers_for_upload_finalize(project) + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}" + env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4" + end + + def fork_project(project, user, object = nil) + allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) + Projects::ForkService.new(project, user, {}).execute + end +end -- cgit v1.2.1 From 796bb651700c26ce1a5693ba6d6c8b2353cb6e34 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 14 Nov 2015 19:29:58 +0100 Subject: Remove duplicate methods in uploaders Signed-off-by: Dmitriy Zaporozhets --- app/uploaders/attachment_uploader.rb | 19 ++----------------- app/uploaders/avatar_uploader.rb | 19 ++----------------- app/uploaders/file_uploader.rb | 19 ++----------------- app/uploaders/uploader_helper.rb | 19 +++++++++++++++++++ 4 files changed, 25 insertions(+), 51 deletions(-) create mode 100644 app/uploaders/uploader_helper.rb diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb index a9691bee46e..a65a896e41e 100644 --- a/app/uploaders/attachment_uploader.rb +++ b/app/uploaders/attachment_uploader.rb @@ -1,26 +1,11 @@ # encoding: utf-8 class AttachmentUploader < CarrierWave::Uploader::Base + include UploaderHelper + storage :file def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end - - def image? - img_ext = %w(png jpg jpeg gif bmp tiff) - if file.respond_to?(:extension) - img_ext.include?(file.extension.downcase) - else - # Not all CarrierWave storages respond to :extension - ext = file.path.split('.').last.downcase - img_ext.include?(ext) - end - rescue - false - end - - def file_storage? - self.class.storage == CarrierWave::Storage::File - end end diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb index 7cad044555b..6135c3ad96f 100644 --- a/app/uploaders/avatar_uploader.rb +++ b/app/uploaders/avatar_uploader.rb @@ -1,6 +1,8 @@ # encoding: utf-8 class AvatarUploader < CarrierWave::Uploader::Base + include UploaderHelper + storage :file after :store, :reset_events_cache @@ -9,23 +11,6 @@ class AvatarUploader < CarrierWave::Uploader::Base "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end - def image? - img_ext = %w(png jpg jpeg gif bmp tiff) - if file.respond_to?(:extension) - img_ext.include?(file.extension.downcase) - else - # Not all CarrierWave storages respond to :extension - ext = file.path.split('.').last.downcase - img_ext.include?(ext) - end - rescue - false - end - - def file_storage? - self.class.storage == CarrierWave::Storage::File - end - def reset_events_cache(file) model.reset_events_cache if model.is_a?(User) end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index e8211585834..ac920119a85 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -1,5 +1,7 @@ # encoding: utf-8 class FileUploader < CarrierWave::Uploader::Base + include UploaderHelper + storage :file attr_accessor :project, :secret @@ -28,21 +30,4 @@ class FileUploader < CarrierWave::Uploader::Base def secure_url File.join("/uploads", @secret, file.filename) end - - def file_storage? - self.class.storage == CarrierWave::Storage::File - end - - def image? - img_ext = %w(png jpg jpeg gif bmp tiff) - if file.respond_to?(:extension) - img_ext.include?(file.extension.downcase) - else - # Not all CarrierWave storages respond to :extension - ext = file.path.split('.').last.downcase - img_ext.include?(ext) - end - rescue - false - end end diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb new file mode 100644 index 00000000000..5ef440f3367 --- /dev/null +++ b/app/uploaders/uploader_helper.rb @@ -0,0 +1,19 @@ +# Extra methods for uploader +module UploaderHelper + def image? + img_ext = %w(png jpg jpeg gif bmp tiff) + if file.respond_to?(:extension) + img_ext.include?(file.extension.downcase) + else + # Not all CarrierWave storages respond to :extension + ext = file.path.split('.').last.downcase + img_ext.include?(ext) + end + rescue + false + end + + def file_storage? + self.class.storage == CarrierWave::Storage::File + end +end -- cgit v1.2.1 From cd513034e66a3eccb9022968af3a09bc7203a9c9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 14 Nov 2015 19:31:14 +0100 Subject: Set less strict flay option for now Signed-off-by: Dmitriy Zaporozhets --- lib/tasks/flay.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake index 5efffc2cdac..dfb9df4772a 100644 --- a/lib/tasks/flay.rake +++ b/lib/tasks/flay.rake @@ -1,6 +1,6 @@ desc 'Code duplication analyze via flay' task :flay do - output = %x(bundle exec flay app/ lib/gitlab/) + output = %x(bundle exec flay --mass 30 app/ lib/gitlab/) if output.include? "Similar code found" puts output -- cgit v1.2.1 From 433e4a80efa50bdc3b588ae34c488b6982c2985d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 14 Nov 2015 19:38:05 +0100 Subject: Fix code duplication in NotificationsHelper Signed-off-by: Dmitriy Zaporozhets --- app/helpers/notifications_helper.rb | 38 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index cf11f8e5320..ba072786145 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -16,40 +16,28 @@ module NotificationsHelper def notification_list_item(notification_level, user_membership) case notification_level when Notification::N_DISABLED - content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do - icon('microphone-slash fw', text: 'Disabled') - end - end + update_notification_link(Notification::N_DISABLED, user_membership, 'Disabled', 'microphone-slash') when Notification::N_PARTICIPATING - content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do - icon('volume-up fw', text: 'Participate') - end - end + update_notification_link(Notification::N_PARTICIPATING, user_membership, 'Participate', 'volume-up') when Notification::N_WATCH - content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do - icon('eye fw', text: 'Watch') - end - end + update_notification_link(Notification::N_WATCH, user_membership, 'Watch', 'eye') when Notification::N_MENTION - content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION } do - icon('at fw', text: 'On mention') - end - end + update_notification_link(Notification::N_MENTION, user_membership, 'On mention', 'at') when Notification::N_GLOBAL - content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do - link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do - icon('globe fw', text: 'Global') - end - end + update_notification_link(Notification::N_GLOBAL, user_membership, 'Global', 'globe') else # do nothing end end + def update_notification_link(notification_label, user_membership, title, icon) + content_tag(:li, class: active_level_for(user_membership, notification_level)) do + link_to '#', class: 'update-notification', data: { notification_level: notification_label } do + icon("#{icon} fw", text: title) + end + end + end + def notification_label(user_membership) Notification.new(user_membership).to_s end -- cgit v1.2.1 From 0698c96d7dc472a0e7e74c290047a50eeddf27bb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 14 Nov 2015 19:43:48 +0100 Subject: Remove duplicate code in Repository#*_names_contains Signed-off-by: Dmitriy Zaporozhets --- app/models/repository.rb | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index f8c4cb1387b..f76b770e867 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -346,8 +346,8 @@ class Repository end end - def branch_names_contains(sha) - args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha}) + def refs_contains_sha(ref_type, sha) + args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha}) names = Gitlab::Popen.popen(args, path_to_repo).first if names.respond_to?(:split) @@ -363,21 +363,12 @@ class Repository end end - def tag_names_contains(sha) - args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha}) - names = Gitlab::Popen.popen(args, path_to_repo).first - - if names.respond_to?(:split) - names = names.split("\n").map(&:strip) - - names.each do |name| - name.slice! '* ' - end + def branch_names_contains(sha) + refs_contains_sha('branch', sha) + end - names - else - [] - end + def tag_names_contains(sha) + refs_contains_sha('tag', sha) end def branches -- cgit v1.2.1 From c9f2f2a4838f9aa49782acdb7cfad75d06f31ac2 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 09:53:17 +0100 Subject: Fix wrong variable name Signed-off-by: Dmitriy Zaporozhets --- app/helpers/notifications_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index ba072786145..499c655d2bf 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -30,9 +30,9 @@ module NotificationsHelper end end - def update_notification_link(notification_label, user_membership, title, icon) + def update_notification_link(notification_level, user_membership, title, icon) content_tag(:li, class: active_level_for(user_membership, notification_level)) do - link_to '#', class: 'update-notification', data: { notification_level: notification_label } do + link_to '#', class: 'update-notification', data: { notification_level: notification_level } do icon("#{icon} fw", text: title) end end -- cgit v1.2.1 From 03f5ff750b107b30a6d306aafb6699a9c9ecff0d Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 16 Nov 2015 13:24:36 +0100 Subject: Show specific runners from projects where user is master or owner --- CHANGELOG | 1 + app/models/user.rb | 15 ++++++++++----- spec/features/runners_spec.rb | 12 +++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 98066668335..45ef22e7e86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -35,6 +35,7 @@ v 8.2.0 (unreleased) - New design for project graphs page - Remove deprecated dumped yaml file generated from previous job definitions - Fix incoming email config defaults + - Show specific runners from projects where user is master or owner - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - Make color of "Accept Merge Request" button consistent with current build status diff --git a/app/models/user.rb b/app/models/user.rb index 9ffadcf4468..61abea1f6ea 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -405,6 +405,15 @@ class User < ActiveRecord::Base end end + def master_or_owner_projects_id + @master_or_owner_projects_id ||= begin + scope = { access_level: [ Gitlab::Access::MASTER, Gitlab::Access::OWNER ] } + project_ids = personal_projects.pluck(:id) + project_ids.push(*groups_projects.where(members: scope).pluck(:id)) + project_ids.push(*projects.where(members: scope).pluck(:id).uniq) + end + end + # Projects user has access to def authorized_projects @authorized_projects ||= Project.where(id: authorized_projects_id) @@ -765,14 +774,10 @@ class User < ActiveRecord::Base !solo_owned_groups.present? end - def ci_authorized_projects - @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id) - end - def ci_authorized_runners @ci_authorized_runners ||= begin runner_ids = Ci::RunnerProject.joins(:project). - where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id) + where(ci_projects: { gitlab_id: master_or_owner_projects_id }).select(:runner_id) Ci::Runner.specific.where(id: runner_ids) end end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 06adb7633b2..b0259026630 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -14,15 +14,25 @@ describe "Runners" do @project2 = FactoryGirl.create :ci_project @project2.gl_project.team << [user, :master] + @project3 = FactoryGirl.create :ci_project + @project3.gl_project.team << [user, :developer] + @shared_runner = FactoryGirl.create :ci_shared_runner @specific_runner = FactoryGirl.create :ci_specific_runner @specific_runner2 = FactoryGirl.create :ci_specific_runner + @specific_runner3 = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner @project2.runners << @specific_runner2 + @project3.runners << @specific_runner3 visit runners_path(@project.gl_project) end + before do + expect(page).to_not have_content(@specific_runner3.display_name) + expect(page).to_not have_content(@specific_runner3.display_name) + end + it "places runners in right places" do expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name) expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name) @@ -76,10 +86,10 @@ describe "Runners" do @project.gl_project.team << [user, :master] @specific_runner = FactoryGirl.create :ci_specific_runner @project.runners << @specific_runner - visit runners_path(@project.gl_project) end it "shows runner information" do + visit runners_path(@project.gl_project) click_on @specific_runner.short_sha expect(page).to have_content(@specific_runner.platform) end -- cgit v1.2.1 From 05335a3c8584c48a9317bd0919eccee6948de742 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 16:07:27 +0100 Subject: Create milestones in the group Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups/milestones_controller.rb | 36 ++++++++++++++++--- app/views/groups/milestones/index.html.haml | 12 +++++-- app/views/groups/milestones/new.html.haml | 46 +++++++++++++++++++++++++ config/routes.rb | 2 +- features/groups.feature | 9 ++++- features/steps/groups.rb | 22 ++++++++++++ features/steps/shared/paths.rb | 4 +++ 7 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 app/views/groups/milestones/new.html.haml diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 669f7f3126d..8779376d93c 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -1,16 +1,34 @@ class Groups::MilestonesController < Groups::ApplicationController before_action :authorize_group_milestone!, only: :update + before_action :group def index - project_milestones = case params[:state] - when 'all'; state - when 'closed'; state('closed') - else state('active') - end + project_milestones = + case params[:state] + when 'all'; state + when 'closed'; state('closed') + else state('active') + end + @group_milestones = Milestones::GroupService.new(project_milestones).execute @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE) end + def new + @group_milestone = OpenStruct.new(title: nil, description: nil) + end + + def create + project_ids = params[:milestone][:project_ids] + title = milestone_params[:title] + + @group.projects.where(id: project_ids).each do |project| + Milestones::CreateService.new(project, current_user, milestone_params).execute + end + + redirect_to group_milestone_path(@group, title.parameterize, title: title) + end + def show project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC") @group_milestone = Milestones::GroupService.new(project_milestones).milestone(title) @@ -51,4 +69,12 @@ class Groups::MilestonesController < Groups::ApplicationController def authorize_group_milestone! return render_404 unless can?(current_user, :admin_group, group) end + + def milestone_params + params.require(:milestone).permit( + :title, + :description, + :due_date + ) + end end diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 2bbcad5fdfb..ded4f3713f6 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -3,9 +3,15 @@ = render 'shared/milestones_filter' .gray-content-block - Only milestones from - %strong #{@group.name} - group are listed here. + .pull-right + %span.pull-right.hidden-xs + = link_to new_group_milestone_path(@group), class: "btn btn-new" do + New Milestone + + .oneline + Only milestones from + %strong #{@group.name} + group are listed here. .milestones %ul.content-list - if @group_milestones.blank? diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml new file mode 100644 index 00000000000..287f89a7074 --- /dev/null +++ b/app/views/groups/milestones/new.html.haml @@ -0,0 +1,46 @@ +%h3.page-title + New Milestone + +%p.light + This will create milestone in every selected project +%hr + += form_for @group_milestone, as: :milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' } do |f| + .row + .col-md-6 + .form-group + = f.label :title, "Title", class: "control-label" + .col-sm-10 + = 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 js-quick-submit' + = render 'projects/notes/hints' + .clearfix + .error-alert + .form-group + = f.label :projects, "Projects", class: "control-label" + .col-sm-10 + = f.collection_select :project_ids, @group.projects, :id, :name, + { selected: @group.projects.map(&:id) }, multiple: true, class: 'select2' + + .col-md-6 + .form-group + = f.label :due_date, "Due Date", class: "control-label" + .col-sm-10= f.hidden_field :due_date + .col-sm-10 + .datepicker + + .form-actions + = f.submit 'Create Milestone', class: "btn-create btn" + = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel" + + +:javascript + $( ".datepicker" ).datepicker({ + dateFormat: "yy-mm-dd", + onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } + }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); diff --git a/config/routes.rb b/config/routes.rb index bd85f4e3c69..c0077ab1a99 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -368,7 +368,7 @@ Gitlab::Application.routes.draw do end resource :avatar, only: [:destroy] - resources :milestones, only: [:index, :show, :update] + resources :milestones, only: [:index, :show, :update, :new, :create] end end diff --git a/features/groups.feature b/features/groups.feature index db37fa3b375..615eff6a330 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -153,6 +153,13 @@ Feature: Groups Then I should see group milestone with descriptions and expiry date And I should see group milestone with all issues and MRs assigned to that milestone + Scenario: Create multiple milestones with one form + Given I visit group "Owned" milestones page + And I click new milestone button + And I fill milestone name + When I press create mileston button + Then milestone in each project should be created + # Group projects in settings Scenario: I should see all projects in the project list in settings Given Group "Owned" has archived project @@ -169,4 +176,4 @@ Feature: Groups When I visit group "Owned" page Then I should see group "Owned" Then I should see project "Public-project" - + diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 70388c18fcf..a8fba2406ae 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -255,6 +255,28 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end + step 'I fill milestone name' do + fill_in 'milestone_title', with: 'v2.9.0' + end + + step 'I click new milestone button' do + click_link "New Milestone" + end + + step 'I press create mileston button' do + click_button "Create Milestone" + end + + step 'milestone in each project should be created' do + group = Group.find_by(name: 'Owned') + expect(page).to have_content "Milestone v2.9.0" + expect(group.projects).to be_present + + group.projects.each do |project| + expect(page).to have_content project.name + end + end + protected def assigned_to_me(key) diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index eb978620da6..c74a5fd3bc7 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -31,6 +31,10 @@ module SharedPaths visit merge_requests_group_path(Group.find_by(name: "Owned")) end + step 'I visit group "Owned" milestones page' do + visit group_milestones_path(Group.find_by(name: "Owned")) + end + step 'I visit group "Owned" members page' do visit group_group_members_path(Group.find_by(name: "Owned")) end -- cgit v1.2.1 From 986695e136a8f6afa326048b30be77a9265d3bf7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 19:20:48 +0100 Subject: Refactor global and group milestones logic Signed-off-by: Dmitriy Zaporozhets --- app/controllers/concerns/global_milestones.rb | 19 +++++ app/controllers/dashboard/milestones_controller.rb | 29 ++----- app/controllers/groups/application_controller.rb | 11 ++- app/controllers/groups/avatars_controller.rb | 2 - app/controllers/groups/group_members_controller.rb | 5 -- app/controllers/groups/milestones_controller.rb | 63 +++++--------- app/finders/milestones_finder.rb | 13 +++ app/helpers/labels_helper.rb | 2 +- app/helpers/milestones_helper.rb | 2 +- app/models/global_label.rb | 17 ++++ app/models/global_milestone.rb | 97 ++++++++++++++++++++++ app/models/group_label.rb | 9 -- app/models/group_milestone.rb | 89 -------------------- app/services/labels/group_service.rb | 26 ------ app/services/milestones/collection_service.rb | 26 ++++++ app/services/milestones/group_service.rb | 26 ------ app/views/dashboard/milestones/index.html.haml | 6 +- app/views/dashboard/milestones/show.html.haml | 36 ++++---- app/views/groups/milestones/index.html.haml | 6 +- app/views/groups/milestones/new.html.haml | 2 +- app/views/groups/milestones/show.html.haml | 42 +++++----- 21 files changed, 254 insertions(+), 274 deletions(-) create mode 100644 app/controllers/concerns/global_milestones.rb create mode 100644 app/finders/milestones_finder.rb create mode 100644 app/models/global_label.rb create mode 100644 app/models/global_milestone.rb delete mode 100644 app/models/group_label.rb delete mode 100644 app/models/group_milestone.rb delete mode 100644 app/services/labels/group_service.rb create mode 100644 app/services/milestones/collection_service.rb delete mode 100644 app/services/milestones/group_service.rb diff --git a/app/controllers/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb new file mode 100644 index 00000000000..b428249acd3 --- /dev/null +++ b/app/controllers/concerns/global_milestones.rb @@ -0,0 +1,19 @@ +module GlobalMilestones + extend ActiveSupport::Concern + + def milestones + @milestones = MilestonesFinder.new.execute(@projects, params) + @milestones = GlobalMilestone.build_collection(@milestones) + @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE) + end + + def milestone + milestones = Milestone.of_projects(@projects).where(title: params[:title]) + + if milestones.present? + @milestone = GlobalMilestone.new(params[:title], milestones) + else + render_404 + end + end +end diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb index 53896d4f2c7..2bdce0f8a00 100644 --- a/app/controllers/dashboard/milestones_controller.rb +++ b/app/controllers/dashboard/milestones_controller.rb @@ -1,34 +1,19 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController - before_action :load_projects + include GlobalMilestones + + before_action :projects + before_action :milestones, only: [:index] + before_action :milestone, only: [:show] def index - project_milestones = case params[:state] - when 'all'; state - when 'closed'; state('closed') - else state('active') - end - @dashboard_milestones = Milestones::GroupService.new(project_milestones).execute - @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE) end def show - project_milestones = Milestone.where(project_id: @projects).order("due_date ASC") - @dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title) end private - def load_projects - @projects = current_user.authorized_projects.sorted_by_activity.non_archived - end - - def title - params[:title] - end - - def state(state = nil) - conditions = { project_id: @projects } - conditions.reverse_merge!(state: state) if state - Milestone.where(conditions).order("title ASC") + def projects + @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived end end diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 6878d4bc07e..be801858eaf 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -1,8 +1,13 @@ class Groups::ApplicationController < ApplicationController layout 'group' + before_action :group private - + + def group + @group ||= Group.find_by(path: params[:group_id]) + end + def authorize_read_group! unless @group and can?(current_user, :read_group, @group) if current_user.nil? @@ -12,13 +17,13 @@ class Groups::ApplicationController < ApplicationController end end end - + def authorize_admin_group! unless can?(current_user, :admin_group, group) return render_404 end end - + def authorize_admin_group_member! unless can?(current_user, :admin_group_member, group) return render_403 diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index 6aa64222f77..f390705dc6a 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -1,8 +1,6 @@ class Groups::AvatarsController < ApplicationController def destroy - @group = Group.find_by(path: params[:group_id]) @group.remove_avatar! - @group.save redirect_to edit_group_path(@group) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 91518c44a98..b25957a06e2 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,6 +1,5 @@ class Groups::GroupMembersController < Groups::ApplicationController skip_before_action :authenticate_user!, only: [:index] - before_action :group # Authorize before_action :authorize_read_group! @@ -80,10 +79,6 @@ class Groups::GroupMembersController < Groups::ApplicationController protected - def group - @group ||= Group.find_by(path: params[:group_id]) - end - def member_params params.require(:group_member).permit(:access_level, :user_id) end diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 8779376d93c..6833a550c9e 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -1,21 +1,16 @@ class Groups::MilestonesController < Groups::ApplicationController - before_action :authorize_group_milestone!, only: :update - before_action :group + include GlobalMilestones - def index - project_milestones = - case params[:state] - when 'all'; state - when 'closed'; state('closed') - else state('active') - end + before_action :projects + before_action :milestones, only: [:index] + before_action :milestone, only: [:show, :update] + before_action :authorize_group_milestone!, only: [:create, :update] - @group_milestones = Milestones::GroupService.new(project_milestones).execute - @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE) + def index end def new - @group_milestone = OpenStruct.new(title: nil, description: nil) + @milestone = Milestone.new end def create @@ -26,55 +21,35 @@ class Groups::MilestonesController < Groups::ApplicationController Milestones::CreateService.new(project, current_user, milestone_params).execute end - redirect_to group_milestone_path(@group, title.parameterize, title: title) + redirect_to milestone_path(title) end def show - project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC") - @group_milestone = Milestones::GroupService.new(project_milestones).milestone(title) end def update - project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC") - @group_milestones = Milestones::GroupService.new(project_milestones).milestone(title) - - @group_milestones.milestones.each do |milestone| - Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone) + @milestone.milestones.each do |milestone| + Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone) end - respond_to do |format| - format.js - format.html do - redirect_to group_milestones_path(group) - end - end + redirect_back_or_default(default: milestone_path(@milestone.title)) end private - def group - @group ||= Group.find_by(path: params[:group_id]) - end - - def title - params[:title] + def authorize_group_milestone! + return render_404 unless can?(current_user, :admin_group, group) end - def state(state = nil) - conditions = { project_id: group.projects } - conditions.reverse_merge!(state: state) if state - Milestone.where(conditions).order("title ASC") + def milestone_params + params.require(:milestone).permit(:title, :description, :due_date, :state_event) end - def authorize_group_milestone! - return render_404 unless can?(current_user, :admin_group, group) + def milestone_path(title) + group_milestone_path(@group, title.parameterize, title: title) end - def milestone_params - params.require(:milestone).permit( - :title, - :description, - :due_date - ) + def projects + @projects ||= @group.projects end end diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb new file mode 100644 index 00000000000..71f207ca030 --- /dev/null +++ b/app/finders/milestones_finder.rb @@ -0,0 +1,13 @@ +class MilestonesFinder + def execute(projects, params) + milestones = Milestone.of_projects(projects) + milestones = milestones.order("due_date ASC") + + case params[:state] + when 'closed' then milestones.closed + when 'all' then milestones + else milestones.active + end + end +end + diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index ee04ace35d0..795fb439f25 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -100,7 +100,7 @@ module LabelsHelper Label.where(project_id: @projects) end - grouped_labels = Labels::GroupService.new(labels).execute + grouped_labels = GlobalLabel.build_collection(labels) grouped_labels.unshift(Label::None) grouped_labels.unshift(Label::Any) diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index 37a5b58cce8..ad43892b639 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -28,7 +28,7 @@ module MilestonesHelper Milestone.where(project_id: @projects) end.active - grouped_milestones = Milestones::GroupService.new(milestones).execute + grouped_milestones = GlobalMilestone.build_collection(milestones) grouped_milestones.unshift(Milestone::None) grouped_milestones.unshift(Milestone::Any) diff --git a/app/models/global_label.rb b/app/models/global_label.rb new file mode 100644 index 00000000000..0171f7d54b7 --- /dev/null +++ b/app/models/global_label.rb @@ -0,0 +1,17 @@ +class GlobalLabel + attr_accessor :title, :labels + alias_attribute :name, :title + + def self.build_collection(labels) + labels = labels.group_by(&:title) + + labels.map do |title, label| + new(title, label) + end + end + + def initialize(title, labels) + @title = title + @labels = labels + end +end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb new file mode 100644 index 00000000000..f96e9d41c94 --- /dev/null +++ b/app/models/global_milestone.rb @@ -0,0 +1,97 @@ +class GlobalMilestone + attr_accessor :title, :milestones + alias_attribute :name, :title + + def self.build_collection(milestones) + milestones = milestones.group_by(&:title) + + milestones.map do |title, milestones| + new(title, milestones) + end + end + + def initialize(title, milestones) + @title = title + @milestones = milestones + end + + def safe_title + @title.parameterize + end + + def projects + milestones.map { |milestone| milestone.project } + end + + def issue_count + milestones.map { |milestone| milestone.issues.count }.sum + end + + def merge_requests_count + milestones.map { |milestone| milestone.merge_requests.count }.sum + end + + def open_items_count + milestones.map { |milestone| milestone.open_items_count }.sum + end + + def closed_items_count + milestones.map { |milestone| milestone.closed_items_count }.sum + end + + def total_items_count + milestones.map { |milestone| milestone.total_items_count }.sum + end + + def percent_complete + ((closed_items_count * 100) / total_items_count).abs + rescue ZeroDivisionError + 0 + end + + def state + state = milestones.map { |milestone| milestone.state } + + if state.count('closed') == state.size + 'closed' + else + 'active' + end + end + + def active? + state == 'active' + end + + def closed? + state == 'closed' + end + + def issues + @issues ||= milestones.map(&:issues).flatten.group_by(&:state) + end + + def merge_requests + @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state) + end + + def participants + @participants ||= milestones.map(&:participants).flatten.compact.uniq + end + + def opened_issues + issues.values_at("opened", "reopened").compact.flatten + end + + def closed_issues + issues['closed'] + end + + def opened_merge_requests + merge_requests.values_at("opened", "reopened").compact.flatten + end + + def closed_merge_requests + merge_requests.values_at("closed", "merged", "locked").compact.flatten + end +end diff --git a/app/models/group_label.rb b/app/models/group_label.rb deleted file mode 100644 index 0fc39cb8771..00000000000 --- a/app/models/group_label.rb +++ /dev/null @@ -1,9 +0,0 @@ -class GroupLabel - attr_accessor :title, :labels - alias_attribute :name, :title - - def initialize(title, labels) - @title = title - @labels = labels - end -end diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb deleted file mode 100644 index 91844da62e2..00000000000 --- a/app/models/group_milestone.rb +++ /dev/null @@ -1,89 +0,0 @@ -class GroupMilestone - attr_accessor :title, :milestones - alias_attribute :name, :title - - def initialize(title, milestones) - @title = title - @milestones = milestones - end - - def safe_title - @title.parameterize - end - - def projects - milestones.map { |milestone| milestone.project } - end - - def issue_count - milestones.map { |milestone| milestone.issues.count }.sum - end - - def merge_requests_count - milestones.map { |milestone| milestone.merge_requests.count }.sum - end - - def open_items_count - milestones.map { |milestone| milestone.open_items_count }.sum - end - - def closed_items_count - milestones.map { |milestone| milestone.closed_items_count }.sum - end - - def total_items_count - milestones.map { |milestone| milestone.total_items_count }.sum - end - - def percent_complete - ((closed_items_count * 100) / total_items_count).abs - rescue ZeroDivisionError - 0 - end - - def state - state = milestones.map { |milestone| milestone.state } - - if state.count('closed') == state.size - 'closed' - else - 'active' - end - end - - def active? - state == 'active' - end - - def closed? - state == 'closed' - end - - def issues - @group_issues ||= milestones.map(&:issues).flatten.group_by(&:state) - end - - def merge_requests - @group_merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state) - end - - def participants - @group_participants ||= milestones.map(&:participants).flatten.compact.uniq - end - - def opened_issues - issues.values_at("opened", "reopened").compact.flatten - end - - def closed_issues - issues['closed'] - end - - def opened_merge_requests - merge_requests.values_at("opened", "reopened").compact.flatten - end - - def closed_merge_requests - merge_requests.values_at("closed", "merged", "locked").compact.flatten - end -end diff --git a/app/services/labels/group_service.rb b/app/services/labels/group_service.rb deleted file mode 100644 index b26cee24d56..00000000000 --- a/app/services/labels/group_service.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Labels - class GroupService < ::BaseService - def initialize(project_labels) - @project_labels = project_labels.group_by(&:title) - end - - def execute - build(@project_labels) - end - - def label(title) - if title - group_label = @project_labels[title].group_by(&:title) - build(group_label).first - else - nil - end - end - - private - - def build(label) - label.map { |title, labels| GroupLabel.new(title, labels) } - end - end -end diff --git a/app/services/milestones/collection_service.rb b/app/services/milestones/collection_service.rb new file mode 100644 index 00000000000..2eefec99e7b --- /dev/null +++ b/app/services/milestones/collection_service.rb @@ -0,0 +1,26 @@ +module Milestones + class CollectionService < Milestones::BaseService + def initialize(project_milestones) + @project_milestones = project_milestones.group_by(&:title) + end + + def execute + build(@project_milestones) + end + + def milestone(title) + if title + group_milestone = @project_milestones[title].group_by(&:title) + build(group_milestone).first + else + nil + end + end + + private + + def build(milestone) + milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) } + end + end +end diff --git a/app/services/milestones/group_service.rb b/app/services/milestones/group_service.rb deleted file mode 100644 index 11d702f1e7b..00000000000 --- a/app/services/milestones/group_service.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Milestones - class GroupService < Milestones::BaseService - def initialize(project_milestones) - @project_milestones = project_milestones.group_by(&:title) - end - - def execute - build(@project_milestones) - end - - def milestone(title) - if title - group_milestone = @project_milestones[title].group_by(&:title) - build(group_milestone).first - else - nil - end - end - - private - - def build(milestone) - milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) } - end - end -end diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index 21b25c3986e..635251e2374 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -10,10 +10,10 @@ .milestones %ul.content-list - - if @dashboard_milestones.blank? + - if @milestones.blank? %li .nothing-here-block No milestones to show - else - - @dashboard_milestones.each do |milestone| + - @milestones.each do |milestone| = render 'milestone', milestone: milestone - = paginate @dashboard_milestones, theme: "gitlab" + = paginate @milestones, theme: "gitlab" diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 2fe14c6388c..580db613ed4 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,14 +1,14 @@ -- page_title @dashboard_milestone.title, "Milestones" +- page_title @milestone.title, "Milestones" %h4.page-title - .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" } - - if @dashboard_milestone.closed? + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? Closed - else Open - Milestone #{@dashboard_milestone.title} + Milestone #{@milestone.title} %hr -- if (@dashboard_milestone.total_items_count == @dashboard_milestone.closed_items_count) && @dashboard_milestone.active? +- if (@milestone.total_items_count == @milestone.closed_items_count) && @milestone.active? .alert.alert-success %span All issues for this milestone are closed. You may close the milestone now. @@ -22,7 +22,7 @@ %th Open issues %th State %th Due date - - @dashboard_milestone.milestones.each do |milestone| + - @milestone.milestones.each do |milestone| %tr %td = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) @@ -39,46 +39,46 @@ .context %p.lead Progress: - #{@dashboard_milestone.closed_items_count} closed + #{@milestone.closed_items_count} closed – - #{@dashboard_milestone.open_items_count} open - = milestone_progress_bar(@dashboard_milestone) + #{@milestone.open_items_count} open + = milestone_progress_bar(@milestone) %ul.nav.nav-tabs %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @dashboard_milestone.issue_count + %span.badge= @milestone.issue_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests - %span.badge= @dashboard_milestone.merge_requests_count + %span.badge= @milestone.merge_requests_count %li = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants - %span.badge= @dashboard_milestone.participants.count + %span.badge= @milestone.participants.count .pull-right - = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @dashboard_milestone.title), class: "btn edit-milestone-link btn-grouped" + = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" .tab-content .tab-pane.active#tab-issues .row .col-md-6 - = render 'issues', title: "Open", issues: @dashboard_milestone.opened_issues + = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 - = render 'issues', title: "Closed", issues: @dashboard_milestone.closed_issues + = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests .row .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @dashboard_milestone.opened_merge_requests + = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @dashboard_milestone.closed_merge_requests + = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants %ul.bordered-list - - @dashboard_milestone.participants.each do |user| + - @milestone.participants.each do |user| %li = link_to user, title: user.name, class: "darken" do = image_tag avatar_icon(user, 32), class: "avatar s32" diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index ded4f3713f6..ffd7dd3fc0b 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -14,10 +14,10 @@ group are listed here. .milestones %ul.content-list - - if @group_milestones.blank? + - if @milestones.blank? %li .nothing-here-block No milestones to show - else - - @group_milestones.each do |milestone| + - @milestones.each do |milestone| = render 'milestone', milestone: milestone - = paginate @group_milestones, theme: "gitlab" + = paginate @milestones, theme: "gitlab" diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 287f89a7074..4c490d8ccb3 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -5,7 +5,7 @@ This will create milestone in every selected project %hr -= form_for @group_milestone, as: :milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' } do |f| += form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' } do |f| .row .col-md-6 .form-group diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index a92ad5d751b..e609abca08e 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,22 +1,22 @@ -- page_title @group_milestone.title, "Milestones" +- page_title @milestone.title, "Milestones" = render "header_title" %h4.page-title - .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" } - - if @group_milestone.closed? + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? Closed - else Open - Milestone #{@group_milestone.title} + Milestone #{@milestone.title} .pull-right - if can?(current_user, :admin_group, @group) - - if @group_milestone.active? - = link_to 'Close Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close" + - if @milestone.active? + = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close" - else - = link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" + = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" %hr -- if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active? +- if (@milestone.total_items_count == @milestone.closed_items_count) && @milestone.active? .alert.alert-success %span All issues for this milestone are closed. You may close the milestone now. @@ -30,7 +30,7 @@ %th Open issues %th State %th Due date - - @group_milestone.milestones.each do |milestone| + - @milestone.milestones.each do |milestone| %tr %td = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) @@ -47,46 +47,46 @@ .context %p.lead Progress: - #{@group_milestone.closed_items_count} closed + #{@milestone.closed_items_count} closed – - #{@group_milestone.open_items_count} open - = milestone_progress_bar(@group_milestone) + #{@milestone.open_items_count} open + = milestone_progress_bar(@milestone) %ul.nav.nav-tabs %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues - %span.badge= @group_milestone.issue_count + %span.badge= @milestone.issue_count %li = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do Merge Requests - %span.badge= @group_milestone.merge_requests_count + %span.badge= @milestone.merge_requests_count %li = link_to '#tab-participants', 'data-toggle' => 'tab' do Participants - %span.badge= @group_milestone.participants.count + %span.badge= @milestone.participants.count .pull-right - = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @group_milestone.title), class: "btn edit-milestone-link btn-grouped" + = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" .tab-content .tab-pane.active#tab-issues .row .col-md-6 - = render 'issues', title: "Open", issues: @group_milestone.opened_issues + = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 - = render 'issues', title: "Closed", issues: @group_milestone.closed_issues + = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests .row .col-md-6 - = render 'merge_requests', title: "Open", merge_requests: @group_milestone.opened_merge_requests + = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 - = render 'merge_requests', title: "Closed", merge_requests: @group_milestone.closed_merge_requests + = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants %ul.bordered-list - - @group_milestone.participants.each do |user| + - @milestone.participants.each do |user| %li = link_to user, title: user.name, class: "darken" do = image_tag avatar_icon(user, 32), class: "avatar s32" -- cgit v1.2.1 From c79d801bf58c58ec21e64cb782176d6dc879a60f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 13 Nov 2015 19:31:02 +0100 Subject: Fix a bug when milestone/label filter was empty for dashboard issues page Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/controllers/dashboard_controller.rb | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 98066668335..dd4306ed0c0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -42,6 +42,7 @@ v 8.2.0 (unreleased) - Ability to add release notes (markdown text and attachments) to git tags (aka Releases) - Relative links from a repositories README.md now link to the default branch - Fix trailing whitespace issue in merge request/issue title + - Fix bug when milestone/label filter was empty for dashboard issues page v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 4ebb3d7276e..b2c1fa4230c 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,5 +1,6 @@ class DashboardController < Dashboard::ApplicationController before_action :event_filter, only: :activity + before_action :projects, only: [:issues, :merge_requests] respond_to :html @@ -47,4 +48,8 @@ class DashboardController < Dashboard::ApplicationController @events = @event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end + + def projects + @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived + end end -- cgit v1.2.1 From 98d6d491b5187945b2d86db20af411240716980a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 14:38:34 +0100 Subject: Move global milestone specs Signed-off-by: Dmitriy Zaporozhets --- app/services/milestones/collection_service.rb | 26 ---------- spec/models/global_milestone_spec.rb | 65 ++++++++++++++++++++++++ spec/services/milestones/group_service_spec.rb | 70 -------------------------- 3 files changed, 65 insertions(+), 96 deletions(-) delete mode 100644 app/services/milestones/collection_service.rb create mode 100644 spec/models/global_milestone_spec.rb delete mode 100644 spec/services/milestones/group_service_spec.rb diff --git a/app/services/milestones/collection_service.rb b/app/services/milestones/collection_service.rb deleted file mode 100644 index 2eefec99e7b..00000000000 --- a/app/services/milestones/collection_service.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Milestones - class CollectionService < Milestones::BaseService - def initialize(project_milestones) - @project_milestones = project_milestones.group_by(&:title) - end - - def execute - build(@project_milestones) - end - - def milestone(title) - if title - group_milestone = @project_milestones[title].group_by(&:title) - build(group_milestone).first - else - nil - end - end - - private - - def build(milestone) - milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) } - end - end -end diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb new file mode 100644 index 00000000000..6eeff30b20e --- /dev/null +++ b/spec/models/global_milestone_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe GlobalMilestone do + let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:group) { create(:group) } + let(:project1) { create(:project, group: group) } + let(:project2) { create(:project, path: 'gitlab-ci', group: group) } + let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) } + let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) } + let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) } + let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) } + let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) } + let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) } + let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) } + + describe :build_collection do + before do + milestones = + [ + milestone1_project1, + milestone1_project2, + milestone1_project3, + milestone2_project1, + milestone2_project2, + milestone2_project3 + ] + + @global_milestones = GlobalMilestone.build_collection(milestones) + end + + it 'should have all project milestones' do + expect(@global_milestones.count).to eq(2) + end + + it 'should have all project milestones titles' do + expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123']) + end + + it 'should have all project milestones' do + expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6) + end + end + + describe :initialize do + before do + milestones = + [ + milestone1_project1, + milestone1_project2, + milestone1_project3, + ] + + @global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones) + end + + it 'should have exactly one group milestone' do + expect(@global_milestone.title).to eq('Milestone v1.2') + end + + it 'should have all project milestones with the same title' do + expect(@global_milestone.milestones.count).to eq(3) + end + end +end diff --git a/spec/services/milestones/group_service_spec.rb b/spec/services/milestones/group_service_spec.rb deleted file mode 100644 index 74eb0f99e0f..00000000000 --- a/spec/services/milestones/group_service_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe Milestones::GroupService do - let(:user) { create(:user) } - let(:user2) { create(:user) } - let(:group) { create(:group) } - let(:project1) { create(:project, group: group) } - let(:project2) { create(:project, path: 'gitlab-ci', group: group) } - let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) } - let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) } - let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) } - let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) } - let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) } - let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) } - let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) } - - describe 'execute' do - context 'with valid projects' do - before do - milestones = - [ - milestone1_project1, - milestone1_project2, - milestone1_project3, - milestone2_project1, - milestone2_project2, - milestone2_project3 - ] - @group_milestones = Milestones::GroupService.new(milestones).execute - end - - it 'should have all project milestones' do - expect(@group_milestones.count).to eq(2) - end - - it 'should have all project milestones titles' do - expect(@group_milestones.map { |group_milestone| group_milestone.title }).to match_array(['Milestone v1.2', 'VD-123']) - end - - it 'should have all project milestones' do - expect(@group_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6) - end - end - end - - describe 'milestone' do - context 'with valid title' do - before do - milestones = - [ - milestone1_project1, - milestone1_project2, - milestone1_project3, - milestone2_project1, - milestone2_project2, - milestone2_project3 - ] - @group_milestones = Milestones::GroupService.new(milestones).milestone('Milestone v1.2') - end - - it 'should have exactly one group milestone' do - expect(@group_milestones.title).to eq('Milestone v1.2') - end - - it 'should have all project milestones with the same title' do - expect(@group_milestones.milestones.count).to eq(3) - end - end - end -end -- cgit v1.2.1 From 8a03cb87442a42c8fc1630ab356dfd35dbb476f7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 14:39:19 +0100 Subject: Lets add more tests to Milestones services Signed-off-by: Dmitriy Zaporozhets --- app/finders/milestones_finder.rb | 1 - spec/services/milestones/close_service_spec.rb | 28 +++++++++++++++++++++++++ spec/services/milestones/create_service_spec.rb | 24 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 spec/services/milestones/close_service_spec.rb create mode 100644 spec/services/milestones/create_service_spec.rb diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb index 71f207ca030..b704e878903 100644 --- a/app/finders/milestones_finder.rb +++ b/app/finders/milestones_finder.rb @@ -10,4 +10,3 @@ class MilestonesFinder end end end - diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb new file mode 100644 index 00000000000..034c0f22e12 --- /dev/null +++ b/spec/services/milestones/close_service_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Milestones::CloseService do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) } + + before do + project.team << [user, :master] + end + + describe :execute do + before do + Milestones::CloseService.new(project, user, {}).execute(milestone) + end + + it { expect(milestone).to be_valid } + it { expect(milestone).to be_closed } + + describe :event do + let(:event) { Event.first } + + it { expect(event.milestone).to be_truthy } + it { expect(event.target).to eq(milestone) } + it { expect(event.action_name).to eq('closed') } + end + end +end diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb new file mode 100644 index 00000000000..757c9a226d8 --- /dev/null +++ b/spec/services/milestones/create_service_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe Milestones::CreateService do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + + describe :execute do + context "valid params" do + before do + project.team << [user, :master] + + opts = { + title: 'v2.1.9', + description: 'Patch release to fix security issue' + } + + @milestone = Milestones::CreateService.new(project, user, opts).execute + end + + it { expect(@milestone).to be_valid } + it { expect(@milestone.title).to eq('v2.1.9') } + end + end +end -- cgit v1.2.1 From 8630d476e419ba524cb5f23e25772bcf2223195d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 14:45:20 +0100 Subject: Add header and page title to new milestone page Signed-off-by: Dmitriy Zaporozhets --- app/views/groups/milestones/new.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 4c490d8ccb3..d4284af8a06 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -1,3 +1,6 @@ +- page_title "Milestones" +- header_title group_title(@group, "Milestones", group_milestones_path(@group)) + %h3.page-title New Milestone -- cgit v1.2.1 From f16f315115e732493d976a9c866af351253ecf61 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 15:07:16 +0100 Subject: Few changes to Group Milestone feature: * Group attachments are not supported so I removed attach file link * Added changelog item * Add markdown hint to project milestone form Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/assets/javascripts/dispatcher.js.coffee | 2 ++ app/views/groups/milestones/new.html.haml | 3 +-- app/views/projects/milestones/_form.html.haml | 6 ++---- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index dd4306ed0c0..27ce7f36689 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ v 8.2.0 (unreleased) - Relative links from a repositories README.md now link to the default branch - Fix trailing whitespace issue in merge request/issue title - Fix bug when milestone/label filter was empty for dashboard issues page + - Add ability to create milestone in group projects from single form v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 951173af5d5..4059fc39c67 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -28,6 +28,8 @@ class Dispatcher when 'projects:milestones:new', 'projects:milestones:edit' new ZenMode() new DropzoneInput($('.milestone-form')) + when 'groups:milestones:new' + new ZenMode() when 'projects:compare:show' new Diff() when 'projects:issues:new','projects:issues:edit' diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index d4284af8a06..800bac4ef02 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -21,7 +21,6 @@ .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 js-quick-submit' - = render 'projects/notes/hints' .clearfix .error-alert .form-group @@ -43,7 +42,7 @@ :javascript - $( ".datepicker" ).datepicker({ + $(".datepicker").datepicker({ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 255ddab479f..24879b19d2b 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -23,9 +23,7 @@ .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 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' }. + = render 'projects/notes/hints' .clearfix .error-alert .col-md-6 @@ -45,7 +43,7 @@ :javascript - $( ".datepicker" ).datepicker({ + $(".datepicker").datepicker({ dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) } }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val())); -- cgit v1.2.1 From f445d3d1f317d8addbe60cf31fa15ca592ba0d19 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 16 Nov 2015 15:36:14 +0100 Subject: unique is for indexes. --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index a8e8dfe6bbf..4870fe3fc4c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -423,7 +423,7 @@ ActiveRecord::Schema.define(version: 20151114113410) do add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree create_table "lfs_objects", force: true do |t| - t.string "oid", null: false, unique: true + t.string "oid", null: false t.integer "size", null: false t.datetime "created_at" t.datetime "updated_at" -- cgit v1.2.1 From 78d542fcd811b6e18209fb97f126fd6291bd411b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 15:56:50 +0100 Subject: Add milestones documentation to workflow Signed-off-by: Dmitriy Zaporozhets --- doc/workflow/README.md | 1 + doc/workflow/milestones.md | 13 +++++++++++++ doc/workflow/milestones/form.png | Bin 0 -> 88591 bytes doc/workflow/milestones/group_form.png | Bin 0 -> 77087 bytes 4 files changed, 14 insertions(+) create mode 100644 doc/workflow/milestones.md create mode 100644 doc/workflow/milestones/form.png create mode 100644 doc/workflow/milestones/group_form.png diff --git a/doc/workflow/README.md b/doc/workflow/README.md index a2653704c33..c1a4f64981f 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -14,5 +14,6 @@ - [Protected branches](protected_branches.md) - [Web Editor](web_editor.md) - [Releases](releases.md) +- [Milestones](milestones.md) - [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) diff --git a/doc/workflow/milestones.md b/doc/workflow/milestones.md new file mode 100644 index 00000000000..1cd1f0f2fc3 --- /dev/null +++ b/doc/workflow/milestones.md @@ -0,0 +1,13 @@ +# Milestones + +Milestone allows you to group issues and set due date for it. +Milestone is created per project. + +![milestone form](milestones/form.png) + +## Groups and milestones + +You can create milestone with single form for several projects that belongs to the same group. +On the group milestones page you will be able to see this milestones grouped together by name. + +![group milestone form](milestones/group_form.png) diff --git a/doc/workflow/milestones/form.png b/doc/workflow/milestones/form.png new file mode 100644 index 00000000000..de44c1ffc1a Binary files /dev/null and b/doc/workflow/milestones/form.png differ diff --git a/doc/workflow/milestones/group_form.png b/doc/workflow/milestones/group_form.png new file mode 100644 index 00000000000..38862dcca68 Binary files /dev/null and b/doc/workflow/milestones/group_form.png differ -- cgit v1.2.1 From 84c71e5d827f1a0b225b7f6e9eeabe00a9c3eadd Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 16 Nov 2015 15:59:15 +0100 Subject: Add unique to oid index for lfs object. --- db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb | 7 +++++++ db/schema.rb | 5 ++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb diff --git a/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb new file mode 100644 index 00000000000..41b93da0a86 --- /dev/null +++ b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb @@ -0,0 +1,7 @@ +class AddUniqueForLfsOidIndex < ActiveRecord::Migration + def change + remove_index :lfs_objects, :oid + remove_index :lfs_objects, [:oid, :size] + add_index :lfs_objects, :oid, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 4870fe3fc4c..aa76cef9fe4 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: 20151114113410) do +ActiveRecord::Schema.define(version: 20151116144118) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -430,8 +430,7 @@ ActiveRecord::Schema.define(version: 20151114113410) do t.string "file" end - add_index "lfs_objects", ["oid", "size"], name: "index_lfs_objects_on_oid_and_size", using: :btree - add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", using: :btree + add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree create_table "lfs_objects_projects", force: true do |t| t.integer "lfs_object_id", null: false -- cgit v1.2.1 From 929ab909c88e9ac5d87acacb376a39dcfa6a639c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 16:14:19 +0100 Subject: Group masters should be able to create/close milestones Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups/milestones_controller.rb | 2 +- app/models/ability.rb | 1 + app/views/groups/milestones/_milestone.html.haml | 2 +- app/views/groups/milestones/index.html.haml | 9 +++++---- app/views/groups/milestones/show.html.haml | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 6833a550c9e..10233222ee1 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -38,7 +38,7 @@ class Groups::MilestonesController < Groups::ApplicationController private def authorize_group_milestone! - return render_404 unless can?(current_user, :admin_group, group) + return render_404 unless can?(current_user, :admin_milestones, group) end def milestone_params diff --git a/app/models/ability.rb b/app/models/ability.rb index 5ae28d5133e..d01b3ae6f05 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -233,6 +233,7 @@ class Ability if group.has_master?(user) || group.has_owner?(user) || user.admin? rules.push(*[ :create_projects, + :admin_milestones ]) end diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml index 41dffdd2fb8..a20bf75bc39 100644 --- a/app/views/groups/milestones/_milestone.html.haml +++ b/app/views/groups/milestones/_milestone.html.haml @@ -22,7 +22,7 @@ %span.label.label-gray = milestone.project.name .col-sm-6 - - if can?(current_user, :admin_group, @group) + - if can?(current_user, :admin_milestones, @group) - if milestone.closed? = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen" - else diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index ffd7dd3fc0b..84ec77c6188 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -3,10 +3,11 @@ = render 'shared/milestones_filter' .gray-content-block - .pull-right - %span.pull-right.hidden-xs - = link_to new_group_milestone_path(@group), class: "btn btn-new" do - New Milestone + - if can?(current_user, :admin_milestones, @group) + .pull-right + %span.pull-right.hidden-xs + = link_to new_group_milestone_path(@group), class: "btn btn-new" do + New Milestone .oneline Only milestones from diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index e609abca08e..716e93f558b 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -9,7 +9,7 @@ Open Milestone #{@milestone.title} .pull-right - - if can?(current_user, :admin_group, @group) + - if can?(current_user, :admin_milestones, @group) - if @milestone.active? = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close" - else -- cgit v1.2.1 From 32f1a7196817b1073327c607905ee40b9140e6df Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 17:24:14 +0100 Subject: Fix removing avatar for group Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups/avatars_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index f390705dc6a..76c87366baa 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -1,4 +1,4 @@ -class Groups::AvatarsController < ApplicationController +class Groups::AvatarsController < Groups::ApplicationController def destroy @group.remove_avatar! @group.save -- cgit v1.2.1 From c8e53d4467e1e8cce4db04aafba00d55f014e283 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 15 Nov 2015 15:30:05 -0500 Subject: Revert "Merge pull request #9820 from huacnlee/avoid-render-form-in-notes-list" This reverts commit 63144cd062f6d259f1f30b6e06eb92a16caa8dec, reversing changes made to 8ab5df9d872414b2cca3ebd16d57b89e2f19e06a. --- app/assets/javascripts/notes.js.coffee | 13 +++++++------ app/controllers/projects/notes_controller.rb | 7 +------ app/views/projects/notes/_note.html.haml | 5 ++++- app/views/projects/notes/_notes_with_form.html.haml | 2 +- app/views/projects/notes/edit.js.erb | 2 -- config/routes.rb | 2 +- features/steps/project/issues/issues.rb | 2 +- spec/features/notes_on_merge_requests_spec.rb | 6 ++++++ spec/features/task_lists_spec.rb | 3 +++ 9 files changed, 24 insertions(+), 18 deletions(-) delete mode 100644 app/views/projects/notes/edit.js.erb diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b0682f16845..ea75c656bcc 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -29,6 +29,7 @@ class @Notes $(document).on "ajax:success", "form.edit_note", @updateNote # Edit note link + $(document).on "click", ".js-note-edit", @showEditForm $(document).on "click", ".note-edit-cancel", @cancelEdit # Reopen and close actions for Issue/MR combined with note form submit @@ -66,6 +67,7 @@ class @Notes $(document).off "ajax:success", ".js-main-target-form" $(document).off "ajax:success", ".js-discussion-note-form" $(document).off "ajax:success", "form.edit_note" + $(document).off "click", ".js-note-edit" $(document).off "click", ".note-edit-cancel" $(document).off "click", ".js-note-delete" $(document).off "click", ".js-note-attachment-delete" @@ -285,14 +287,13 @@ class @Notes Adds a hidden div with the original content of the note to fill the edit note form with if the user cancels ### - showEditForm: (note, formHTML) -> - nodeText = note.find(".note-text"); - nodeText.hide() - note.find('.note-edit-form').remove() - nodeText.after(formHTML) + showEditForm: (e) -> + e.preventDefault() + note = $(this).closest(".note") note.find(".note-body > .note-text").hide() note.find(".note-header").hide() - form = note.find(".note-edit-form") + base_form = note.find(".note-edit-form") + form = base_form.clone().insertAfter(base_form) form.addClass('current-note-edit-form gfm-form') form.find('.div-dropzone').remove() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 0c98e2f1bfd..41cd08c93c6 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :edit, :delete_attachment] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment] def index current_fetched_at = Time.now.to_i @@ -29,11 +29,6 @@ class Projects::NotesController < Projects::ApplicationController end end - def edit - @note = note - render layout: false - end - def update @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 8a3292f7daf..88808301985 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -7,7 +7,7 @@ .note-header - if note_editable?(note) .note-actions - = link_to edit_namespace_project_note_path(note.project.namespace, note.project, note), title: 'Edit comment', remote: true, class: 'js-note-edit' do + = link_to '#', title: 'Edit comment', class: 'js-note-edit' do = icon('pencil-square-o') = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'js-note-delete danger' do @@ -59,6 +59,9 @@ .note-text = preserve do = markdown(note.note, {no_header_anchors: true}) + - unless note.system? + -# System notes can't be edited + = render 'projects/notes/edit_form', note: note - if note.attachment.url .note-attachment diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 91cefa6d14d..04222b8f7c4 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -7,4 +7,4 @@ = render "projects/notes/form", view: params[:view] :javascript - window._notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}") + new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}") diff --git a/app/views/projects/notes/edit.js.erb b/app/views/projects/notes/edit.js.erb deleted file mode 100644 index 2599bad5d6e..00000000000 --- a/app/views/projects/notes/edit.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$note = $('.note-row-<%= @note.id %>:visible'); -_notes.showEditForm($note, '<%= escape_javascript(render('edit_form', note: @note)) %>'); diff --git a/config/routes.rb b/config/routes.rb index bd85f4e3c69..696136d964c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -660,7 +660,7 @@ Gitlab::Application.routes.draw do end end - resources :notes, constraints: { id: /\d+/ } do + resources :notes, only: [:index, :create, :destroy, :update], constraints: { id: /\d+/ } do member do delete :delete_attachment end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index be134b9c2bb..af2da41badb 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -219,7 +219,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'The code block should be unchanged' do - expect(page).to have_content("Command [1]: /usr/local/bin/git , see [text](doc/text)") + expect(page).to have_content("```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```") end step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index 16d5a03e88c..d7cb3b2e86e 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -65,6 +65,12 @@ describe 'Comments', feature: true do end describe 'when editing a note', js: true do + it 'should contain the hidden edit form' do + page.within("#note_#{note.id}") do + is_expected.to have_css('.note-edit-form', visible: false) + end + end + describe 'editing the note' do before do find('.note').hover diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb index 6cbe685a93b..fca3c77fc64 100644 --- a/spec/features/task_lists_spec.rb +++ b/spec/features/task_lists_spec.rb @@ -51,6 +51,7 @@ feature 'Task Lists', feature: true do expect(page).to have_selector(container) expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") + expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector('form.js-issuable-update') expect(page).to have_selector('a.btn-close') end @@ -89,6 +90,7 @@ feature 'Task Lists', feature: true do expect(page).to have_selector('.note .js-task-list-container') expect(page).to have_selector('.note .js-task-list-container .task-list .task-list-item .task-list-item-checkbox') + expect(page).to have_selector('.note .js-task-list-container .js-task-list-field') end it 'is only editable by author' do @@ -125,6 +127,7 @@ feature 'Task Lists', feature: true do expect(page).to have_selector(container) expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox") + expect(page).to have_selector("#{container} .js-task-list-field") expect(page).to have_selector('form.js-issuable-update') expect(page).to have_selector('a.btn-close') end -- cgit v1.2.1 From ccb0c40c54d913fe140231c88f4adcd2d41c5b87 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 15 Nov 2015 14:32:28 -0500 Subject: Make ProjectWiki touch Project#last_activity_at after wiki actions Closes #3026 --- app/models/project_wiki.rb | 10 ++++++++++ spec/models/project_wiki_spec.rb | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 231973fa543..b5fec38378b 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -86,6 +86,8 @@ class ProjectWiki commit = commit_details(:created, message, title) wiki.write_page(title, format, content, commit) + + update_project_activity rescue Gollum::DuplicatePageError => e @error_message = "Duplicate page: #{e.message}" return false @@ -95,10 +97,14 @@ class ProjectWiki commit = commit_details(:updated, message, page.title) wiki.update_page(page, page.name, format, content, commit) + + update_project_activity end def delete_page(page, message = nil) wiki.delete_page(page, commit_details(:deleted, message, page.title)) + + update_project_activity end def page_title_and_dir(title) @@ -146,4 +152,8 @@ class ProjectWiki def path_to_repo @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git") end + + def update_project_activity + @project.touch(:last_activity_at) + end end diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index 9f6cdeeaa96..3b889144447 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -184,6 +184,12 @@ describe ProjectWiki do subject.create_page("test page", "some content", :markdown, "commit message") expect(subject.pages.first.page.version.message).to eq("commit message") end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.create_page('Test Page', 'This is content') + end end describe "#update_page" do @@ -205,6 +211,12 @@ describe ProjectWiki do it "sets the correct commit message" do expect(@page.version.message).to eq("updated page") end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.update_page(@gollum_page, 'Yet more content', :markdown, 'Updated page again') + end end describe "#delete_page" do @@ -217,6 +229,12 @@ describe ProjectWiki do subject.delete_page(@page) expect(subject.pages.count).to eq(0) end + + it 'updates project activity' do + expect(subject).to receive(:update_project_activity) + + subject.delete_page(@page) + end end private -- cgit v1.2.1 From b093f50986b6dcd0e4caf33d3c96831155e71db8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 19:55:58 +0100 Subject: Some code and doc improvements Signed-off-by: Dmitriy Zaporozhets --- app/models/global_milestone.rb | 4 ++++ app/views/dashboard/milestones/show.html.haml | 2 +- app/views/groups/milestones/show.html.haml | 2 +- doc/workflow/milestones.md | 8 ++++---- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index f96e9d41c94..1321ccd963f 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -94,4 +94,8 @@ class GlobalMilestone def closed_merge_requests merge_requests.values_at("closed", "merged", "locked").compact.flatten end + + def complete? + total_items_count == closed_items_count + end end diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 580db613ed4..83077a398bd 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -8,7 +8,7 @@ Milestone #{@milestone.title} %hr -- if (@milestone.total_items_count == @milestone.closed_items_count) && @milestone.active? +- if @milestone.complete? && @milestone.active? .alert.alert-success %span All issues for this milestone are closed. You may close the milestone now. diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 716e93f558b..d161259e4aa 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -16,7 +16,7 @@ = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" %hr -- if (@milestone.total_items_count == @milestone.closed_items_count) && @milestone.active? +- if @milestone.complete? && @milestone.active? .alert.alert-success %span All issues for this milestone are closed. You may close the milestone now. diff --git a/doc/workflow/milestones.md b/doc/workflow/milestones.md index 1cd1f0f2fc3..dff36899aec 100644 --- a/doc/workflow/milestones.md +++ b/doc/workflow/milestones.md @@ -1,13 +1,13 @@ # Milestones -Milestone allows you to group issues and set due date for it. -Milestone is created per project. +Milestones allow you to organize issues and merge requests into a cohesive group, optionally setting a due date. +A common use is keeping track of an upcoming software version. Milestones are created per-project. ![milestone form](milestones/form.png) ## Groups and milestones -You can create milestone with single form for several projects that belongs to the same group. -On the group milestones page you will be able to see this milestones grouped together by name. +You can create a milestone for several projects in the same group simultaneously. +On the group's milestones page, you will be able to see the status of that milestone across all of the selected projects. ![group milestone form](milestones/group_form.png) -- cgit v1.2.1 From da26fd3fc2673b65a75b7cfac2925d1abb0d5f9a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 20:22:14 +0100 Subject: Dont allow code duplication check to fail Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 141e7ba41de..94753093540 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,4 +87,3 @@ flay: tags: - ruby - mysql - allow_failure: true -- cgit v1.2.1 From adec8c7768152c81a40dd3a9fff77ea8525438c4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 20:40:57 +0100 Subject: Refactor select2 tags Signed-off-by: Dmitriy Zaporozhets --- app/helpers/namespaces_helper.rb | 9 --------- app/helpers/selects_helper.rb | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index b3132a1f3ba..e7f3cb21038 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -17,15 +17,6 @@ module NamespacesHelper grouped_options_for_select(options, selected) end - def namespace_select_tag(id, opts = {}) - css_class = "ajax-namespace-select " - css_class << "multiselect " if opts[:multiple] - css_class << (opts[:class] || '') - value = opts[:selected] || '' - - hidden_field_tag(id, value, class: css_class) - end - def namespace_icon(namespace, size = 40) if namespace.kind_of?(Group) group_icon(namespace) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 12fce8db701..7e54d4d1b5b 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -35,8 +35,20 @@ module SelectsHelper end def groups_select_tag(id, opts = {}) - css_class = "ajax-groups-select " - css_class << "multiselect " if opts[:multiple] + opts[:class] ||= '' + opts[:class] << ' ajax-groups-select' + select2_tag(id, opts) + end + + def namespace_select_tag(id, opts = {}) + opts[:class] ||= '' + opts[:class] << ' ajax-namespace-select' + select2_tag(id, opts) + end + + def select2_tag(id, opts = {}) + css_class = '' + css_class << 'multiselect ' if opts[:multiple] css_class << (opts[:class] || '') value = opts[:selected] || '' -- cgit v1.2.1 From 79fa6c646cecf887e6de8f2739d958ae49bb6eb3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 21:56:28 +0100 Subject: Remove duplication in reference filters Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/markdown/abstract_reference_filter.rb | 100 +++++++++++++++++++++ lib/gitlab/markdown/issue_reference_filter.rb | 63 ++----------- .../markdown/merge_request_reference_filter.rb | 61 ++----------- lib/gitlab/markdown/snippet_reference_filter.rb | 61 ++----------- 4 files changed, 119 insertions(+), 166 deletions(-) create mode 100644 lib/gitlab/markdown/abstract_reference_filter.rb diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb new file mode 100644 index 00000000000..fd5b7eb9332 --- /dev/null +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -0,0 +1,100 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering. + # All this functionality moved to this class + class AbstractReferenceFilter < ReferenceFilter + include CrossProjectReference + + def self.object_class + # Implement in child class + # Example: MergeRequest + end + + def self.object_name + object_class.name.underscore + end + + def self.object_sym + object_name.to_sym + end + + def self.data_reference + "data-#{object_name.dasherize}" + end + + # Public: Find references in text (like `!123` for merge requests) + # + # AnyReferenceFilter.references_in(text) do |match, object| + # "PREFIX#{object}" + # end + # + # PREFIX - symbol that detects reference (like ! for merge requests) + # object - reference object (snippet, merget request etc) + # text - String text to search. + # + # Yields the String match, the Integer referenced object ID, and an optional String + # of the external project reference. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(object_class.reference_pattern) do |match| + yield match, $~[object_sym].to_i, $~[:project] + end + end + + def self.referenced_by(node) + { object_sym => LazyReference.new(object_class, node.attr(data_reference)) } + end + + delegate :object_class, :object_sym, :references_in, to: :class + + def find_object(project, id) + # Implement in child class + # Example: project.merge_requests.find + end + + def url_for_object(object, project) + # Implement in child class + # Example: project_merge_request_url + end + + def call + replace_text_nodes_matching(object_class.reference_pattern) do |content| + object_link_filter(content) + end + end + + # Replace references (like `!123` for merge requests) in text with links + # to the referenced object's details page. + # + # text - String text to replace references in. + # + # Returns a String with references replaced with links. All links + # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. + def object_link_filter(text) + references_in(text) do |match, id, project_ref| + project = project_from_ref(project_ref) + + if project && object = find_object(project, id) + title = escape_once("#{object_title}: #{object.title}") + klass = reference_class(object_sym) + data = data_attribute(project: project.id, object_sym => object.id) + url = url_for_object(object, project) + + %(#{match}) + else + match + end + end + end + + def object_title + object_class.name.titleize + end + end + end +end diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb index 481d282f7b1..1ed69e2f431 100644 --- a/lib/gitlab/markdown/issue_reference_filter.rb +++ b/lib/gitlab/markdown/issue_reference_filter.rb @@ -6,66 +6,17 @@ module Gitlab # issues that do not exist are ignored. # # This filter supports cross-project references. - class IssueReferenceFilter < ReferenceFilter - include CrossProjectReference - - # Public: Find `#123` issue references in text - # - # IssueReferenceFilter.references_in(text) do |match, issue, project_ref| - # "##{issue}" - # end - # - # text - String text to search. - # - # Yields the String match, the Integer issue ID, and an optional String of - # the external project reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Issue.reference_pattern) do |match| - yield match, $~[:issue].to_i, $~[:project] - end - end - - def self.referenced_by(node) - { issue: LazyReference.new(Issue, node.attr("data-issue")) } - end - - def call - replace_text_nodes_matching(Issue.reference_pattern) do |content| - issue_link_filter(content) - end + class IssueReferenceFilter < AbstractReferenceFilter + def self.object_class + Issue end - # Replace `#123` issue references in text with links to the referenced - # issue's details page. - # - # text - String text to replace references in. - # - # Returns a String with `#123` references replaced with links. All links - # have `gfm` and `gfm-issue` class names attached for styling. - def issue_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if project && issue = project.get_issue(id) - url = url_for_issue(id, project, only_path: context[:only_path]) - - title = escape_once("Issue: #{issue.title}") - klass = reference_class(:issue) - data = data_attribute(project: project.id, issue: issue.id) - - %(#{match}) - else - match - end - end + def find_object(project, id) + project.get_issue(id) end - def url_for_issue(*args) - IssuesHelper.url_for_issue(*args) + def url_for_object(issue, project) + IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) end end end diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 5bc63269808..1f47f03c94e 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -6,65 +6,16 @@ module Gitlab # to merge requests that do not exist are ignored. # # This filter supports cross-project references. - class MergeRequestReferenceFilter < ReferenceFilter - include CrossProjectReference - - # Public: Find `!123` merge request references in text - # - # MergeRequestReferenceFilter.references_in(text) do |match, merge_request, project_ref| - # "##{merge_request}" - # end - # - # text - String text to search. - # - # Yields the String match, the Integer merge request ID, and an optional - # String of the external project reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(MergeRequest.reference_pattern) do |match| - yield match, $~[:merge_request].to_i, $~[:project] - end - end - - def self.referenced_by(node) - { merge_request: LazyReference.new(MergeRequest, node.attr("data-merge-request")) } - end - - def call - replace_text_nodes_matching(MergeRequest.reference_pattern) do |content| - merge_request_link_filter(content) - end + class MergeRequestReferenceFilter < AbstractReferenceFilter + def self.object_class + MergeRequest end - # Replace `!123` merge request references in text with links to the - # referenced merge request's details page. - # - # text - String text to replace references in. - # - # Returns a String with `!123` references replaced with links. All links - # have `gfm` and `gfm-merge_request` class names attached for styling. - def merge_request_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if project && merge_request = project.merge_requests.find_by(iid: id) - title = escape_once("Merge Request: #{merge_request.title}") - klass = reference_class(:merge_request) - data = data_attribute(project: project.id, merge_request: merge_request.id) - - url = url_for_merge_request(merge_request, project) - - %(#{match}) - else - match - end - end + def find_object(project, id) + project.merge_requests.find_by(iid: id) end - def url_for_merge_request(mr, project) + def url_for_object(mr, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_merge_request_url(project.namespace, project, mr, only_path: context[:only_path]) diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb index f783f951711..f7bd07c2a34 100644 --- a/lib/gitlab/markdown/snippet_reference_filter.rb +++ b/lib/gitlab/markdown/snippet_reference_filter.rb @@ -6,65 +6,16 @@ module Gitlab # snippets that do not exist are ignored. # # This filter supports cross-project references. - class SnippetReferenceFilter < ReferenceFilter - include CrossProjectReference - - # Public: Find `$123` snippet references in text - # - # SnippetReferenceFilter.references_in(text) do |match, snippet| - # "$#{snippet}" - # end - # - # text - String text to search. - # - # Yields the String match, the Integer snippet ID, and an optional String - # of the external project reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Snippet.reference_pattern) do |match| - yield match, $~[:snippet].to_i, $~[:project] - end - end - - def self.referenced_by(node) - { snippet: LazyReference.new(Snippet, node.attr("data-snippet")) } - end - - def call - replace_text_nodes_matching(Snippet.reference_pattern) do |content| - snippet_link_filter(content) - end + class SnippetReferenceFilter < AbstractReferenceFilter + def self.object_class + Snippet end - # Replace `$123` snippet references in text with links to the referenced - # snippets's details page. - # - # text - String text to replace references in. - # - # Returns a String with `$123` references replaced with links. All links - # have `gfm` and `gfm-snippet` class names attached for styling. - def snippet_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if project && snippet = project.snippets.find_by(id: id) - title = escape_once("Snippet: #{snippet.title}") - klass = reference_class(:snippet) - data = data_attribute(project: project.id, snippet: snippet.id) - - url = url_for_snippet(snippet, project) - - %(#{match}) - else - match - end - end + def find_object(project, id) + project.snippets.find_by(id: id) end - def url_for_snippet(snippet, project) + def url_for_object(snippet, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_snippet_url(project.namespace, project, snippet, only_path: context[:only_path]) -- cgit v1.2.1 From 9573e06e265f7768923bfb7eff1e5fdc56d5b9bb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 23:06:28 +0100 Subject: Allow flay to fail for now since there is still a lot of refactoring todo Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94753093540..141e7ba41de 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,3 +87,4 @@ flay: tags: - ruby - mysql + allow_failure: true -- cgit v1.2.1 From bfbfa3b5f050405180b2024ff6a790bb71915606 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 16 Nov 2015 23:53:21 +0100 Subject: Remove duplication in issue emails Signed-off-by: Dmitriy Zaporozhets --- app/mailers/emails/issues.rb | 66 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 2c035fbb70b..11533bc53c6 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -1,53 +1,47 @@ module Emails module Issues def new_issue_email(recipient_id, issue_id) - @issue = Issue.find(issue_id) - @project = @issue.project - @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) - mail_new_thread(@issue, - from: sender(@issue.author_id), - to: recipient(recipient_id), - subject: subject("#{@issue.title} (##{@issue.iid})")) - - SentNotification.record(@issue, recipient_id, reply_key) + mail_with_notification(issue_id, recipient_id) do + mail_new_thread(@issue, thread_options(@issue.author_id, recipient_id)) + end end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id) - @issue = Issue.find(issue_id) - @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id - @project = @issue.project - @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) - mail_answer_thread(@issue, - from: sender(updated_by_user_id), - to: recipient(recipient_id), - subject: subject("#{@issue.title} (##{@issue.iid})")) - - SentNotification.record(@issue, recipient_id, reply_key) + mail_with_notification(issue_id, recipient_id) do + @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id + mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + end end def closed_issue_email(recipient_id, issue_id, updated_by_user_id) - @issue = Issue.find issue_id - @project = @issue.project - @updated_by = User.find updated_by_user_id - @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) - mail_answer_thread(@issue, - from: sender(updated_by_user_id), - to: recipient(recipient_id), - subject: subject("#{@issue.title} (##{@issue.iid})")) - - SentNotification.record(@issue, recipient_id, reply_key) + mail_with_notification(issue_id, recipient_id) do + @updated_by = User.find updated_by_user_id + mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + end end def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) - @issue = Issue.find issue_id - @issue_status = status + mail_with_notification(issue_id, recipient_id) do + @issue_status = status + @updated_by = User.find updated_by_user_id + mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + end + end + + def thread_options(sender_id, recipient_id) + { + from: sender(sender_id), + to: recipient(recipient_id), + subject: subject("#{@issue.title} (##{@issue.iid})") + } + end + + def mail_with_notification(issue_id, recipient_id) + @issue = Issue.find(issue_id) @project = @issue.project - @updated_by = User.find updated_by_user_id @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) - mail_answer_thread(@issue, - from: sender(updated_by_user_id), - to: recipient(recipient_id), - subject: subject("#{@issue.title} (##{@issue.iid})")) + + yield SentNotification.record(@issue, recipient_id, reply_key) end -- cgit v1.2.1 From 3300db70ff53699732672824859186cd083623fa Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Mon, 16 Nov 2015 02:01:26 -0800 Subject: Rewrite HTTP links to force TLS, where possible --- CONTRIBUTING.md | 8 ++++---- app/views/admin/users/_profile.html.haml | 4 ++-- app/views/users/show.html.haml | 4 ++-- doc/ci/docker/using_docker_build.md | 4 ++-- doc/ci/quick_start/README.md | 6 +++--- doc/customization/libravatar.md | 6 +++--- doc/development/architecture.md | 2 +- doc/hooks/custom_hooks.md | 2 +- doc/install/database_mysql.md | 2 +- doc/install/installation.md | 4 ++-- doc/integration/ldap.md | 4 ++-- doc/legal/corporate_contributor_license_agreement.md | 2 +- doc/legal/individual_contributor_license_agreement.md | 2 +- doc/markdown/markdown.md | 12 ++++++------ doc/operations/unicorn.md | 4 ++-- doc/release/security.md | 4 ++-- doc/ssh/README.md | 2 +- doc/update/6.x-or-7.x-to-7.14.md | 2 +- doc/workflow/gitlab_flow.md | 12 ++++++------ doc/workflow/importing/migrating_from_svn.md | 4 ++-- 20 files changed, 45 insertions(+), 45 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f79ff413a0..5d85c9f3fca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ By submitting code as an individual you agree to the [individual contributor lic ## Security vulnerability disclosure -Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. +Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. ## Closing policy for issues and merge requests @@ -35,7 +35,7 @@ The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple. -Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. +Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. ### Issue tracker guidelines @@ -72,7 +72,7 @@ If you can, please submit a merge request with the fix or improvements including 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code 1. Add your changes to the [CHANGELOG](CHANGELOG) 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message -1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) +1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) 1. Push the commit to your fork 1. Submit a merge request (MR) to the master branch 1. The MR title should describe the change you want to make @@ -181,4 +181,4 @@ This code of conduct applies both within project spaces and in public spaces whe Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com -This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) diff --git a/app/views/admin/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml index 90d9980c85c..7d11edc79e2 100644 --- a/app/views/admin/users/_profile.html.haml +++ b/app/views/admin/users/_profile.html.haml @@ -16,11 +16,11 @@ - unless user.linkedin.blank? %li %span.light LinkedIn: - %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}" + %strong= link_to user.linkedin, "https://www.linkedin.com/in/#{user.linkedin}" - unless user.twitter.blank? %li %span.light Twitter: - %strong= link_to user.twitter, "http://www.twitter.com/#{user.twitter}" + %strong= link_to user.twitter, "https://twitter.com/#{user.twitter}" - unless user.website_url.blank? %li %span.light Website: diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 30992412184..d5a92cb816a 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -32,11 +32,11 @@ = icon('skype') - unless @user.linkedin.blank? .profile-link-holder - = link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do + = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do = icon('linkedin-square') - unless @user.twitter.blank? .profile-link-holder - = link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do + = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do = icon('twitter-square') - unless @user.website_url.blank? .profile-link-holder diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 5af27470d2f..4b1788a9af0 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -35,7 +35,7 @@ GitLab Runner then executes build scripts as `gitlab-runner` user. ```bash $ sudo gitlab-runner register -n \ - --url http://gitlab.com/ci \ + --url https://gitlab.com/ci \ --token RUNNER_TOKEN \ --executor shell --description "My Runner" @@ -84,7 +84,7 @@ In order to do that follow the steps: ```bash $ sudo gitlab-runner register -n \ - --url http://gitlab.com/ci \ + --url https://gitlab.com/ci \ --token RUNNER_TOKEN \ --executor docker \ --description "My Docker Runner" \ diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index a87a1f806fc..d69064a91fd 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -6,7 +6,7 @@ To start building projects with GitLab CI a few steps needs to be done. First you need to have a working GitLab and GitLab CI instance. -You can omit this step if you use [GitLab.com](http://GitLab.com/). +You can omit this step if you use [GitLab.com](https://GitLab.com/). ## 2. Create repository on GitLab @@ -16,7 +16,7 @@ Push your application to that repository. ## 3. Add project to CI The next part is to login to GitLab CI. -Point your browser to the URL you have set GitLab or use [gitlab.com/ci](http://gitlab.com/ci/). +Point your browser to the URL you have set GitLab or use [gitlab.com/ci](https://gitlab.com/ci/). On the first screen you will see a list of GitLab's projects that you have access to: @@ -97,7 +97,7 @@ If you do it correctly your runner should be shown under **Runners activated for ### Shared runners -If you use [gitlab.com/ci](http://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc. +If you use [gitlab.com/ci](https://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc. These are special virtual machines that are run on GitLab's infrastructure that can build any project. To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project. diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md index 54c1780c3ab..bd2c242afc2 100644 --- a/doc/customization/libravatar.md +++ b/doc/customization/libravatar.md @@ -2,7 +2,7 @@ GitLab by default supports [Gravatar](https://gravatar.com) avatar service. Libravatar is a service which delivers your avatar (profile picture) to other websites and their API is -[heavily based on gravatar](http://wiki.libravatar.org/api/). +[heavily based on gravatar](https://wiki.libravatar.org/api/). This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server. @@ -31,7 +31,7 @@ the configuration options as follows: ## Self-hosted -If you are [running your own libravatar service](http://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration +If you are [running your own libravatar service](https://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration but the important part is to provide the same placeholders so GitLab can parse the URL correctly. For example, you host a service on `http://libravatar.example.com` the `plain_url` you need to supply in `gitlab.yml` is @@ -63,7 +63,7 @@ Run `sudo gitlab-ctl reconfigure` for changes to take effect. ## Default URL for missing images -[Libravatar supports different sets](http://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service. +[Libravatar supports different sets](https://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service. In order to use a different set other than `identicon`, replace `&d=identicon` portion of the URL with another supported set. For example, you can use `retro` set in which case the URL would look like: `plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=retro"` diff --git a/doc/development/architecture.md b/doc/development/architecture.md index c00d290371e..6101a71a8de 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -146,7 +146,7 @@ nginx Apache httpd -- [Explanation of Apache logs](http://httpd.apache.org/docs/2.2/logs.html). +- [Explanation of Apache logs](https://httpd.apache.org/docs/2.2/logs.html). - `/var/log/apache2/` contains error and output logs (on Ubuntu). - `/var/log/httpd/` contains error and output logs (on RHEL). diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md index 548c484bc08..0f2665a3bf7 100644 --- a/doc/hooks/custom_hooks.md +++ b/doc/hooks/custom_hooks.md @@ -7,7 +7,7 @@ Please explore webhooks as an option if you do not have filesystem access. For a Git natively supports hooks that are executed on different actions. Examples of server-side git hooks include pre-receive, post-receive, and update. See -[Git SCM Server-Side Hooks](http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks) +[Git SCM Server-Side Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks) for more information about each hook type. As of gitlab-shell version 2.2.0 (which requires GitLab 7.5+), GitLab diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index c565e90da2f..513ad69ec26 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -2,7 +2,7 @@ ## Note -We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](http://bugs.mysql.com/bug.php?id=65830) that [suggested](http://bugs.mysql.com/bug.php?id=50909) [fixes](http://bugs.mysql.com/bug.php?id=65830) [have](http://bugs.mysql.com/bug.php?id=63164). +We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). ## MySQL diff --git a/doc/install/installation.md b/doc/install/installation.md index 8028e51dbcd..193db10e4d3 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -106,7 +106,7 @@ Then select 'Internet Site' and press enter to confirm the hostname. ## 2. Ruby -The use of Ruby version managers such as [RVM](http://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby. +The use of Ruby version managers such as [RVM](https://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby. Remove the old Ruby 1.8 if present @@ -298,7 +298,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ### Install Gems -**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](http://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2. +**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](https://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2. # For PostgreSQL (note, the option says "without ... mysql") sudo -u git -H bundle install --deployment --without development test mysql aws kerberos diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index 9b7d8fa3969..7e2920b8865 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -71,7 +71,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server # Filter LDAP users # - # Format: RFC 4515 http://tools.ietf.org/search/rfc4515 + # Format: RFC 4515 https://tools.ietf.org/search/rfc4515 # Ex. (employeeType=developer) # # Note: GitLab does not support omniauth-ldap's custom filter syntax. @@ -145,7 +145,7 @@ If multiple LDAP email attributes are present, e.g. `mail: foo@bar.com` and `ema ## Using an LDAP filter to limit access to your GitLab server If you want to limit all GitLab access to a subset of the LDAP users on your LDAP server you can set up an LDAP user filter. -The filter must comply with [RFC 4515](http://tools.ietf.org/search/rfc4515). +The filter must comply with [RFC 4515](https://tools.ietf.org/search/rfc4515). ```ruby # For omnibus packages; new LDAP server syntax diff --git a/doc/legal/corporate_contributor_license_agreement.md b/doc/legal/corporate_contributor_license_agreement.md index 13bf15fcf45..7b94506c297 100644 --- a/doc/legal/corporate_contributor_license_agreement.md +++ b/doc/legal/corporate_contributor_license_agreement.md @@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and 8. It is your responsibility to notify GitLab B.V. when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab B.V.. -This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. +This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. diff --git a/doc/legal/individual_contributor_license_agreement.md b/doc/legal/individual_contributor_license_agreement.md index 72b01433dd0..f97c252fd7c 100644 --- a/doc/legal/individual_contributor_license_agreement.md +++ b/doc/legal/individual_contributor_license_agreement.md @@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and 8. You agree to notify GitLab B.V. of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. -This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. +This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md index ac3851f8c95..bc8e7d155e7 100644 --- a/doc/markdown/markdown.md +++ b/doc/markdown/markdown.md @@ -43,7 +43,7 @@ You can also use other rich text files in GitLab. You might have to install a de ## Newlines -GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p). +GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p). A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines. Line-breaks, or softreturns, are rendered if you end a line with two or more spaces @@ -72,14 +72,14 @@ do_this_and_do_that_and_another_thing GFM will autolink almost any URL you copy and paste into your text. - * http://www.google.com + * https://www.google.com * https://google.com/ * ftp://ftp.us.debian.org/debian/ * smb://foo/bar/baz * irc://irc.freenode.net/gitlab * http://localhost:3000 -* http://www.google.com +* https://www.google.com * https://google.com/ * ftp://ftp.us.debian.org/debian/ * smb://foo/bar/baz @@ -390,7 +390,7 @@ There are two ways to create links, inline-style and reference-style. [arbitrary case-insensitive reference text]: https://www.mozilla.org [1]: http://slashdot.org - [link text itself]: http://www.reddit.com + [link text itself]: https://www.reddit.com [I'm an inline-style link](https://www.google.com) @@ -406,7 +406,7 @@ Some text to show that the reference links can follow later. [arbitrary case-insensitive reference text]: https://www.mozilla.org [1]: http://slashdot.org -[link text itself]: http://www.reddit.com +[link text itself]: https://www.reddit.com **Note** @@ -583,5 +583,5 @@ By including colons in the header row, you can align the text within that column ## References - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet). -- The [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown. +- The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown. - [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown. diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md index 3998da01f01..bad61151bda 100644 --- a/doc/operations/unicorn.md +++ b/doc/operations/unicorn.md @@ -52,7 +52,7 @@ leak memory, probably because it does not handle user requests.) To make these memory leaks manageable, GitLab comes with the [unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This -gem [monkey-patches](http://en.wikipedia.org/wiki/Monkey_patch) the Unicorn +gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn workers to do a memory self-check after every 16 requests. If the memory of the Unicorn worker exceeds a pre-set limit then the worker process exits. The Unicorn master then automatically replaces the worker process. @@ -83,4 +83,4 @@ is a normal value for our current GitLab.com setup and traffic. The high frequency of Unicorn memory restarts on some GitLab sites can be a source of confusion for administrators. Usually they are a [red -herring](http://en.wikipedia.org/wiki/Red_herring). +herring](https://en.wikipedia.org/wiki/Red_herring). diff --git a/doc/release/security.md b/doc/release/security.md index 60bcfbb6da5..b1a62b333e6 100644 --- a/doc/release/security.md +++ b/doc/release/security.md @@ -8,7 +8,7 @@ Do a security release when there is a critical issue that needs to be addresses ## Security vulnerability disclosure -Please report suspected security vulnerabilities in private to , also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. +Please report suspected security vulnerabilities in private to , also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. ## Release Procedure @@ -25,7 +25,7 @@ Please report suspected security vulnerabilities in private to Date: Tue, 17 Nov 2015 10:40:29 +0100 Subject: Remove code duplication in gitlab_markdown_helper.rb Signed-off-by: Dmitriy Zaporozhets --- app/helpers/gitlab_markdown_helper.rb | 52 ++++++++++++++++------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 65813482120..a0f6b80e9eb 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -46,39 +46,13 @@ module GitlabMarkdownHelper end def markdown(text, context = {}) - return "" unless text.present? - - context.reverse_merge!( - path: @path, - pipeline: :default, - project: @project, - project_wiki: @project_wiki, - ref: @ref - ) - - user = current_user if defined?(current_user) - - html = Gitlab::Markdown.render(text, context) - Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user) + process_markdown(text, options) end # TODO (rspeicher): Remove all usages of this helper and just call `markdown` # with a custom pipeline depending on the content being rendered def gfm(text, options = {}) - return "" unless text.present? - - options.reverse_merge!( - path: @path, - pipeline: :default, - project: @project, - project_wiki: @project_wiki, - ref: @ref - ) - - user = current_user if defined?(current_user) - - html = Gitlab::Markdown.gfm(text, options) - Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user) + process_markdown(text, options, :gfm) end def asciidoc(text) @@ -204,4 +178,26 @@ module GitlabMarkdownHelper '' end end + + def process_markdown(text, options, method = :markdown) + return "" unless text.present? + + options.reverse_merge!( + path: @path, + pipeline: :default, + project: @project, + project_wiki: @project_wiki, + ref: @ref + ) + + user = current_user if defined?(current_user) + + html = if method == :gfm + Gitlab::Markdown.gfm(text, options) + else + Gitlab::Markdown.render(text, options) + end + + Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user) + end end -- cgit v1.2.1 From 437d4c76ece4bdf89e3702b05d13027a9a6f63a1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 10:49:23 +0100 Subject: Remove duplication in diff_helper.rb Signed-off-by: Dmitriy Zaporozhets --- app/helpers/diff_helper.rb | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b889fb28973..30e829cdd4e 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -132,25 +132,11 @@ module DiffHelper end def inline_diff_btn - params_copy = params.dup - params_copy[:view] = 'inline' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do - 'Inline' - end + diff_btn('Inline', 'inline', params[:view] != 'parallel') end def parallel_diff_btn - params_copy = params.dup - params_copy[:view] = 'parallel' - # Always use HTML to handle case where JSON diff rendered this button - params_copy.delete(:format) - - link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do - 'Side-by-side' - end + diff_btn('Side-by-side', 'parallel', params[:view] == 'parallel') end def submodule_link(blob, ref, repository = @repository) @@ -187,4 +173,18 @@ module DiffHelper def editable_diff?(diff) !diff.deleted_file && @merge_request && @merge_request.source_project end + + private + + def diff_btn(title, name, selected) + params_copy = params.dup + params_copy[:view] = name + + # Always use HTML to handle case where JSON diff rendered this button + params_copy.delete(:format) + + link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn') do + title + end + end end -- cgit v1.2.1 From 3cebe9e78064030553e62939ec3612993c63ad76 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 11:03:18 +0100 Subject: Refactor duplciate code for groups_controller.rb and slack_service/note_message.rb Signed-off-by: Dmitriy Zaporozhets --- app/controllers/concerns/issues_action.rb | 14 ++++++++++ app/controllers/concerns/merge_requests_action.rb | 9 +++++++ app/controllers/dashboard_controller.rb | 20 +++----------- app/controllers/groups_controller.rb | 20 +++----------- app/helpers/gitlab_markdown_helper.rb | 2 +- .../project_services/slack_service/note_message.rb | 31 +++++++++++----------- 6 files changed, 46 insertions(+), 50 deletions(-) create mode 100644 app/controllers/concerns/issues_action.rb create mode 100644 app/controllers/concerns/merge_requests_action.rb diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb new file mode 100644 index 00000000000..effd4721949 --- /dev/null +++ b/app/controllers/concerns/issues_action.rb @@ -0,0 +1,14 @@ +module IssuesAction + extend ActiveSupport::Concern + + def issues + @issues = get_issues_collection + @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE) + @issues = @issues.preload(:author, :project) + + respond_to do |format| + format.html + format.atom { render layout: false } + end + end +end diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb new file mode 100644 index 00000000000..f7a25111db9 --- /dev/null +++ b/app/controllers/concerns/merge_requests_action.rb @@ -0,0 +1,9 @@ +module MergeRequestsAction + extend ActiveSupport::Concern + + def merge_requests + @merge_requests = get_merge_requests_collection + @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE) + @merge_requests = @merge_requests.preload(:author, :target_project) + end +end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index b2c1fa4230c..087da935087 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -1,26 +1,12 @@ class DashboardController < Dashboard::ApplicationController + include IssuesAction + include MergeRequestsAction + before_action :event_filter, only: :activity before_action :projects, only: [:issues, :merge_requests] respond_to :html - def merge_requests - @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) - @merge_requests = @merge_requests.preload(:author, :target_project) - end - - def issues - @issues = get_issues_collection - @issues = @issues.page(params[:page]).per(PER_PAGE) - @issues = @issues.preload(:author, :project) - - respond_to do |format| - format.html - format.atom { render layout: false } - end - end - def activity @last_push = current_user.recent_push diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index fb4eb094f27..fb26a4e6fc3 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,4 +1,7 @@ class GroupsController < Groups::ApplicationController + include IssuesAction + include MergeRequestsAction + skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests] respond_to :html before_action :group, except: [:new, :create] @@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController end end - def merge_requests - @merge_requests = get_merge_requests_collection - @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) - @merge_requests = @merge_requests.preload(:author, :target_project) - end - - def issues - @issues = get_issues_collection - @issues = @issues.page(params[:page]).per(PER_PAGE) - @issues = @issues.preload(:author, :project) - - respond_to do |format| - format.html - format.atom { render layout: false } - end - end - def edit end diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index a0f6b80e9eb..98c6d9d5d2e 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -46,7 +46,7 @@ module GitlabMarkdownHelper end def markdown(text, context = {}) - process_markdown(text, options) + process_markdown(text, context) end # TODO (rspeicher): Remove all usages of this helper and just call `markdown` diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb index 074478b292d..b15d9a14677 100644 --- a/app/models/project_services/slack_service/note_message.rb +++ b/app/models/project_services/slack_service/note_message.rb @@ -45,30 +45,27 @@ class SlackService def create_commit_note(commit) commit_sha = commit[:id] commit_sha = Commit.truncate_sha(commit_sha) - commit_link = "[commit #{commit_sha}](#{@note_url})" - title = format_title(commit[:message]) - @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*" + commented_on_message( + "[commit #{commit_sha}](#{@note_url})", + format_title(commit[:message])) end def create_issue_note(issue) - issue_iid = issue[:iid] - note_link = "[issue ##{issue_iid}](#{@note_url})" - title = format_title(issue[:title]) - @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*" + commented_on_message( + "[issue ##{issue[:iid]}](#{@note_url})", + format_title(issue[:title])) end def create_merge_note(merge_request) - merge_request_id = merge_request[:iid] - merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})" - title = format_title(merge_request[:title]) - @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*" + commented_on_message( + "[merge request ##{merge_request[:iid]}](#{@note_url})", + format_title(merge_request[:title])) end def create_snippet_note(snippet) - snippet_id = snippet[:id] - snippet_link = "[snippet ##{snippet_id}](#{@note_url})" - title = format_title(snippet[:title]) - @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*" + commented_on_message( + "[snippet ##{snippet[:id]}](#{@note_url})", + format_title(snippet[:title])) end def description_message @@ -78,5 +75,9 @@ class SlackService def project_link "[#{@project_name}](#{@project_url})" end + + def commented_on_message(target_link, title) + @message = "#{@user_name} commented on #{target_link} in #{project_link}: *#{title}*" + end end end -- cgit v1.2.1 From 4747412a49baf2d4776833e962e82b8cc893a06a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 11:18:01 +0100 Subject: Set higher flay value to avoid unnecessary refactoring for now Signed-off-by: Dmitriy Zaporozhets --- lib/tasks/flay.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake index dfb9df4772a..e9587595fef 100644 --- a/lib/tasks/flay.rake +++ b/lib/tasks/flay.rake @@ -1,6 +1,6 @@ desc 'Code duplication analyze via flay' task :flay do - output = %x(bundle exec flay --mass 30 app/ lib/gitlab/) + output = %x(bundle exec flay --mass 35 app/ lib/gitlab/) if output.include? "Similar code found" puts output -- cgit v1.2.1 From 616675b4a6ef63abed2d133333fe5f5fbe1d73c6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 11:55:43 +0100 Subject: Remove duplication in mailers/emails/notes.rb Signed-off-by: Dmitriy Zaporozhets --- app/mailers/emails/issues.rb | 22 +++++++------ app/mailers/emails/notes.rb | 75 +++++++++++++++++++++++--------------------- 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 11533bc53c6..abdeefed5ef 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -1,34 +1,36 @@ module Emails module Issues def new_issue_email(recipient_id, issue_id) - mail_with_notification(issue_id, recipient_id) do - mail_new_thread(@issue, thread_options(@issue.author_id, recipient_id)) + issue_mail_with_notification(issue_id, recipient_id) do + mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id)) end end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id) - mail_with_notification(issue_id, recipient_id) do + issue_mail_with_notification(issue_id, recipient_id) do @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id - mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) end end def closed_issue_email(recipient_id, issue_id, updated_by_user_id) - mail_with_notification(issue_id, recipient_id) do + issue_mail_with_notification(issue_id, recipient_id) do @updated_by = User.find updated_by_user_id - mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) end end def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) - mail_with_notification(issue_id, recipient_id) do + issue_mail_with_notification(issue_id, recipient_id) do @issue_status = status @updated_by = User.find updated_by_user_id - mail_answer_thread(@issue, thread_options(updated_by_user_id, recipient_id)) + mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) end end - def thread_options(sender_id, recipient_id) + private + + def issue_thread_options(sender_id, recipient_id) { from: sender(sender_id), to: recipient(recipient_id), @@ -36,7 +38,7 @@ module Emails } end - def mail_with_notification(issue_id, recipient_id) + def issue_mail_with_notification(issue_id, recipient_id) @issue = Issue.find(issue_id) @project = @issue.project @target_url = namespace_project_issue_url(@project.namespace, @project, @issue) diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 87ba94a583d..65f37e92677 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -1,49 +1,54 @@ module Emails module Notes def note_commit_email(recipient_id, note_id) - @note = Note.find(note_id) - @commit = @note.noteable - @project = @note.project - @target_url = namespace_project_commit_url(@project.namespace, @project, - @commit, anchor: - "note_#{@note.id}") - mail_answer_thread(@commit, - from: sender(@note.author_id), - to: recipient(recipient_id), - subject: subject("#{@commit.title} (#{@commit.short_id})")) - - SentNotification.record_note(@note, recipient_id, reply_key) + note_mail_with_notification(note_id, recipient_id) do + @commit = @note.noteable + @target_url = namespace_project_commit_url(*note_target_url_options) + + mail_answer_thread(@commit, + from: sender(@note.author_id), + to: recipient(recipient_id), + subject: subject("#{@commit.title} (#{@commit.short_id})")) + end end def note_issue_email(recipient_id, note_id) - @note = Note.find(note_id) - @issue = @note.noteable - @project = @note.project - @target_url = namespace_project_issue_url(@project.namespace, @project, - @issue, anchor: - "note_#{@note.id}") - mail_answer_thread(@issue, - from: sender(@note.author_id), - to: recipient(recipient_id), - subject: subject("#{@issue.title} (##{@issue.iid})")) - - SentNotification.record_note(@note, recipient_id, reply_key) + note_mail_with_notification(note_id, recipient_id) do + @issue = @note.noteable + @target_url = namespace_project_issue_url(*note_target_url_options) + mail_answer_thread(@issue, note_thread_options(recipient_id)) + end end def note_merge_request_email(recipient_id, note_id) + note_mail_with_notification(note_id, recipient_id) do + @merge_request = @note.noteable + @target_url = namespace_project_merge_request_url(*note_target_url_options) + mail_answer_thread(@merge_request, note_thread_options(recipient_id)) + end + end + + private + + def note_target_url_options + [@project.namespace, @project, @note.noteable, anchor: "note_#{@note.id}"] + end + + def note_thread_options(recipient_id) + { + from: sender(@note.author_id), + to: recipient(recipient_id), + subject: subject("#{@note.noteable.title} (##{@note.noteable.iid})") + } + end + + def note_mail_with_notification(note_id, recipient_id) @note = Note.find(note_id) - @merge_request = @note.noteable @project = @note.project - @target_url = namespace_project_merge_request_url(@project.namespace, - @project, - @merge_request, anchor: - "note_#{@note.id}") - mail_answer_thread(@merge_request, - from: sender(@note.author_id), - to: recipient(recipient_id), - subject: subject("#{@merge_request.title} (##{@merge_request.iid})")) - - SentNotification.record_note(@note, recipient_id, reply_key) + + yield + + SentNotification.record(@note, recipient_id, reply_key) end end end -- cgit v1.2.1 From 9b0efdb71ea6e4f3a33db9959c63125cd50afebe Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 12:05:35 +0100 Subject: Remove code duplication in notification_service.rb Signed-off-by: Dmitriy Zaporozhets --- app/services/notification_service.rb | 46 ++++++++++++++---------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index a6b22348650..4b871f072d4 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -113,7 +113,7 @@ class NotificationService end # Add all users participating in the thread (author, assignee, comment authors) - participants = + participants = if target.respond_to?(:participants) target.participants(note.author) else @@ -276,35 +276,25 @@ class NotificationService # Remove users with disabled notifications from array # Also remove duplications and nil recipients def reject_muted_users(users, project = nil) - users = users.to_a.compact.uniq - users = users.reject(&:blocked?) - - users.reject do |user| - next user.notification.disabled? unless project - - member = project.project_members.find_by(user_id: user.id) - - if !member && project.group - member = project.group.group_members.find_by(user_id: user.id) - end - - # reject users who globally disabled notification and has no membership - next user.notification.disabled? unless member - - # reject users who disabled notification in project - next true if member.notification.disabled? - - # reject users who have N_GLOBAL in project and disabled in global settings - member.notification.global? && user.notification.disabled? - end + reject_users(users, :disabled?, project) end # Remove users with notification level 'Mentioned' def reject_mention_users(users, project = nil) + reject_users(users, :mention?, project) + end + + # Reject users which method_name from notification object returns true. + # + # Example: + # reject_users(users, :watch?, project) + # + def reject_users(users, method_name, project = nil) users = users.to_a.compact.uniq + users = users.reject(&:blocked?) users.reject do |user| - next user.notification.mention? unless project + next user.notification.send(method_name) unless project member = project.project_members.find_by(user_id: user.id) @@ -313,19 +303,19 @@ class NotificationService end # reject users who globally set mention notification and has no membership - next user.notification.mention? unless member + next user.notification.send(method_name) unless member # reject users who set mention notification in project - next true if member.notification.mention? + next true if member.notification.send(method_name) # reject users who have N_MENTION in project and disabled in global settings - member.notification.global? && user.notification.mention? + member.notification.global? && user.notification.send(method_name) end end def reject_unsubscribed_users(recipients, target) return recipients unless target.respond_to? :subscriptions - + recipients.reject do |user| subscription = target.subscriptions.find_by_user_id(user.id) subscription && !subscription.subscribed @@ -343,7 +333,7 @@ class NotificationService recipients end end - + def new_resource_email(target, project, method) recipients = build_recipients(target, project, target.author) -- cgit v1.2.1 From 4de50a866e89d83fce0264c1e0b689b638d9be95 Mon Sep 17 00:00:00 2001 From: Den Girnyk Date: Tue, 17 Nov 2015 13:08:11 +0200 Subject: Fix md syntax in doc/api/commits.md --- doc/api/commits.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/commits.md b/doc/api/commits.md index 8e4a0ee1b82..93d62b751e6 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -188,7 +188,7 @@ Parameters: "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"", + "started_at": "2015-10-12T09:47:16.250Z", "finished_at": "2015-10-12T09:47:16.262Z", "author": { "id": 1, @@ -228,7 +228,7 @@ POST /projects/:id/statuses/:sha "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"", + "started_at": "2015-10-12T09:47:16.250Z", "finished_at": "2015-10-12T09:47:16.262Z", "author": { "id": 1, -- cgit v1.2.1 From d27e400cb9ecf4423a0c713f4c3b084c154640b9 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 17 Nov 2015 12:39:06 +0100 Subject: LFS doc, first draft --- doc/workflow/git_lfs.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 doc/workflow/git_lfs.md diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md new file mode 100644 index 00000000000..6202822ea2d --- /dev/null +++ b/doc/workflow/git_lfs.md @@ -0,0 +1,127 @@ +# Git LFS + +Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. +The general recommendation is to not have Git repositories larger than 1GB to preserve performance. + +GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain +environments it is not always convenient to use different commands to differentiate between the large files and regular ones. + +Git LFS makes this simpler for the end user by removing the requirement to learn new commands + + +## How it works + +Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. +Once the request is authorized, Git LFS client receives instructions from where to fetch/where to push the large file. + +## Requirements + +* Git LFS is supported in GitLab starting with version 8.2 +* Git LFS client version 0.6.0 and up + +## GitLab and Git LFS + +### Configuration + +Git LFS objects can be large in size and they are stored on GitLab server storage. + +There are two configuration options to help GitLab server administrators: + +* Enabling/disabling Git LFS support +* Changing the location of LFS object storage + +#### Omnibus packages + +In `/etc/gitlab/gitlab.rb`: + +```ruby +gitlab_rails['lfs_enabled'] = false +gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects" +``` + +#### Installations from source + +In `config/gitlab.yml`: + +```yaml + lfs: + enabled: false + storage_path: /mnt/storage/lfs-objects +``` + +### Known limitations + +* Git LFS v1 original API is not supported since it was deprecated early in LFS development, starting with Git LFS version 0.6.0 +* When SSH is set as a remote, Git LFS objects still go through HTTPS +* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended +* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the url to Git config manually (see #troubleshooting-tips) + +## Using Git LFS + +Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: +For example, if you want to upload a very large file and check it into your Git repository: + +```bash +git clone git@gitlab.example.com:group/project.git +git lfs init # initialize the Git LFS project project +git lfs track "*.iso" # select the file extensions that you want to treat as large files +cp ~/tmp/debian.iso ./ # copy a large file into the current directory +git add . # add the large file to git annex +git commit -am "Added Debian iso" # commit the file meta data +git push origin master # sync the git repo and large file to the GitLab server +``` + +Downloading a single large file is also very simple: + +```bash +git clone git@gitlab.example.com:group/project.git +git lfs fetch debian.iso # download the large file +``` + + +## Troubleshooting tips + +### error: Repository or object not found + +Few reasons why this error can occur: + +1. Check the version of Git LFS on the client machine, `git lfs version`. Only version 0.6.0 and up are supported. +1. Check the Git config for traces of deprecated API, `git lfs -l`. If `batch = false` remove the line and try using Git LFS client > 0.6.0 + +### Invalid status for : 501 + +When attempting to push a LFS object to a GitLab server that doesn't have Git LFS support enabled, server will return status `error 501`. Check with your GitLab administrator why Git LFS is not enabled on the server + +### getsockopt: connection refused + +When pushing a LFS object and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, +LFS client is trying to reach GitLab through HTTPS but your GitLab is being served on HTTP. +This behaviour is caused by Git LFS using HTTPS connections by default when it doesn't have a `lfsurl` set in the Git config. + +To go around this issue set the lfs url in git config: + +```bash + +git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch" +``` + +### Credentials are always required when pushing an object + +Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. + +By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). + +For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: + +```bash +git config --global credential.helper 'cache --timeout=3600' +``` + +This will remember the credentials for an hour after which Git operations will require re-authentication. + +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. + +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) + + -- cgit v1.2.1 From 84b5d0356a3364393aacb4a4b22c0635f7e4b4b6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 12:42:43 +0100 Subject: Refactor similar code for Issue and MR update service Signed-off-by: Dmitriy Zaporozhets --- app/services/issuable_base_service.rb | 35 ++++++++++++++++++++++++ app/services/issues/update_service.rb | 36 +++++++------------------ app/services/merge_requests/update_service.rb | 39 +++++++-------------------- 3 files changed, 53 insertions(+), 57 deletions(-) diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 15b3825f96a..11d2b08bba7 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -28,6 +28,9 @@ class IssuableBaseService < BaseService end def filter_params(issuable_ability_name = :issue) + params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE + params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE + ability = :"admin_#{issuable_ability_name}" unless can?(current_user, ability, project) @@ -36,4 +39,36 @@ class IssuableBaseService < BaseService params.delete(:assignee_id) end end + + def update(issuable) + change_state(issuable) + filter_params + old_labels = issuable.labels.to_a + + if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) + issuable.reset_events_cache + + if issuable.labels != old_labels + create_labels_note( + issuable, + issuable.labels - old_labels, + old_labels - issuable.labels) + end + + handle_changes(issuable) + issuable.create_new_cross_references!(current_user) + execute_hooks(issuable, 'update') + end + + issuable + end + + def change_state(issuable) + case params.delete(:state_event) + when 'reopen' + reopen_service.new(project, current_user, {}).execute(issuable) + when 'close' + close_service.new(project, current_user, {}).execute(issuable) + end + end end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index aa1fd79d22d..7c112f731a7 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -1,33 +1,7 @@ module Issues class UpdateService < Issues::BaseService def execute(issue) - case params.delete(:state_event) - when 'reopen' - Issues::ReopenService.new(project, current_user, {}).execute(issue) - when 'close' - Issues::CloseService.new(project, current_user, {}).execute(issue) - end - - params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE - params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE - - filter_params - old_labels = issue.labels.to_a - - if params.present? && issue.update_attributes(params.merge(updated_by: current_user)) - issue.reset_events_cache - - if issue.labels != old_labels - create_labels_note( - issue, issue.labels - old_labels, old_labels - issue.labels) - end - - handle_changes(issue) - issue.create_new_cross_references!(current_user) - execute_hooks(issue, 'update') - end - - issue + update(issue) end def handle_changes(issue) @@ -44,5 +18,13 @@ module Issues create_title_change_note(issue, issue.previous_changes['title'].first) end end + + def reopen_service + Issues::ReopenService + end + + def close_service + Issues::CloseService + end end end diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index d2849e5193f..a5db3776eb6 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -11,36 +11,7 @@ module MergeRequests params.except!(:target_project_id) params.except!(:source_branch) - case params.delete(:state_event) - when 'reopen' - MergeRequests::ReopenService.new(project, current_user, {}).execute(merge_request) - when 'close' - MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request) - end - - params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE - params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE - - filter_params - old_labels = merge_request.labels.to_a - - if params.present? && merge_request.update_attributes(params.merge(updated_by: current_user)) - merge_request.reset_events_cache - - if merge_request.labels != old_labels - create_labels_note( - merge_request, - merge_request.labels - old_labels, - old_labels - merge_request.labels - ) - end - - handle_changes(merge_request) - merge_request.create_new_cross_references!(current_user) - execute_hooks(merge_request, 'update') - end - - merge_request + update(merge_request) end def handle_changes(merge_request) @@ -68,5 +39,13 @@ module MergeRequests merge_request.mark_as_unchecked end end + + def reopen_service + MergeRequests::ReopenService + end + + def close_service + MergeRequests::CloseService + end end end -- cgit v1.2.1 From 49e32cb59fbb5412630426152aa4a7f38f02a443 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 12:55:16 +0100 Subject: Remove small code duplication in user_reference_filter.rb Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/markdown/user_reference_filter.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index 2a594e1662e..ab5e1f6fe9e 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -85,13 +85,12 @@ module Gitlab def link_to_all project = context[:project] - url = urls.namespace_project_url(project.namespace, project, only_path: context[:only_path]) data = data_attribute(project: project.id) - text = User.reference_prefix + 'all' - %(#{text}) + + link_tag(url, data, text) end def link_to_namespace(namespace) @@ -105,16 +104,20 @@ module Gitlab def link_to_group(group, namespace) url = urls.group_url(group, only_path: context[:only_path]) data = data_attribute(group: namespace.id) - text = Group.reference_prefix + group - %(#{text}) + + link_tag(url, data, text) end def link_to_user(user, namespace) url = urls.user_url(user, only_path: context[:only_path]) data = data_attribute(user: namespace.owner_id) - text = User.reference_prefix + user + + link_tag(url, data, text) + end + + def link_tag(url, data, text) %(#{text}) end end -- cgit v1.2.1 From fab04fac13c9f9c116e1e4ed744244c5041e198d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 17 Nov 2015 12:55:48 +0100 Subject: Code duplication check should be enabled now Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 141e7ba41de..94753093540 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,4 +87,3 @@ flay: tags: - ruby - mysql - allow_failure: true -- cgit v1.2.1 From 24cf6865d3c0d47615a814c091cdb40bf513307e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 13:10:01 +0100 Subject: Correctly set comparison first commit when range includes a merge commit --- app/controllers/projects/compare_controller.rb | 4 ++-- app/helpers/diff_helper.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 71aaad1fad6..3517b2bece6 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -19,8 +19,8 @@ class Projects::CompareController < Projects::ApplicationController if compare_result @commits = Commit.decorate(compare_result.commits, @project) @diffs = compare_result.diffs - @commit = @commits.last - @first_commit = @commits.first + @commit = @project.commit(head_ref) + @first_commit = @project.commit(base_ref) @line_notes = [] end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b889fb28973..f7bf2a66eb5 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -171,7 +171,7 @@ module DiffHelper def commit_for_diff(diff) if diff.deleted_file first_commit = @first_commit || @commit - first_commit.parent + first_commit.parent || @first_commit else @commit end -- cgit v1.2.1 From ecb83afabcb69d80995b56323bb89b1ee1176225 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 15:49:37 +0100 Subject: Refactor ability changes --- app/models/ability.rb | 54 ++++++++++++++++++++++----------------- app/models/concerns/has_owners.rb | 31 ---------------------- app/models/group.rb | 23 +++++++++++++++-- app/models/member.rb | 26 +++++++++++-------- app/models/project.rb | 4 +-- 5 files changed, 67 insertions(+), 71 deletions(-) delete mode 100644 app/models/concerns/has_owners.rb diff --git a/app/models/ability.rb b/app/models/ability.rb index eef481c8f8a..500af08d209 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -240,11 +240,11 @@ class Ability # Only group owner and administrators can admin group if group.has_owner?(user) || user.admin? - rules.push(*[ - :admin_group, - :admin_namespace, - :admin_group_member - ]) + rules += [ + :admin_group, + :admin_namespace, + :admin_group_member + ] end rules.flatten @@ -255,16 +255,15 @@ class Ability # Only namespace owner and administrators can admin it if namespace.owner == user || user.admin? - rules.push(*[ - :create_projects, - :admin_namespace - ]) + rules += [ + :create_projects, + :admin_namespace + ] end rules.flatten end - [:issue, :merge_request].each do |name| define_method "#{name}_abilities" do |user, subject| rules = [] @@ -305,15 +304,18 @@ class Ability rules = [] target_user = subject.user group = subject.group - can_manage = group_abilities(user, group).include?(:admin_group_member) - if can_manage && (user != target_user) - rules << :update_group_member - rules << :destroy_group_member - end + unless group.last_owner?(target_user) + can_manage = group_abilities(user, group).include?(:admin_group_member) - if !group.last_owner?(user) && (can_manage || (user == target_user)) - rules << :destroy_group_member + if can_manage && user != target_user + rules << :update_group_member + rules << :destroy_group_member + end + + if user == target_user + rules << :destroy_group_member + end end rules @@ -323,16 +325,20 @@ class Ability rules = [] target_user = subject.user project = subject.project - can_manage = project_abilities(user, project).include?(:admin_project_member) - if can_manage && user != target_user && target_user != project.owner - rules << :update_project_member - rules << :destroy_project_member - end + unless target_user == project.owner + can_manage = project_abilities(user, project).include?(:admin_project_member) - if user == target_user && target_user != project.owner - rules << :destroy_project_member + if can_manage && user != target_user + rules << :update_project_member + rules << :destroy_project_member + end + + if user == target_user + rules << :destroy_project_member + end end + rules end diff --git a/app/models/concerns/has_owners.rb b/app/models/concerns/has_owners.rb deleted file mode 100644 index 53ef6e939dd..00000000000 --- a/app/models/concerns/has_owners.rb +++ /dev/null @@ -1,31 +0,0 @@ -# == Owners concern -# -# Contains owners functionality for groups -# -module HasOwners - extend ActiveSupport::Concern - - def owners - @owners ||= members.owners.includes(:user).map(&:user) - end - - def members - raise NotImplementedError, "Expected members to be defined in #{self.class.name}" - end - - def add_owner(user, current_user = nil) - add_user(user, Gitlab::Access::OWNER, current_user) - end - - def has_owner?(user) - owners.include?(user) - end - - def has_master?(user) - members.masters.where(user_id: user).any? - end - - def last_owner?(user) - has_owner?(user) && owners.size == 1 - end -end diff --git a/app/models/group.rb b/app/models/group.rb index 11fde7ba6cd..2c9e75496b9 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -20,8 +20,7 @@ require 'file_size_validator' class Group < Namespace include Gitlab::ConfigHelper include Referable - include HasOwners - + has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember' alias_method :members, :group_members has_many :users, through: :group_members @@ -66,6 +65,10 @@ class Group < Namespace end end + def owners + @owners ||= group_members.owners.includes(:user).map(&:user) + end + def add_users(user_ids, access_level, current_user = nil) user_ids.each do |user_id| Member.add_user(self.group_members, user_id, access_level, current_user) @@ -92,6 +95,22 @@ class Group < Namespace add_user(user, Gitlab::Access::MASTER, current_user) end + def add_owner(user, current_user = nil) + add_user(user, Gitlab::Access::OWNER, current_user) + end + + def has_owner?(user) + owners.include?(user) + end + + def has_master?(user) + members.masters.where(user_id: user).any? + end + + def last_owner?(user) + has_owner?(user) && owners.size == 1 + end + def avatar_type unless self.avatar.image? self.errors.add :avatar, "only images allowed" diff --git a/app/models/member.rb b/app/models/member.rb index eed9f2537e9..28aee2e3799 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -34,16 +34,18 @@ class Member < ActiveRecord::Base message: "already exists in source", allow_nil: true } validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true - validates :invite_email, presence: { if: :invite? }, - email: { - strict_mode: true, - allow_nil: true - }, - uniqueness: { - scope: [:source_type, - :source_id], - allow_nil: true - } + validates :invite_email, + presence: { + if: :invite? + }, + email: { + strict_mode: true, + allow_nil: true + }, + uniqueness: { + scope: [:source_type, :source_id], + allow_nil: true + } scope :invite, -> { where(user_id: nil) } scope :non_invite, -> { where("user_id IS NOT NULL") } @@ -100,7 +102,9 @@ class Member < ActiveRecord::Base private def can_update_member?(current_user, member) - !current_user || current_user.can?(:update_group_member, member) || + # There is no current user for bulk actions, in which case anything is allowed + !current_user || + current_user.can?(:update_group_member, member) || current_user.can?(:update_project_member, member) end end diff --git a/app/models/project.rb b/app/models/project.rb index 09465775786..a099a67cf63 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -42,8 +42,7 @@ class Project < ActiveRecord::Base include Sortable include AfterCommitQueue include CaseSensitivity - include HasOwners - + extend Gitlab::ConfigHelper extend Enumerize @@ -117,7 +116,6 @@ class Project < ActiveRecord::Base has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' has_many :protected_branches, dependent: :destroy has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember' - alias_method :my_members, :project_members has_many :users, through: :project_members has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys, through: :deploy_keys_projects -- cgit v1.2.1 From e3fe3da63d23981f5a0f3bd629046cbe0533a132 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 15:51:40 +0100 Subject: Use project member abilities more extensively --- app/controllers/groups/group_members_controller.rb | 30 +++++++++---------- .../projects/project_members_controller.rb | 34 +++++++++++++--------- .../groups/group_members/_group_member.html.haml | 6 ++-- app/views/groups/group_members/index.html.haml | 6 ++-- .../project_members/_project_member.html.haml | 11 +++---- app/views/projects/project_members/_team.html.haml | 4 +-- app/views/projects/project_members/update.js.haml | 3 +- 7 files changed, 49 insertions(+), 45 deletions(-) diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index b25957a06e2..0e902c4bb43 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -3,8 +3,7 @@ class Groups::GroupMembersController < Groups::ApplicationController # Authorize before_action :authorize_read_group! - before_action :authorize_admin_group!, except: [:index, :leave] - before_action :authorize_admin_group_member!, only: [:create, :resend_invite] + before_action :authorize_admin_group_member!, except: [:index, :leave] def index @project = @group.projects.find(params[:project_id]) if params[:project_id] @@ -17,7 +16,8 @@ class Groups::GroupMembersController < Groups::ApplicationController end @members = @members.order('access_level DESC').page(params[:page]).per(50) - @group_member = GroupMember.new + + @group_member = @group.group_members.new end def create @@ -27,24 +27,23 @@ class Groups::GroupMembersController < Groups::ApplicationController end def update - @member = @group.group_members.find(params[:id]) + @group_member = @group.group_members.find(params[:id]) - return render_403 unless can?(current_user, :update_group_member, @member) + return render_403 unless can?(current_user, :update_group_member, @group_member) - @member.update_attributes(member_params) + @group_member.update_attributes(member_params) end def destroy @group_member = @group.group_members.find(params[:id]) - if can?(current_user, :destroy_group_member, @group_member) # May fail if last owner. - @group_member.destroy - respond_to do |format| - format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' } - format.js { render nothing: true } - end - else - return render_403 + return render_403 unless can?(current_user, :destroy_group_member, @group_member) + + @group_member.destroy + + respond_to do |format| + format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' } + format.js { render nothing: true } end end @@ -63,10 +62,11 @@ class Groups::GroupMembersController < Groups::ApplicationController end def leave - @group_member = @group.group_members.where(user_id: current_user.id).first + @group_member = @group.group_members.find_by(user_id: current_user) if can?(current_user, :destroy_group_member, @group_member) @group_member.destroy + redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.") else if @group.last_owner?(current_user) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 9de5269cd25..07eb94e4f48 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -1,6 +1,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize - before_action :authorize_admin_project!, except: :leave + before_action :authorize_admin_project_member!, except: :leave def index @project_members = @project.project_members @@ -29,10 +29,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController @project_member = @project.project_members.new end - def new - @project_member = @project.project_members.new - end - def create @project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user) @@ -41,11 +37,17 @@ class Projects::ProjectMembersController < Projects::ApplicationController def update @project_member = @project.project_members.find(params[:id]) + + return render_403 unless can?(current_user, :update_project_member, @project_member) + @project_member.update_attributes(member_params) end def destroy @project_member = @project.project_members.find(params[:id]) + + return render_403 unless can?(current_user, :destroy_project_member, @project_member) + @project_member.destroy respond_to do |format| @@ -71,16 +73,22 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def leave - if @project.namespace == current_user.namespace - message = 'You can not leave your own project. Transfer or delete the project.' - return redirect_back_or_default(default: { action: 'index' }, options: { alert: message }) - end + @project_member = @project.project_members.find_by(user_id: current_user) - @project.project_members.find_by(user_id: current_user).destroy + if can?(current_user, :destroy_project_member, @project_member) + @project_member.destroy - respond_to do |format| - format.html { redirect_to dashboard_projects_path } - format.js { render nothing: true } + respond_to do |format| + format.html { redirect_to dashboard_projects_path, notice: "You left the project." } + format.js { render nothing: true } + end + else + if current_user == @project.owner + message = 'You can not leave your own project. Transfer or delete the project.' + redirect_back_or_default(default: { action: 'index' }, options: { alert: message }) + else + render_403 + end end end diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index 3c19381321a..be94b1abc11 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -1,6 +1,5 @@ - user = member.user - return unless user || member.invite? -- show_roles = true if show_roles.nil? %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %span{class: ("list-item-name" if show_controls)} @@ -25,11 +24,11 @@ = link_to member.created_by.name, user_path(member.created_by) = time_ago_with_tooltip(member.created_at) - - if show_controls && can?(current_user, :admin_group_member, member) + - if show_controls && can?(current_user, :admin_group_member, @group) = link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do Resend invite - - if show_roles + - if should_user_see_group_roles?(current_user, @group) %span.pull-right %strong= member.human_access - if show_controls @@ -37,6 +36,7 @@ = button_tag class: "btn-xs btn js-toggle-button", title: 'Edit access level', type: 'button' do %i.fa.fa-pencil-square-o + - if can?(current_user, :destroy_group_member, member)   - if current_user == user diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 15d289471c9..d4ad33a8bf1 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,8 +1,6 @@ - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) -- show_roles = should_user_see_group_roles?(current_user, @group) - -- if show_roles +- if should_user_see_group_roles?(current_user, @group) %p.light Members of group have access to all group projects. Read more about permissions @@ -32,7 +30,7 @@ (#{@members.total_count}) %ul.well-list - @members.each do |member| - = render 'groups/group_members/group_member', member: member, show_roles: show_roles, show_controls: true + = render 'groups/group_members/group_member', member: member, show_controls: true = paginate @members, theme: 'gitlab' diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml index 76c46d1d806..f07cd97e63d 100644 --- a/app/views/projects/project_members/_project_member.html.haml +++ b/app/views/projects/project_members/_project_member.html.haml @@ -24,18 +24,19 @@ = link_to member.created_by.name, user_path(member.created_by) = time_ago_with_tooltip(member.created_at) - - if current_user_can_admin_project + - if can?(current_user, :admin_project_member, @project) = link_to resend_invite_namespace_project_project_member_path(@project.namespace, @project, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do Resend invite - - if current_user_can_admin_project - - unless @project.personal? && user == current_user - .pull-right - %strong= member.human_access + - if can?(current_user, :admin_project_member, @project) + .pull-right + %strong= member.human_access + - if can?(current_user, :update_project_member, member) = button_tag class: "btn-xs btn js-toggle-button", title: 'Edit access level', type: 'button' do %i.fa.fa-pencil-square-o + - if can?(current_user, :destroy_project_member, member)   - if current_user == user = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: leave_project_message(@project) }, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 615c425e59a..b807fb2cc9d 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,5 +1,3 @@ -- can_admin_project = can?(current_user, :admin_project, @project) - .panel.panel-default.prepend-top-20 .panel-heading %strong #{@project.name} @@ -8,4 +6,4 @@ (#{members.count}) %ul.well-list - members.each do |project_member| - = render 'project_member', member: project_member, current_user_can_admin_project: can_admin_project + = render 'project_member', member: project_member diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml index 811b1858821..2fb3a41d541 100644 --- a/app/views/projects/project_members/update.js.haml +++ b/app/views/projects/project_members/update.js.haml @@ -1,3 +1,2 @@ -- can_admin_project = can?(current_user, :admin_project, @project) :plain - $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member, current_user_can_admin_project: can_admin_project))}'); + $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member))}'); -- cgit v1.2.1 From d60a23b718abc365651f146de678f20367ef25b1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 15:53:53 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3c22df7c9a3..f7c6b09e7b2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.2.0 (unreleased) - Fix trailing whitespace issue in merge request/issue title - Fix bug when milestone/label filter was empty for dashboard issues page - Add ability to create milestone in group projects from single form + - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From 3ae1dee475dbeb9bdf62ed139292cba09b6c98bb Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Mon, 16 Nov 2015 12:00:37 +0800 Subject: Avoid render edit_form when visitor can't edit them. Reverted #9820, github/task_list need a form, textarea for update. --- app/views/projects/notes/_note.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 88808301985..efa7dd01cc2 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -59,8 +59,7 @@ .note-text = preserve do = markdown(note.note, {no_header_anchors: true}) - - unless note.system? - -# System notes can't be edited + - if note_editable?(note) = render 'projects/notes/edit_form', note: note - if note.attachment.url -- cgit v1.2.1 From 756d61562bb8c1a2ebcb29eb0f62548d2338db52 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:24:02 +0100 Subject: Minor refactoring --- app/models/ability.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 95dd53bf425..f5cd14a89c0 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,7 +2,7 @@ class Ability class << self def allowed(user, subject) return not_auth_abilities(user, subject) if user.nil? - return [] unless user.kind_of?(User) + return [] unless user.is_a?(User) return [] if user.blocked? case subject.class.name @@ -22,14 +22,20 @@ class Ability # List of possible abilities # for non-authenticated user def not_auth_abilities(user, subject) - return not_auth_personal_snippet_abilities(subject) if subject.kind_of?(PersonalSnippet) - return not_auth_project_abilities(subject) if subject.kind_of?(Project) || subject.respond_to?(:project) - return not_auth_group_abilities(subject) if subject.kind_of?(Group) || subject.respond_to?(:group) - [] + case true + when subject.is_a?(PersonalSnippet) + not_auth_personal_snippet_abilities(subject) + when subject.is_a?(Project) || subject.respond_to?(:project) + not_auth_project_abilities(subject) + when subject.is_a?(Group) || subject.respond_to?(:group) + not_auth_group_abilities(subject) + else + [] + end end def not_auth_project_abilities(subject) - project = if subject.kind_of?(Project) + project = if subject.is_a?(Project) subject else subject.project @@ -57,7 +63,7 @@ class Ability end def not_auth_group_abilities(subject) - group = if subject.kind_of?(Group) + group = if subject.is_a?(Group) subject else subject.group @@ -327,7 +333,7 @@ class Ability end if snippet.public? || snippet.internal? - rules.push(:read_personal_snippet) + rules << :read_personal_snippet end rules -- cgit v1.2.1 From 1b3475653f253c62ab493fa62665d8a3e105aa88 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:25:02 +0100 Subject: Fix changelog --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b555a050445..ee0da5598a3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,8 +25,7 @@ v 8.2.0 (unreleased) - Allow to define cache in `.gitlab-ci.yml` - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - - Use issue editor as cross reference comment author when issue is edited with a new mention. - - Improve personal snippet access workflow + - Improve personal snippet access workflow (Douglas Alexandre) - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Fix omniauth documentation setting for omnibus configuration (Jon Cairns) - Add "New file" link to dropdown on project page -- cgit v1.2.1 From e49190cad933abc38271b46cded8150fd9b15568 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:08:58 +0100 Subject: Don't use params[:view] directly. --- app/helpers/diff_helper.rb | 8 ++++++-- app/views/projects/commit/show.html.haml | 2 +- app/views/projects/diffs/_diffs.html.haml | 2 +- app/views/projects/diffs/_file.html.haml | 3 +-- app/views/projects/notes/_form.html.haml | 2 +- app/views/projects/notes/_notes_with_form.html.haml | 4 ++-- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b889fb28973..f47d5c4c742 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -1,4 +1,8 @@ module DiffHelper + def diff_view + params[:view] == 'parallel' ? 'parallel' : 'inline' + end + def allowed_diff_size if diff_hard_limit_enabled? Commit::DIFF_HARD_LIMIT_FILES @@ -137,7 +141,7 @@ module DiffHelper # Always use HTML to handle case where JSON diff rendered this button params_copy.delete(:format) - link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do + link_to url_for(params_copy), id: "inline-diff-btn", class: (diff_view == 'inline' ? 'btn active' : 'btn') do 'Inline' end end @@ -148,7 +152,7 @@ module DiffHelper # Always use HTML to handle case where JSON diff rendered this button params_copy.delete(:format) - link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do + link_to url_for(params_copy), id: "parallel-diff-btn", class: (diff_view == 'parallel' ? 'btn active' : 'btn') do 'Side-by-side' end end diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 30a3973828f..85e203cbe57 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -3,4 +3,4 @@ = render "commit_box" = render "ci_menu" if @ci_commit = render "projects/diffs/diffs", diffs: @diffs, project: @project -= render "projects/notes/notes_with_form", view: params[:view] += render "projects/notes/notes_with_form" diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index e46bf1ab1e7..416fb4da071 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -1,4 +1,4 @@ -- if params[:view] == 'parallel' +- if diff_view == 'parallel' - fluid_layout true - diff_files = safe_diff_files(diffs) diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 410ff6abb43..c745b4e69bf 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -33,7 +33,7 @@ -# Skipp all non non-supported blobs - return unless blob.respond_to?('text?') - if blob.text? - - if params[:view] == 'parallel' + - if diff_view == 'parallel' = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i - else = render "projects/diffs/text_file", diff_file: diff_file, index: i @@ -42,4 +42,3 @@ = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i - else .nothing-here-block No preview for this file type - diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 13dfa0a1bb3..5dd84317e3b 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -1,5 +1,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f| - = hidden_field_tag :view, params[:view] + = hidden_field_tag :view, diff_view = hidden_field_tag :line_type = note_target_fields(@note) = f.hidden_field :commit_id diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 91cefa6d14d..066a1986f69 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -4,7 +4,7 @@ .js-main-target-form - if can? current_user, :create_note, @project - = render "projects/notes/form", view: params[:view] + = render "projects/notes/form", view: diff_view :javascript - window._notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}") + window._notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}") -- cgit v1.2.1 From 8c308e3df6b2c9b259f66172d13cc81009c1ae89 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:51:35 +0100 Subject: Don't fail when there was no previous assignee --- app/services/notification_service.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 16c84f4a055..27928c0945b 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -113,7 +113,7 @@ class NotificationService end # Add all users participating in the thread (author, assignee, comment authors) - participants = + participants = if target.respond_to?(:participants) target.participants(note.author) else @@ -325,7 +325,7 @@ class NotificationService def reject_unsubscribed_users(recipients, target) return recipients unless target.respond_to? :subscriptions - + recipients.reject do |user| subscription = target.subscriptions.find_by_user_id(user.id) subscription && !subscription.subscribed @@ -343,7 +343,7 @@ class NotificationService recipients end end - + def new_resource_email(target, project, method) recipients = build_recipients(target, project, target.author) @@ -361,12 +361,13 @@ class NotificationService end def reassign_resource_email(target, project, current_user, method) - assignee_id_was = previous_record(target, "assignee_id") - previous_assignee = User.find(assignee_id_was) + previous_assignee_id = previous_record(target, "assignee_id") + previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id + recipients = build_recipients(target, project, current_user, [previous_assignee]) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, assignee_id_was, current_user.id) + mailer.send(method, recipient.id, target.id, previous_assignee_id, current_user.id) end end @@ -378,9 +379,10 @@ class NotificationService end end - def build_recipients(target, project, current_user, previous_records = nil ) + def build_recipients(target, project, current_user, extra_recipients = nil ) recipients = target.participants(current_user) - recipients.concat(previous_records).compact.uniq if previous_records + + recipients = recipients.concat(extra_recipients).compact.uniq if extra_recipients recipients = add_project_watchers(recipients, project) recipients = reject_mention_users(recipients, project) -- cgit v1.2.1 From 80a2eb5d50102ff8e0dcfb67994acc3037b0270e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:51:39 +0100 Subject: Fix spec --- spec/services/issues/update_service_spec.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 4e79484f26a..6a9053753c1 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -3,13 +3,15 @@ require 'spec_helper' describe Issues::UpdateService do let(:user) { create(:user) } let(:user2) { create(:user) } - let(:issue) { create(:issue, title: 'Old title', assignee_id: user.id) } + let(:user3) { create(:user) } + let(:issue) { create(:issue, title: 'Old title', assignee_id: user3.id) } let(:label) { create(:label) } let(:project) { issue.project } before do project.team << [user, :master] project.team << [user2, :developer] + project.team << [user3, :developer] end describe 'execute' do @@ -35,10 +37,10 @@ describe Issues::UpdateService do it { expect(@issue.labels.first.title).to eq('Bug') } it 'should send email to user2 about assign of new issue and email to user about issue unassignment' do - deliveries = ActionMailer::Base.deliveries + deliveries = ActionMailer::Base.deliveries email = deliveries.last - recipients = deliveries.map(&:to).uniq.flatten - expect(recipients.last(2)).to include(user.email,user2.email) + recipients = deliveries.last(2).map(&:to).flatten + expect(recipients).to include(user2.email, user3.email) expect(email.subject).to include(issue.title) end -- cgit v1.2.1 From 0fbf47a219c7c284b5728a4a6d066f1d0700f985 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:51:49 +0100 Subject: Add credits to changelog --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 56c0daa61a1..119185b8cef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,7 +32,7 @@ v 8.2.0 (unreleased) - Add "added", "modified" and "removed" properties to commit object in webhook - Rename "Back to" links to "Go to" because its not always a case it point to place user come from - Allow groups to appear in the search results if the group owner allows it - - Add email notification to former assignee upon unassignment + - Add email notification to former assignee upon unassignment (Adam Lieskovský) - New design for project graphs page - Remove deprecated dumped yaml file generated from previous job definitions - Fix incoming email config defaults -- cgit v1.2.1 From 931c56f822c21e5f77743297603cfff5065eb772 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 16:53:15 +0100 Subject: Add tests for merge request update. --- spec/services/issues/update_service_spec.rb | 2 +- spec/services/merge_requests/update_service_spec.rb | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 6a9053753c1..f55527ee9a3 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -36,7 +36,7 @@ describe Issues::UpdateService do it { expect(@issue.labels.count).to eq(1) } it { expect(@issue.labels.first.title).to eq('Bug') } - it 'should send email to user2 about assign of new issue and email to user about issue unassignment' do + it 'should send email to user2 about assign of new issue and email to user3 about issue unassignment' do deliveries = ActionMailer::Base.deliveries email = deliveries.last recipients = deliveries.last(2).map(&:to).flatten diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index c75173c1452..2ed51d223b7 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' describe MergeRequests::UpdateService do let(:user) { create(:user) } let(:user2) { create(:user) } - let(:merge_request) { create(:merge_request, :simple, title: 'Old title') } + let(:user3) { create(:user) } + let(:merge_request) { create(:merge_request, :simple, title: 'Old title', assignee_id: user3.id) } let(:project) { merge_request.project } let(:label) { create(:label) } @@ -47,9 +48,11 @@ describe MergeRequests::UpdateService do with(@merge_request, 'update') end - it 'should send email to user2 about assign of new merge_request' do - email = ActionMailer::Base.deliveries.last - expect(email.to.first).to eq(user2.email) + it 'should send email to user2 about assign of new merge request and email to user3 about merge request unassignment' do + deliveries = ActionMailer::Base.deliveries + email = deliveries.last + recipients = deliveries.last(2).map(&:to).flatten + expect(recipients).to include(user2.email, user3.email) expect(email.subject).to include(merge_request.title) end -- cgit v1.2.1 From 4597f8ef209b832538c3aa8524c197ef505d625e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 10:44:01 -0500 Subject: Update monthly release template [ci skip] --- doc/release/monthly.md | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index d347f58ba0f..c9ab87671d2 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -54,21 +54,25 @@ template are explained below: - [ ] Update GitLab.com with RC1 - [ ] Create the regression issue in the CE issue tracker: - > This is a meta issue to index possible regressions in this monthly release - > and any patch versions. - > - > Please do not raise or discuss issues directly in this issue but link to - > issues that might warrant a patch release. If there is a Merge Request - > that fixes the issue, please link to that as well. - > - > Please only post one regression issue and/or merge request per comment. - > Comments will be updated by the release manager as they are addressed. + ``` + This is a meta issue to index possible regressions in this monthly release + and any patch versions. + + Please do not raise or discuss issues directly in this issue but link to + issues that might warrant a patch release. If there is a Merge Request + that fixes the issue, please link to that as well. + + Please only post one regression issue and/or merge request per comment. + Comments will be updated by the release manager as they are addressed. + ``` - [ ] Tweet about RC1 release: - > GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable - > Use at your own risk. Please link regressions issues from - > LINK_TO_REGRESSION_ISSUE + ``` + GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable + Use at your own risk. Please link regressions issues from + LINK_TO_REGRESSION_ISSUE + ``` ### Xth: (3 working days before the 22nd) -- cgit v1.2.1 From 85102d094f44741e6fbf53910df1bed363a020bb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 11:02:11 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 3c22df7c9a3..ca09d9c09f3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Please view this file on the master branch, on stable branches it's out of date. -v 8.2.0 (unreleased) +v 8.3.0 (unreleased) + +v 8.2.0 - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) -- cgit v1.2.1 From f008b2d2dfd1b8030ec0634c223ca9f11d304e91 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 17:19:31 +0100 Subject: Remove extra space --- app/services/notification_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 27928c0945b..d9aa6bc98cb 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -379,7 +379,7 @@ class NotificationService end end - def build_recipients(target, project, current_user, extra_recipients = nil ) + def build_recipients(target, project, current_user, extra_recipients = nil) recipients = target.participants(current_user) recipients = recipients.concat(extra_recipients).compact.uniq if extra_recipients -- cgit v1.2.1 From 6f6c75cb85195984c6e610ad109464dcb952a5fe Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 11:59:22 -0500 Subject: Bump VERSION [ci skip] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a2264f05f50..8d0676ff07b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.0.pre +8.3.0.pre -- cgit v1.2.1 From e945ec02804bb28dbd228d8002a159c8da0fcc38 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 18:53:56 +0100 Subject: Add "Start a new merge request" option to every commit form --- .../javascripts/blob/blob_file_dropzone.js.coffee | 17 ++----- app/assets/javascripts/blob/edit_blob.js.coffee | 7 --- app/assets/javascripts/blob/new_blob.js.coffee | 7 --- app/assets/javascripts/new_commit_form.js.coffee | 21 +++++++++ app/assets/stylesheets/pages/editor.scss | 4 -- app/controllers/application_controller.rb | 30 ------------ .../concerns/creates_merge_request_for_commit.rb | 28 ++++++++++++ app/controllers/projects/blob_controller.rb | 53 ++++++++++++---------- app/controllers/projects/tree_controller.rb | 13 +++++- app/helpers/merge_requests_helper.rb | 20 ++++++++ app/views/projects/blob/_actions.html.haml | 2 +- app/views/projects/blob/_new_dir.html.haml | 14 +++--- app/views/projects/blob/_remove.html.haml | 16 +++---- app/views/projects/blob/_upload.html.haml | 18 +++----- app/views/projects/blob/edit.html.haml | 19 ++------ app/views/projects/blob/new.html.haml | 24 ++-------- app/views/projects/blob/show.html.haml | 4 +- app/views/projects/tree/_tree_content.html.haml | 2 +- .../shared/_commit_message_container.html.haml | 8 ++-- app/views/shared/_new_commit_form.html.haml | 18 ++++++++ 20 files changed, 168 insertions(+), 157 deletions(-) create mode 100644 app/assets/javascripts/new_commit_form.js.coffee create mode 100644 app/controllers/concerns/creates_merge_request_for_commit.rb create mode 100644 app/views/shared/_new_commit_form.html.haml diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee index 5b604adbbb1..195f8b11e5d 100644 --- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee +++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee @@ -23,18 +23,6 @@ class @BlobFileDropzone init: -> this.on 'addedfile', (file) -> $('.dropzone-alerts').html('').hide() - commit_message = form.find('#commit_message')[0] - - if /^Upload/.test(commit_message.placeholder) - commit_message.placeholder = 'Upload ' + file.name - - return - - this.on 'removedfile', (file) -> - commit_message = form.find('#commit_message')[0] - - if /^Upload/.test(commit_message.placeholder) - commit_message.placeholder = 'Upload new file' return @@ -47,8 +35,9 @@ 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()) + formData.append('new_branch', form.find('.js-new-branch').val()) + formData.append('create_merge_request', form.find('.js-create-merge-request').val()) + formData.append('commit_message', form.find('.js-commit-message').val()) return # Override behavior of adding error underneath preview diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee index bf4cd85aee0..f6bf836f19f 100644 --- a/app/assets/javascripts/blob/edit_blob.js.coffee +++ b/app/assets/javascripts/blob/edit_blob.js.coffee @@ -11,13 +11,6 @@ class @EditBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $('#new_branch').keyup -> - if $(this).val() != $('#original_branch').val() - $('.form-group.destination').show() - else - $('.form-group.destination').hide() - $('#create_merge_request').prop('checked', false) - # Before a form submission, move the content from the Ace editor into the # submitted textarea $('form').submit -> diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee index c2d6c7acfef..68c5e5195e3 100644 --- a/app/assets/javascripts/blob/new_blob.js.coffee +++ b/app/assets/javascripts/blob/new_blob.js.coffee @@ -11,13 +11,6 @@ class @NewBlob if ace_mode editor.getSession().setMode "ace/mode/" + ace_mode - $('#new_branch').keyup -> - if $(this).val() != $('#original_branch').val() - $('.form-group.destination').show() - else - $('.form-group.destination').hide() - $('#create_merge_request').prop('checked', false) - # Before a form submission, move the content from the Ace editor into the # submitted textarea $('form').submit -> diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee new file mode 100644 index 00000000000..2e561dea3e1 --- /dev/null +++ b/app/assets/javascripts/new_commit_form.js.coffee @@ -0,0 +1,21 @@ +class @NewCommitForm + constructor: (form) -> + @newBranch = form.find('.js-new-branch') + @originalBranch = form.find('.js-original-branch') + @createMergeRequest = form.find('.js-create-merge-request') + @createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group') + + @renderDestination() + @newBranch.keyup @renderDestination + + renderDestination: => + different = @newBranch.val() != @originalBranch.val() + + if different + @createMergeRequestFormGroup.show() + @createMergeRequest.prop('checked', true) unless @wasDifferent + else + @createMergeRequestFormGroup.hide() + @createMergeRequest.prop('checked', false) + + @wasDifferent = different diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index c0defa82bff..e2c521af91e 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -63,8 +63,4 @@ margin-top: 0; padding: $gl-padding } - - .destination { - display: none; - } } diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6f87ee08b2d..0d182e8eb04 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,7 +22,6 @@ class ApplicationController < ActionController::Base helper_method :abilities, :can?, :current_application_settings helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled? - helper_method :new_mr_from_push_event, :new_mr_path_for_fork_from_push_event, :new_mr_path_from_push_event rescue_from Encoding::CompatibilityError do |exception| log_exception(exception) @@ -343,35 +342,6 @@ class ApplicationController < ActionController::Base current_application_settings.import_sources.include?('git') end - # new merge requests routing helpers - def new_mr_path_from_push_event(event, target_branch=nil) - target_project = event.project.forked_from_project || event.project - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, target_project, target_branch) - ) - end - - def new_mr_path_for_fork_from_push_event(event, target_branch=nil) - new_namespace_project_merge_request_path( - event.project.namespace, - event.project, - new_mr_from_push_event(event, event.project.forked_from_project, target_branch) - ) - end - - def new_mr_from_push_event(event, target_project, target_branch) - { - merge_request: { - source_project_id: event.project.id, - target_project_id: target_project.id, - source_branch: event.branch_name, - target_branch: target_branch || target_project.repository.root_ref - } - } - end - def redirect_to_home_page_url? # If user is not signed-in and tries to access root_path - redirect him to landing page # Don't redirect to the default URL to prevent endless redirections diff --git a/app/controllers/concerns/creates_merge_request_for_commit.rb b/app/controllers/concerns/creates_merge_request_for_commit.rb new file mode 100644 index 00000000000..c7527822158 --- /dev/null +++ b/app/controllers/concerns/creates_merge_request_for_commit.rb @@ -0,0 +1,28 @@ +module CreatesMergeRequestForCommit + extend ActiveSupport::Concern + + def new_merge_request_path + if @project.forked? + target_project = @project.forked_from_project || @project + target_branch = target_project.repository.root_ref + else + target_project = @project + target_branch = @ref + end + + new_namespace_project_merge_request_path( + @project.namespace, + @project, + merge_request: { + source_project_id: @project.id, + target_project_id: target_project.id, + source_branch: @new_branch, + target_branch: target_branch + } + ) + end + + def create_merge_request? + params[:create_merge_request] && @new_branch != @ref + end +end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index d7fae64fcdd..41ec7bde45d 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a file's blame class Projects::BlobController < Projects::ApplicationController include ExtractsPath + include CreatesMergeRequestForCommit include ActionView::Helpers::SanitizeHelper # Raised when given an invalid file path @@ -27,15 +28,8 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "The changes have been successfully committed" respond_to do |format| - format.html do - url = if params[:create_merge_request] - new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) - else - namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) - end - redirect_to url - end - format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } } + format.html { redirect_to after_create_path } + format.json { render json: { message: "success", filePath: after_create_path } } end else flash[:alert] = result[:message] @@ -59,14 +53,7 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" respond_to do |format| - format.html do - url = if params[:create_merge_request] - new_mr_path_from_push_event(current_user.recent_push(@project.id), @ref) - else - after_edit_path - end - redirect_to url - end + format.html { redirect_to after_edit_path } format.json { render json: { message: "success", filePath: after_edit_path } } end else @@ -91,7 +78,7 @@ class Projects::BlobController < Projects::ApplicationController if result[:status] == :success flash[:notice] = "Your changes have been successfully committed" - redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch) + redirect_to after_destroy_path else flash[:alert] = result[:message] render :show @@ -145,15 +132,33 @@ class Projects::BlobController < Projects::ApplicationController render_404 end + def after_create_path + @after_create_path ||= + if create_merge_request? + new_merge_request_path + else + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path)) + end + end + def after_edit_path @after_edit_path ||= - if from_merge_request + if create_merge_request? + new_merge_request_path + elsif from_merge_request && @new_branch == @ref diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) + "#file-path-#{hexdigest(@path)}" - elsif @target_branch.present? - namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path)) else - namespace_project_blob_path(@project.namespace, @project, @id) + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path)) + end + end + + def after_destroy_path + @after_destroy_path ||= + if create_merge_request? + new_merge_request_path + else + namespace_project_tree_path(@project.namespace, @project, @new_branch) end end @@ -168,7 +173,7 @@ class Projects::BlobController < Projects::ApplicationController def editor_variables @current_branch = @ref - @target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref + @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref @file_path = if action_name.to_s == 'create' @@ -188,7 +193,7 @@ class Projects::BlobController < Projects::ApplicationController @commit_params = { file_path: @file_path, current_branch: @current_branch, - target_branch: @target_branch, + target_branch: @new_branch, commit_message: params[:commit_message], file_content: params[:content], file_content_encoding: params[:encoding] diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index bdcb1a3e297..8f272ad1281 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -1,6 +1,7 @@ # Controller for viewing a repository's file structure class Projects::TreeController < Projects::ApplicationController include ExtractsPath + include CreatesMergeRequestForCommit include ActionView::Helpers::SanitizeHelper before_action :require_non_empty_project, except: [:new, :create] @@ -43,7 +44,7 @@ class Projects::TreeController < Projects::ApplicationController 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)) } + format.html { redirect_to after_create_dir_path } end else flash[:alert] = message @@ -53,6 +54,8 @@ class Projects::TreeController < Projects::ApplicationController end end + private + 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]) @@ -63,4 +66,12 @@ class Projects::TreeController < Projects::ApplicationController commit_message: params[:commit_message], } end + + def after_create_dir_path + if create_merge_request? + new_merge_request_path + else + namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) + end + end end diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 7e8f61fd274..b804d4f4e3b 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -1,4 +1,24 @@ module MergeRequestsHelper + def new_mr_path_from_push_event(event) + target_project = event.project.forked_from_project || event.project + new_namespace_project_merge_request_path( + event.project.namespace, + event.project, + new_mr_from_push_event(event, target_project) + ) + end + + def new_mr_from_push_event(event, target_project) + { + merge_request: { + source_project_id: event.project.id, + target_project_id: target_project.id, + source_branch: event.branch_name, + target_branch: target_project.repository.root_ref + } + } + end + def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 373b3a0c5b0..ba3e0c3c590 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -19,4 +19,4 @@ - if allowed_tree_edit? .btn-group{ role: "group" } %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace - %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove + %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index a0fc8bbd752..13b5ffd17ff 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -5,21 +5,19 @@ %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_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' 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" + + = render 'shared/new_commit_form', placeholder: "Add new directory" + .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" :javascript - disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create"); + disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); + new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml index cae5ff01099..1cf19a7d3db 100644 --- a/app/views/projects/blob/_remove.html.haml +++ b/app/views/projects/blob/_remove.html.haml @@ -3,16 +3,16 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h3.page-title Remove #{@blob.name} - %p.light - From branch - %strong= @ref + %h3.page-title Delete #{@blob.name} .modal-body - = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-requires-input' do - = render 'shared/commit_message_container', params: params, - placeholder: 'Removed this file because...' + = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-requires-input' do + = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}" + .form-group .col-sm-offset-2.col-sm-10 - = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file' + = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + +:javascript + new NewCommitForm($('.js-replace-blob-form')) diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index a1c54e731f0..3bb61f0c944 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -5,7 +5,7 @@ %a.close{href: "#", "data-dismiss" => "modal"} × %h3.page-title #{title} .modal-body - = form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do + = form_tag form_path, method: method, class: 'js-upload-blob-form form-horizontal' do .dropzone .dropzone-previews.blob-upload-dropzone-previews %p.dz-message.light @@ -13,19 +13,15 @@ = link_to 'click to upload', '#', class: "markdown-selector" %br .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" + + = render 'shared/new_commit_form', placeholder: placeholder + .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' = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript - disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'); - new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}'); + disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); + new BlobFileDropzone($('.js-upload-blob-form'), '#{method}'); + new NewCommitForm($('.js-upload-blob-form')) diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 26216462707..56745165251 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -13,28 +13,15 @@ %i.fa.fa-eye = editing_preview_title(@blob.name) - = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input') do + = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data - = render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}" - - .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.destination - .col-sm-offset-2.col-sm-10 - .checkbox - = label_tag :create_merge_request do - = check_box_tag :create_merge_request, 1, false - Start a new merge request + = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}" = hidden_field_tag 'last_commit', @last_commit = hidden_field_tag 'content', '', id: "file-content" = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id] - = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: @after_edit_path :javascript blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}") + new NewCommitForm($('.js-edit-blob-form')) diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 0439e55131e..1ff68005450 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -2,32 +2,18 @@ = render "header_title" .gray-content-block.top-block - Create a new file + %h3.page-title + Create 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 + = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do = render 'projects/blob/editor', ref: @ref - = render 'shared/commit_message_container', params: params, - placeholder: 'Add new file' - - - 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 js-quick-submit" - - .form-group.destination - .col-sm-offset-2.col-sm-10 - .checkbox - = label_tag :create_merge_request do - = check_box_tag :create_merge_request, 1, false - Start a new merge request + = render 'shared/new_commit_form', placeholder: "Add new file" = hidden_field_tag 'content', '', id: 'file-content' - = hidden_field_tag 'original_branch', @ref = render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_tree_path(@project.namespace, @project, @id) :javascript blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null) + new NewCommitForm($('.js-new-blob-form')) diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index f52b89f6921..b7276868ce6 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -10,6 +10,4 @@ = render 'projects/blob/remove' - title = "Replace #{@blob.name}" - = render 'projects/blob/upload', title: title, placeholder: title, - button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), - method: :put + = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index ee4c9d1693d..c64e684df26 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -30,7 +30,7 @@ = render "projects/tree/readme", readme: tree.readme - 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/upload', title: 'Upload New File', 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 diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml index cc3f1268f8b..7c57924277e 100644 --- a/app/views/shared/_commit_message_container.html.haml +++ b/app/views/shared/_commit_message_container.html.haml @@ -1,13 +1,15 @@ .form-group.commit_message-group - = label_tag 'commit_message', class: 'control-label' do + - nonce = SecureRandom.hex + = label_tag "commit_message-#{nonce}", class: 'control-label' do Commit message .col-sm-10 .commit-message-container .max-width-marker = text_area_tag 'commit_message', (params[:commit_message] || local_assigns[:text]), - class: 'form-control js-quick-submit', placeholder: local_assigns[:placeholder], - required: true, rows: (local_assigns[:rows] || 3) + class: 'form-control js-commit-message js-quick-submit', placeholder: local_assigns[:placeholder], + required: true, rows: (local_assigns[:rows] || 3), + id: "commit_message-#{nonce}" - if local_assigns[:hint] %p.hint Try to keep the first line under 52 characters diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml new file mode 100644 index 00000000000..8636341c60d --- /dev/null +++ b/app/views/shared/_new_commit_form.html.haml @@ -0,0 +1,18 @@ += render 'shared/commit_message_container', 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', @new_branch || @ref, class: "form-control js-new-branch" + + .form-group.js-create-merge-request-form-group + .col-sm-offset-2.col-sm-10 + .checkbox + - nonce = SecureRandom.hex + = label_tag "create_merge_request-#{nonce}" do + = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" + Start a new merge request with this commit + + = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch' -- cgit v1.2.1 From 95139a6508ee2ac958c898704eb5a0421ad7487e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 17 Nov 2015 18:54:45 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3c22df7c9a3..1009b1d1681 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.2.0 (unreleased) - Fix trailing whitespace issue in merge request/issue title - Fix bug when milestone/label filter was empty for dashboard issues page - Add ability to create milestone in group projects from single form + - Add option to create merge request when editing/creating a file (Dirceu Tiegs) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From 632f60cc76c1b2cd91a079ca8538d59fa28c9818 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 13:48:45 -0500 Subject: Fix CHANGELOG [ci skip] --- CHANGELOG | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 56072e10eff..94f07a31689 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,7 +3,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) v 8.2.0 -v 8.2.0 (unreleased) - Fix grouping of contributors by email in graph. - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) @@ -1957,4 +1956,4 @@ v 0.8.0 - stability - security fixes - increased test coverage - - email notification \ No newline at end of file + - email notification -- cgit v1.2.1 From 08dc38223e0c18233052e04ac95a4f6942fcb1b5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 17 Nov 2015 15:00:14 -0500 Subject: Rename `not_auth_*` ability methods to `anonymous_*` --- app/models/ability.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index f5cd14a89c0..c93139e9039 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,7 +1,7 @@ class Ability class << self def allowed(user, subject) - return not_auth_abilities(user, subject) if user.nil? + return anonymous_abilities(user, subject) if user.nil? return [] unless user.is_a?(User) return [] if user.blocked? @@ -19,22 +19,21 @@ class Ability end.concat(global_abilities(user)) end - # List of possible abilities - # for non-authenticated user - def not_auth_abilities(user, subject) + # List of possible abilities for anonymous user + def anonymous_abilities(user, subject) case true when subject.is_a?(PersonalSnippet) - not_auth_personal_snippet_abilities(subject) + anonymous_personal_snippet_abilities(subject) when subject.is_a?(Project) || subject.respond_to?(:project) - not_auth_project_abilities(subject) + anonymous_project_abilities(subject) when subject.is_a?(Group) || subject.respond_to?(:group) - not_auth_group_abilities(subject) + anonymous_group_abilities(subject) else [] end end - def not_auth_project_abilities(subject) + def anonymous_project_abilities(subject) project = if subject.is_a?(Project) subject else @@ -62,7 +61,7 @@ class Ability end end - def not_auth_group_abilities(subject) + def anonymous_group_abilities(subject) group = if subject.is_a?(Group) subject else @@ -76,7 +75,7 @@ class Ability end end - def not_auth_personal_snippet_abilities(snippet) + def anonymous_personal_snippet_abilities(snippet) if snippet.public? [:read_personal_snippet] else -- cgit v1.2.1 From 3d762c2e92f42a2c35cfbc90c87caedc8d60518a Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 17 Nov 2015 12:19:04 -0800 Subject: More labels since we get more specializations. --- PROCESS.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PROCESS.md b/PROCESS.md index a4b0c83644b..1408c8302ee 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -34,13 +34,16 @@ The most important thing is making sure valid issues receive feedback from the d ## Workflow labels -Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. +Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. - *Awaiting feedback*: Feedback pending from the reporter - *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away) - *Attached MR*: There is a MR attached and the discussion should happen there - We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay. -- *Awaiting developer action/feedback*: Issue needs to be fixed or clarified by a developer +- *Developer*: needs help from a developer +- *UX* needs needs help from a UX designer +- *Frontend* needs help from a Front-end engineer +- *Graphics* needs help from a Graphics designer ## Functional labels -- cgit v1.2.1 From 1e74a12c2f1ed024bb7bd0d1963011833b7404ca Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 17 Nov 2015 14:43:51 -0800 Subject: Example workflow. --- PROCESS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PROCESS.md b/PROCESS.md index 1408c8302ee..482ad5fe9e1 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -45,6 +45,8 @@ Workflow labels are purposely not very detailed since that would be hard to keep - *Frontend* needs help from a Front-end engineer - *Graphics* needs help from a Graphics designer +Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. + ## Functional labels These labels describe what development specialities are involved such as: PostgreSQL, UX, LDAP. -- cgit v1.2.1 From 303f79f8046876c7a461708dee5d1f50f12737bf Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 18 Nov 2015 17:10:04 +0800 Subject: Make sure notify email always has author info. --- app/views/notify/_note_message.html.haml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/views/notify/_note_message.html.haml b/app/views/notify/_note_message.html.haml index 00cb4aa24cc..27112c6745a 100644 --- a/app/views/notify/_note_message.html.haml +++ b/app/views/notify/_note_message.html.haml @@ -1,2 +1,4 @@ +%div + "#{link_to @note.author_name, user_url(@note.author)} wrote:" %div = markdown(@note.note, pipeline: :email) -- cgit v1.2.1 From 6c62f182c61e9f5f2c1cd3e98c647f246bf6880b Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 10:32:29 +0100 Subject: Updates to the lfs doc. --- doc/workflow/git_lfs.md | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index 6202822ea2d..7304735f574 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -12,7 +12,7 @@ Git LFS makes this simpler for the end user by removing the requirement to learn ## How it works Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. -Once the request is authorized, Git LFS client receives instructions from where to fetch/where to push the large file. +Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. ## Requirements @@ -23,7 +23,7 @@ Once the request is authorized, Git LFS client receives instructions from where ### Configuration -Git LFS objects can be large in size and they are stored on GitLab server storage. +Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. There are two configuration options to help GitLab server administrators: @@ -49,7 +49,7 @@ In `config/gitlab.yml`: storage_path: /mnt/storage/lfs-objects ``` -### Known limitations +## Known limitations * Git LFS v1 original API is not supported since it was deprecated early in LFS development, starting with Git LFS version 0.6.0 * When SSH is set as a remote, Git LFS objects still go through HTTPS @@ -66,8 +66,13 @@ For example, if you want to upload a very large file and check it into your Git git clone git@gitlab.example.com:group/project.git git lfs init # initialize the Git LFS project project git lfs track "*.iso" # select the file extensions that you want to treat as large files +``` + +Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: + +```bash cp ~/tmp/debian.iso ./ # copy a large file into the current directory -git add . # add the large file to git annex +git add . # add the large file to the project git commit -am "Added Debian iso" # commit the file meta data git push origin master # sync the git repo and large file to the GitLab server ``` @@ -80,26 +85,32 @@ git lfs fetch debian.iso # download the large file ``` -## Troubleshooting tips +## Troubleshooting ### error: Repository or object not found -Few reasons why this error can occur: +There are a couple of reasons why this error can occur: -1. Check the version of Git LFS on the client machine, `git lfs version`. Only version 0.6.0 and up are supported. -1. Check the Git config for traces of deprecated API, `git lfs -l`. If `batch = false` remove the line and try using Git LFS client > 0.6.0 +* Wrong version of LFS client used: + +Check the version of Git LFS on the client machine with `git lfs version`. Only version 0.6.0 and newer are supported. + +* Project is using deprecated LFS API + +Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try using Git LFS client newer than 0.6.0. ### Invalid status for : 501 -When attempting to push a LFS object to a GitLab server that doesn't have Git LFS support enabled, server will return status `error 501`. Check with your GitLab administrator why Git LFS is not enabled on the server +When attempting to push a LFS object to a GitLab server that doesn't have Git LFS support enabled, server will return status `error 501`. Check with your GitLab administrator why Git LFS is not enabled on the server. See [Configuration section](#configuration) for instructions on how to enable LFS support. ### getsockopt: connection refused -When pushing a LFS object and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, -LFS client is trying to reach GitLab through HTTPS but your GitLab is being served on HTTP. -This behaviour is caused by Git LFS using HTTPS connections by default when it doesn't have a `lfsurl` set in the Git config. +If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, +the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. + +This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. -To go around this issue set the lfs url in git config: +To prevent this from happening, set the lfs url in project Git config: ```bash -- cgit v1.2.1 From 60e45651425369d7e55b85984ac8f5045d8bdef6 Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Wed, 18 Nov 2015 10:03:08 +0000 Subject: missing . --- doc/workflow/git_lfs.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index 7304735f574..4990a9b5aac 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -6,7 +6,7 @@ The general recommendation is to not have Git repositories larger than 1GB to pr GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain environments it is not always convenient to use different commands to differentiate between the large files and regular ones. -Git LFS makes this simpler for the end user by removing the requirement to learn new commands +Git LFS makes this simpler for the end user by removing the requirement to learn new commands. ## How it works @@ -133,6 +133,4 @@ This will remember the credentials for an hour after which Git operations will r If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) - - +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) \ No newline at end of file -- cgit v1.2.1 From b6251af40fa8ffdc9e85f5ed4c279774fe0ecaaf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 11:15:49 +0100 Subject: Fix feature spec. --- features/project/source/browse_files.feature | 14 ++++++------- features/steps/project/source/browse_files.rb | 30 +++++++-------------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 69aa79f2d24..e545ea63ca8 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -42,7 +42,7 @@ Feature: Project Source Browse Files 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 am redirected to the new merge request page And I can see the new commit message @javascript @@ -64,7 +64,7 @@ Feature: Project Source Browse Files And I fill the commit message And I fill the new branch name And I click on "Commit Changes" - Then I am redirected to the new file on new branch + Then I am redirected to the new merge request page And I should see its new content @javascript @@ -134,7 +134,7 @@ Feature: Project Source Browse Files And I fill the commit message And I fill the new branch name And I click on "Commit Changes" - Then I am redirected to the ".gitignore" on new branch + Then I am redirected to the new merge request page And I should see its new content @javascript @wip @@ -154,7 +154,7 @@ Feature: Project Source Browse Files 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 + Then I am redirected to the new merge request page @javascript Scenario: I attempt to create an existing directory @@ -174,12 +174,12 @@ Feature: Project Source Browse Files Then I see diff @javascript - Scenario: I can remove file and commit + Scenario: I can delete file and commit Given I click on ".gitignore" file in repo And I see the ".gitignore" - And I click on "Remove" + And I click on "Delete" And I fill the commit message - And I click on "Remove file" + And I click on "Delete file" Then I am redirected to the files URL And I don't see the ".gitignore" diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 84725b9b585..f40e0f0d528 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -98,12 +98,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps click_button 'Create directory' end - step 'I click on "Remove"' do - click_button 'Remove' + step 'I click on "Delete"' do + click_button 'Delete' end - step 'I click on "Remove file"' do - click_button 'Remove file' + step 'I click on "Delete file"' do + click_button 'Delete file' end step 'I click on "Replace"' do @@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I can see new file page' do - expect(page).to have_content "new file" + expect(page).to have_content "Create New File" expect(page).to have_content "Commit message" end @@ -225,10 +225,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'master/.gitignore')) end - step 'I am redirected to the ".gitignore" on new branch' do - expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'new_branch_name/.gitignore')) - end - step 'I am redirected to the permalink URL' do expect(current_path).to( eq(namespace_project_blob_path(@project.namespace, @project, @@ -247,20 +243,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps @project.namespace, @project, 'master/' + new_file_name_with_directory)) end - step 'I am redirected to the new file on new branch' do - expect(current_path).to eq(namespace_project_blob_path( - @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)) + step 'I am redirected to the new merge request page' do + expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project)) end step 'I am redirected to the root directory' do -- cgit v1.2.1 From 2f048df4a4a83ff009d2ef2d14ee04e5a2798618 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 18 Nov 2015 11:17:41 +0100 Subject: API support, incorporated feedback --- .../projects/merge_requests_controller.rb | 4 +- app/models/commit_status.rb | 2 +- app/models/merge_request.rb | 6 +- app/services/merge_requests/merge_service.rb | 16 ++--- .../merge_when_build_succeeds_service.rb | 2 +- app/services/merge_requests/refresh_service.rb | 4 +- app/services/system_note_service.rb | 2 +- .../merge_requests/widget/open/_accept.html.haml | 2 +- .../open/_merge_when_build_succeeds.html.haml | 8 +-- app/workers/merge_worker.rb | 13 +---- config/routes.rb | 3 +- doc/api/merge_requests.md | 60 +++++++++++++++++-- lib/api/merge_requests.rb | 68 +++++++++++++++------- spec/models/merge_request_spec.rb | 14 ++++- spec/services/system_note_service_spec.rb | 16 +++++ 15 files changed, 157 insertions(+), 63 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index d58dab2d666..931298df5d8 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -171,9 +171,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.update(merge_error: nil) if params[:merge_when_build_succeeds] && @merge_request.ci_commit.active? - MergeRequests::MergeWhenBuildSucceedsService.new(@project, - current_user, - merge_params: merge_params) + MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) .execute(@merge_request) @status = :merge_when_build_succeeds else diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index b1049fab788..acc86b1a8cd 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -75,7 +75,7 @@ class CommitStatus < ActiveRecord::Base build.update_attributes finished_at: Time.now end - after_transition running: :success do |build, transition| + after_transition [:pending, :running] => :success do |build, transition| MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c5f04dbcf4c..7b372399a3a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -253,6 +253,10 @@ class MergeRequest < ActiveRecord::Base end end + def mergeable_by_or_author(user) + self.can_be_merged_by?(user) || self.author == user + end + def mr_and_commit_notes # Fetch comments only from last 100 commits commits_for_notes_limit = 100 @@ -390,7 +394,7 @@ class MergeRequest < ActiveRecord::Base def reset_merge_when_build_succeeds return unless merge_when_build_succeeds? - + self.merge_when_build_succeeds = false self.merge_user = nil self.merge_params = nil diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 7963af127e1..db8d18a7d84 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -6,15 +6,12 @@ module MergeRequests # Executed when you do merge via GitLab UI # class MergeService < MergeRequests::BaseService - attr_reader :merge_request, :commit_message + attr_reader :merge_request - def execute(merge_request, commit_message) - @commit_message = commit_message + def execute(merge_request) @merge_request = merge_request - unless @merge_request.mergeable? - return error('Merge request is not mergeable') - end + return error('Merge request is not mergeable') unless @merge_request.mergeable? merge_request.in_locked_state do if commit @@ -32,7 +29,7 @@ module MergeRequests committer = repository.user_to_committer(current_user) options = { - message: commit_message, + message: params[:commit_message] || merge_request.merge_commit_message, author: committer, committer: committer } @@ -46,6 +43,11 @@ module MergeRequests def after_merge MergeRequests::PostMergeService.new(project, current_user).execute(merge_request) + + if params[:should_remove_source_branch] + DeleteBranchService.new(@merge_request.source_project, current_user). + execute(merge_request.source_branch) + end end end end diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb index a4418360b8c..d5cae2f98f7 100644 --- a/app/services/merge_requests/merge_when_build_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb @@ -1,7 +1,7 @@ module MergeRequests class MergeWhenBuildSucceedsService < MergeRequests::BaseService def execute(merge_request) - merge_request.merge_params.merge!(params[:merge_params]) + merge_request.merge_params.merge!(params) # The service is also called when the merge params are updated. already_approved = merge_request.merge_when_build_succeeds? diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index 335ef32abce..e5024857201 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -77,9 +77,7 @@ module MergeRequests end def reset_merge_when_build_succeeds - merge_requests_for_source_branch.each do |merge_request| - merge_request.reset_merge_when_build_succeeds - end + merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds) end def find_new_commits diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index c9846e9f26f..5e8281a3fd0 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -132,7 +132,7 @@ class SystemNoteService # Called when 'merge when build succeeds' is executed def self.merge_when_build_succeeds(noteable, project, author) - body = "Approved this request to be merged automatically when the build succeeds" + body = "This merge request will be automatically merged when the build for #{noteable.ci_commit.short_sha} succeeds" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 2afc5f81251..2a51241971f 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -29,5 +29,5 @@ $('.accept_merge_request').on 'click', -> btn = $(this) btn.html(" Merge in progress") - $('.accept-mr-form').on 'ajax:sen', -> + $('.accept-mr-form').on 'ajax:send', -> $(".accept-mr-form :input").disable() diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index f3894334968..ddd1a7bd63d 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -1,9 +1,9 @@ %h4 Approved by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} to be merged automatically when - #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds + #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds. %div - - if @merge_request.merge_params["should_remove_source_branch"] + - if @merge_request.merge_params["should_remove_source_branch"].present? = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch @@ -19,9 +19,9 @@ - if remove_source_branch_button || @merge_request.can_be_merged_by?(current_user) .clearfix.prepend-top-10 - if remove_source_branch_button - = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do + = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do = icon('times') Remove Source Branch When Merged - if @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user - = link_to merge_namespace_project_merge_request_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request), remote: true, method: :delete, class: "btn btn-grouped btn-warning btn-sm" do + = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do Cancel Automatic Merge diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb index 5d1a8555b7d..c87c0a252b1 100644 --- a/app/workers/merge_worker.rb +++ b/app/workers/merge_worker.rb @@ -8,16 +8,7 @@ class MergeWorker current_user = User.find(current_user_id) merge_request = MergeRequest.find(merge_request_id) - result = MergeRequests::MergeService.new(merge_request.target_project, current_user). - execute(merge_request, params[:commit_message]) - - if result[:status] == :success && params[:should_remove_source_branch].present? - DeleteBranchService.new(merge_request.source_project, current_user). - execute(merge_request.source_branch) - - merge_request.source_project.repository.expire_branch_names - end - - result + MergeRequests::MergeService.new(merge_request.target_project, current_user, params). + execute(merge_request) end end diff --git a/config/routes.rb b/config/routes.rb index 917c3d3f1ed..993687665cc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -556,8 +556,7 @@ Gitlab::Application.routes.draw do get :diffs get :commits post :merge - delete :merge, action: :cancel_merge_when_build_succeeds - get :merge_check + post :cancel_merge_when_build_succeeds get :ci_status post :toggle_subscription end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index ffa7f2cdf14..1b2ad1caf49 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -2,8 +2,8 @@ ## List merge requests -Get all merge requests for this project. -The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). +Get all merge requests for this project. +The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. ``` @@ -292,9 +292,59 @@ PUT /projects/:id/merge_request/:merge_request_id/merge Parameters: -- `id` (required) - The ID of a project -- `merge_request_id` (required) - ID of MR -- `merge_commit_message` (optional) - Custom merge commit message +- `id` (required) - The ID of a project +- `merge_request_id` (required) - ID of MR +- `merge_commit_message` (optional) - Custom merge commit message +- `should_remove_source_branch` (optional) - if `true` removes the source branch +- `merge_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds + +```json +{ + "id": 1, + "target_branch": "master", + "source_branch": "test1", + "project_id": 3, + "title": "test1", + "state": "merged", + "upvotes": 0, + "downvotes": 0, + "author": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + }, + "assignee": { + "id": 1, + "username": "admin", + "email": "admin@example.com", + "name": "Administrator", + "state": "active", + "created_at": "2012-04-29T08:46:00Z" + } +} +``` + +## Cancel Merge When Build Succeeds + +Cancels the merge when build succeeds and reset the merge parameters + +If successfull you'll get `200 OK`. + +If you don't have permissions to accept this merge request - you'll get a 401 + +If the merge request is already merged or closed - you get 405 and error message 'Method Not Allowed' + +In case the merge request is not set to be merged when the build succeeds, you'll also get a 405 with the error message 'Method Not Allowed' +``` +PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds +``` +Parameters: + +- `id` (required) - The ID of a project +- `merge_request_id` (required) - ID of MR ```json { diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 6eb84baf9cb..f981432db36 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -179,9 +179,10 @@ module API # Merge MR # # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # merge_commit_message (optional) - Custom merge commit message + # id (required) - The ID of a project + # merge_request_id (required) - ID of MR + # merge_commit_message (optional) - Custom merge commit message + # merge_when_build_succeeds (optional) - truethy when this MR should be merged when the build is succesfull # Example: # PUT /projects/:id/merge_request/:merge_request_id/merge # @@ -191,34 +192,57 @@ module API allowed = ::Gitlab::GitAccess.new(current_user, user_project). can_push_to_branch?(merge_request.target_branch) - if allowed - if merge_request.unchecked? - merge_request.check_if_can_be_merged - end + # Merge request can not be merged + # because user dont have permissions to push into target branch + unauthorized! unless allowed + + not_allowed! unless merge_request.open? - if merge_request.open? && !merge_request.work_in_progress? - if merge_request.can_be_merged? - commit_message = params[:merge_commit_message] || merge_request.merge_commit_message + merge_request.check_if_can_be_merged if merge_request.unchecked? - ::MergeRequests::MergeService.new(merge_request.target_project, current_user). - execute(merge_request, commit_message) + merge_params = { + commit_message: params[:merge_commit_message], + should_remove_source_branch: params[:should_remove_source_branch] + } - present merge_request, with: Entities::MergeRequest - else - render_api_error!('Branch cannot be merged', 405) - end + if !merge_request.work_in_progress? + if parse_boolean(params[:merge_when_build_succeeds]) + ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). + execute(merge_request) else - # Merge request can not be merged - # because it is already closed/merged or marked as WIP - not_allowed! + ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params). + execute(merge_request, merge_params) end else - # Merge request can not be merged - # because user dont have permissions to push into target branch - unauthorized! + render_api_error!('Branch cannot be merged', 405) end + + present merge_request, with: Entities::MergeRequest end + # Cancel Merge if Merge When build succeeds is enabled + # Parameters: + # id (required) - The ID of a project + # merge_request_id (required) - ID of MR + # + post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do + merge_request = user_project.merge_requests.find(params[:merge_request_id]) + + allowed = ::Gitlab::GitAccess.new(current_user, user_project). + can_push_to_branch?(merge_request.target_branch) + + # Merge request can not be merged + # because user dont have permissions to push into target branch + unauthorized! unless allowed + + if merge_request.merged? || !merge_request.open? || !merge_request.merge_when_build_succeeds? + not_allowed! + end + + merge_request.reset_merge_when_build_succeeds + + present merge_request, with: Entities::MergeRequest + end # Get a merge request's comments # diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index eed2cbc5412..e42534a8491 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -30,7 +30,7 @@ describe MergeRequest do describe 'associations' do it { is_expected.to belong_to(:target_project).with_foreign_key(:target_project_id).class_name('Project') } it { is_expected.to belong_to(:source_project).with_foreign_key(:source_project_id).class_name('Project') } - + it { is_expected.to belong_to(:merge_user).class_name("User") } it { is_expected.to have_one(:merge_request_diff).dependent(:destroy) } end @@ -53,6 +53,8 @@ describe MergeRequest do it { is_expected.to respond_to(:unchecked?) } it { is_expected.to respond_to(:can_be_merged?) } it { is_expected.to respond_to(:cannot_be_merged?) } + it { is_expected.to respond_to(:merge_params) } + it { is_expected.to respond_to(:merge_when_build_succeeds) } end describe '#to_reference' do @@ -171,6 +173,16 @@ describe MergeRequest do end end + describe "#reset_merge_when_build_succeeds" do + let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true } + it "sets the item to false" do + merge_if_green.reset_merge_when_build_succeeds + merge_if_green.reload + + expect(merge_if_green.merge_when_build_succeeds).to be_falsey + end + end + describe "#hook_attrs" do it "has all the required keys" do attrs = subject.hook_attrs diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index a45130bd473..35912ece644 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -207,6 +207,22 @@ describe SystemNoteService do end end + describe '.merge_when_build_succeeds' do + let(:ci_commit) { create :ci_commit, gl_project: project } + let(:merge_request) { create :merge_request, project: project } + + subject { described_class.merge_when_build_succeeds(merge_request, project, author) } + + it_behaves_like 'a system note' + + it "posts the Merge When Build Succeeds system note" do + allow(merge_request).to receive(:ci_commit).and_return(ci_commit) + allow(ci_commit).to receive(:short_sha).and_return('12345678') + + expect(subject.note).to eq "This merge request will be automatically merged when the build for 12345678 succeeds" + end + end + describe '.change_title' do subject { described_class.change_title(noteable, project, author, 'Old title') } -- cgit v1.2.1 From 531177757eef772cc7ce5dd3898c3e6803187ed6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:19:06 +0100 Subject: Add import_error to project. --- db/migrate/20151110125604_add_import_error_to_project.rb | 5 +++++ db/schema.rb | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151110125604_add_import_error_to_project.rb diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb new file mode 100644 index 00000000000..7fc990f8d0a --- /dev/null +++ b/db/migrate/20151110125604_add_import_error_to_project.rb @@ -0,0 +1,5 @@ +class AddImportErrorToProject < ActiveRecord::Migration + def change + add_column :projects, :import_error, :text + end +end diff --git a/db/schema.rb b/db/schema.rb index aa76cef9fe4..440a33e2006 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -640,7 +640,10 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 + t.integer "commit_count", default: 0 + t.boolean "merge_requests_ff_only_enabled", default: false + t.text "issues_template" + t.text "import_error" end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree -- cgit v1.2.1 From 841a7c6897b23957286056498cc3f05ec4330d15 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:22:51 +0100 Subject: Store and show reason why import failed. --- app/models/project.rb | 10 +++-- app/views/projects/imports/new.html.haml | 12 ++++-- app/workers/repository_fork_worker.rb | 14 +++---- app/workers/repository_import_worker.rb | 67 +++++++++++++++++++------------- lib/gitlab/backend/shell.rb | 7 ++-- 5 files changed, 65 insertions(+), 45 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index a099a67cf63..c2ff103759a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -310,15 +310,17 @@ class Project < ActiveRecord::Base def add_import_job if forked? - unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path) - import_fail - end + RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path) else - RepositoryImportWorker.perform_async(id) + RepositoryImportWorker.perform_async(self.id) end end def clear_import_data + update(import_error: nil) + + ProjectCacheWorker.perform_async(self.id) + self.import_data.destroy if self.import_data end diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 92a87690c54..f7b4416d87e 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -1,12 +1,16 @@ - page_title "Import repository" %h3.page-title - - if @project.import_failed? - Import failed. Retry? - - else - Import repository + Import repository %hr +- if @project.import_failed? + .alert.alert-danger + %p The repository could not be imported. + %pre.prepend-top-10 + :preserve + #{@project.import_error.try(:strip)} + = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| .form-group.import-url-data = f.label :import_url, class: 'control-label' do diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index acd1c43f06b..2f991c52339 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -13,22 +13,20 @@ class RepositoryForkWorker end result = gitlab_shell.fork_repository(source_path, target_path) - unless result logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}") + project.update(import_error: "The project could not be forked.") project.import_fail - project.save return end - if project.valid_repo? - ProjectCacheWorker.perform_async(project.id) - project.import_finish - else - project.import_fail + unless project.valid_repo? logger.error("Project #{id} had an invalid repository after fork") + project.update(import_error: "The forked repository is invalid.") + project.import_fail + return end - project.save + project.import_finish end end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index ea2808045eb..5be2245df28 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -7,37 +7,52 @@ class RepositoryImportWorker def perform(project_id) project = Project.find(project_id) - unless project.import_url == Project::UNKNOWN_IMPORT_URL - import_result = gitlab_shell.send(:import_repository, - project.path_with_namespace, - project.import_url) - return project.import_fail unless import_result - else + if project.import_url == Project::UNKNOWN_IMPORT_URL + # In this case, we only want to import issues, not a repository. unless project.create_repository - return project.import_fail + project.update(import_error: "The repository could not be created.") + project.import_fail + return + end + else + begin + import_result = gitlab_shell.import_repository(project.path_with_namespace, project.import_url) + rescue Gitlab::Shell::Error => e + project.update(import_error: e.message) + project.import_fail + return end end - data_import_result = case project.import_type - when 'github' - Gitlab::GithubImport::Importer.new(project).execute - when 'gitlab' - Gitlab::GitlabImport::Importer.new(project).execute - when 'bitbucket' - Gitlab::BitbucketImport::Importer.new(project).execute - when 'google_code' - Gitlab::GoogleCodeImport::Importer.new(project).execute - when 'fogbugz' - Gitlab::FogbugzImport::Importer.new(project).execute - else - true - end - return project.import_fail unless data_import_result - - Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' + data_import_result = + case project.import_type + when 'github' + Gitlab::GithubImport::Importer.new(project).execute + when 'gitlab' + Gitlab::GitlabImport::Importer.new(project).execute + when 'bitbucket' + Gitlab::BitbucketImport::Importer.new(project).execute + when 'google_code' + Gitlab::GoogleCodeImport::Importer.new(project).execute + when 'fogbugz' + Gitlab::FogbugzImport::Importer.new(project).execute + else + true + end + + unless data_import_result + project.update(import_error: "The remote issue data could not be imported.") + project.import_fail + return + end + + if project.import_type == 'bitbucket' + Gitlab::BitbucketImport::KeyDeleter.new(project).execute + end project.import_finish - project.save - ProjectCacheWorker.perform_async(project.id) + + # Explicitly update mirror so that upstream remote is created and fetched + project.update_mirror end end diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 01b8bda05c6..87ac30b5ffe 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -1,6 +1,6 @@ module Gitlab class Shell - class AccessDenied < StandardError; end + class Error < StandardError; end class KeyAdder < Struct.new(:io) def add_key(id, key) @@ -36,8 +36,9 @@ module Gitlab # import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git") # def import_repository(name, url) - Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'import-project', - "#{name}.git", url, '240']) + output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240']) + raise Error, output unless status.zero? + true end # Move repository -- cgit v1.2.1 From 40470975e863b271f09fe147eb2eb545211e1b08 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:23:51 +0100 Subject: Add Project#safe_import_url helper. --- app/helpers/projects_helper.rb | 8 -------- app/models/project.rb | 8 ++++++++ app/views/projects/imports/show.html.haml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 690ae2090db..c9cd4a0d54c 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -253,14 +253,6 @@ module ProjectsHelper filename_path(project, :version) end - def hidden_pass_url(original_url) - result = URI(original_url) - result.password = '*****' unless result.password.nil? - result - rescue - original_url - end - def project_wiki_path_with_version(proj, page, version, is_newest) url_params = is_newest ? {} : { version_id: version } namespace_project_wiki_path(proj.namespace, proj, page, url_params) diff --git a/app/models/project.rb b/app/models/project.rb index c2ff103759a..70a648e68a3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -348,6 +348,14 @@ class Project < ActiveRecord::Base import_status == 'finished' end + def safe_import_url + result = URI.parse(self.import_url) + result.password = '*****' unless result.password.nil? + result.to_s + rescue + original_url + end + def check_limit unless creator.can_create_project? or namespace.kind == 'group' errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it") diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml index 06886d215a3..c0d1ce0d120 100644 --- a/app/views/projects/imports/show.html.haml +++ b/app/views/projects/imports/show.html.haml @@ -8,7 +8,7 @@ - else Import in progress. - unless @project.forked? - %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)} + %p.monospace git clone --bare #{@project.safe_import_url} %p Please wait while we import the repository for you. Refresh at will. :javascript new ProjectImport(); -- cgit v1.2.1 From 01d2b1943f009720a3f8a51e441dcc18f1706c9f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:26:12 +0100 Subject: Move import form to partial. --- app/assets/stylesheets/framework/common.scss | 4 ++++ app/views/projects/imports/new.html.haml | 12 ++---------- app/views/projects/new.html.haml | 15 +-------------- app/views/shared/_import_form.html.haml | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 24 deletions(-) create mode 100644 app/views/shared/_import_form.html.haml diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index ddbacd7fd41..40f4beb1968 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -328,6 +328,10 @@ table { } } +.well { + margin-bottom: 0; +} + .search_box { @extend .well; text-align: center; diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index f7b4416d87e..01b247336ee 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -12,15 +12,7 @@ #{@project.import_error.try(:strip)} = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Import existing git repo - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' - .well.prepend-top-20 - This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. - %br - The import will time out after 4 minutes. For big repositories, use a clone/push combination. - For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"} + = render "shared/import_form", f: f + .form-actions = f.submit 'Start import', class: "btn btn-create", tabindex: 4 diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index a02c12f06a8..c9d1fc3da21 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -23,7 +23,6 @@ = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} - if import_sources_enabled? - .project-import.js-toggle-container .form-group %label.control-label Import project from @@ -82,19 +81,7 @@ %span Any repo by URL .js-toggle-content.hide - .form-group.import-url-data - = f.label :import_url, class: 'control-label' do - %span Git repository URL - .col-sm-10 - = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' - .well.prepend-top-20 - %ul - %li - The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: https://username:password@gitlab.company.com/group/project.git. - %li - The import will time out after 4 minutes. For big repositories, use a clone/push combination. - %li - To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}. + = render "shared/import_form", f: f .prepend-botton-10 diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml new file mode 100644 index 00000000000..285af56ad73 --- /dev/null +++ b/app/views/shared/_import_form.html.haml @@ -0,0 +1,16 @@ +.form-group.import-url-data + = f.label :import_url, class: 'control-label' do + %span Git repository URL + .col-sm-10 + = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git' + + .well.prepend-top-20 + %ul + %li + The repository must be accessible over http://, https:// or git://. + %li + If your HTTP repository is not publicly accessible, add authentication information to the URL: https://username:password@gitlab.company.com/group/project.git. + %li + The import will time out after 4 minutes. For big repositories, use a clone/push combination. + %li + To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}. -- cgit v1.2.1 From 7b405d306431448f384591de792497e719d71caa Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:25:01 +0100 Subject: Fix redirect after import fails. --- app/controllers/projects/imports_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 066b66014f8..fb8788f0818 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -28,8 +28,8 @@ class Projects::ImportsController < Projects::ApplicationController if @project.import_finished? redirect_to(project_path(@project)) and return else - redirect_to new_namespace_project_import_path(@project.namespace, - @project) && return + redirect_to(new_namespace_project_import_path(@project.namespace, + @project)) and return end end end -- cgit v1.2.1 From a0519818cb4ff90d6a1d66b6360ec94f05bc79ec Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:26:43 +0100 Subject: Tweak code formatting. --- app/services/projects/create_service.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 5b84527eccf..700a1db04d8 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -55,7 +55,9 @@ module Projects @project.save if @project.persisted? && !@project.import? - raise 'Failed to create repository' unless @project.create_repository + unless @project.create_repository + raise 'Failed to create repository' + end end end @@ -94,9 +96,7 @@ module Projects @project.team << [current_user, :master, current_user] end - if @project.import? - @project.import_start - end + @project.import_start if @project.import? end end end -- cgit v1.2.1 From 153085a93bf7f2350d15009fe7924cea190cd7f2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:27:55 +0100 Subject: Add Repository#is_ancestor? convenience method. --- app/models/repository.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index f76b770e867..c5b6ee80dc6 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -484,7 +484,7 @@ class Repository root_ref_commit = commit(root_ref) if branch_commit - rugged.merge_base(root_ref_commit.id, branch_commit.id) == branch_commit.id + is_ancestor?(branch_commit.id, root_ref_commit.id) else nil end @@ -494,6 +494,11 @@ class Repository rugged.merge_base(first_commit_id, second_commit_id) end + def is_ancestor?(ancestor_id, descendant_id) + merge_base(ancestor_id, descendant_id) == ancestor_id + end + + def search_files(query, ref) offset = 2 args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) -- cgit v1.2.1 From b1f4d14f7d521b1ceb167a52a188eb93e9384db4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 11 Nov 2015 16:28:31 +0100 Subject: Clean up Repository cache code. --- app/models/repository.rb | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index c5b6ee80dc6..fd60c7edbb1 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -105,29 +105,25 @@ class Repository end def add_branch(branch_name, ref) - cache.expire(:branch_names) - @branches = nil + expire_branches_cache gitlab_shell.add_branch(path_with_namespace, branch_name, ref) end def add_tag(tag_name, ref, message = nil) - cache.expire(:tag_names) - @tags = nil + expire_tags_cache gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end def rm_branch(branch_name) - cache.expire(:branch_names) - @branches = nil + expire_branches_cache gitlab_shell.rm_branch(path_with_namespace, branch_name) end def rm_tag(tag_name) - cache.expire(:tag_names) - @tags = nil + expire_tags_cache gitlab_shell.rm_tag(path_with_namespace, tag_name) end @@ -169,6 +165,16 @@ class Repository end end + def expire_tags_cache + cache.expire(:tag_names) + @tags = nil + end + + def expire_branches_cache + cache.expire(:branch_names) + @branches = nil + end + def expire_cache cache_keys.each do |key| cache.expire(key) -- cgit v1.2.1 From 91fdbdcde7f2d58f6f5987640eed538588822b93 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:44:33 +0100 Subject: Add tooltips to home panel buttons. --- app/views/projects/_home_panel.html.haml | 15 +++++---------- app/views/projects/buttons/_download.html.haml | 4 ++++ app/views/projects/buttons/_dropdown.html.haml | 2 -- app/views/projects/buttons/_fork.html.haml | 25 +++++++++++++------------ app/views/projects/buttons/_star.html.haml | 2 +- app/views/shared/_clone_panel.html.haml | 5 ++--- 6 files changed, 25 insertions(+), 28 deletions(-) create mode 100644 app/views/projects/buttons/_download.html.haml diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 8c0980369fd..88d54bf6f21 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -18,17 +18,12 @@ .project-repo-buttons .split-one = render 'projects/buttons/star' + = render 'projects/buttons/fork' - - unless empty_repo - = render 'projects/buttons/fork' - = render "shared/clone_panel" - .split-repo-buttons - - unless empty_repo - - if can? current_user, :download_code, @project - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do - = icon('download fw') - + + .split-repo-buttons + = render "projects/buttons/download" = render 'projects/buttons/dropdown' + = render 'projects/buttons/notifications' - diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml new file mode 100644 index 00000000000..14ee2263b7d --- /dev/null +++ b/app/views/projects/buttons/_download.html.haml @@ -0,0 +1,4 @@ +- unless @project.empty_repo? + - if can? current_user, :download_code, @project + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', rel: 'nofollow', title: "Download ZIP" do + = icon('download') diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 18cae8ef6d3..b277b765b6b 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -32,5 +32,3 @@ = link_to new_namespace_project_tag_path(@project.namespace, @project) do = icon('tags fw') New tag - - diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 8f2f631eb7d..2d3abf09051 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -1,12 +1,13 @@ -- if current_user && can?(current_user, :fork_project, @project) - - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn' do - = icon('code-fork fw') - Fork - %span.count - = @project.forks_count - - else - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn' do - = icon('code-fork fw') - %span.count - = @project.forks_count +- unless @project.empty_repo? + - if current_user && can?(current_user, :fork_project, @project) + - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do + = icon('code-fork fw') + Fork + %span.count + = @project.forks_count + - else + = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do + = icon('code-fork fw') + %span.count + = @project.forks_count diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 06583902035..41a3ec6d90f 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star', method: :post, remote: true do + = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do = icon('star fw') %span.count = @project.star_count diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 2e4aab36301..8bcb24ae9df 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -21,7 +21,6 @@ = gitlab_config.protocol.upcase = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true - if project.kind_of?(Project) - .input-group-addon - .visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(project.visibility_level)} project" } + .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } + .visibility-level-label = visibility_level_icon(project.visibility_level) - -- cgit v1.2.1 From 5036d8d5d55a8b22e423fb440d75f3d3949038d6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:45:49 +0100 Subject: Fix styling of import error. --- app/assets/stylesheets/framework/tw_bootstrap.scss | 2 +- app/views/projects/imports/new.html.haml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 99d028d1228..50c0cf61f4e 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -172,7 +172,7 @@ } .panel-body { - form { + form, pre { margin: 0; } diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml index 01b247336ee..6027fb23360 100644 --- a/app/views/projects/imports/new.html.haml +++ b/app/views/projects/imports/new.html.haml @@ -5,11 +5,12 @@ %hr - if @project.import_failed? - .alert.alert-danger - %p The repository could not be imported. - %pre.prepend-top-10 - :preserve - #{@project.import_error.try(:strip)} + .panel.panel-danger + .panel-heading The repository could not be imported. + .panel-body + %pre + :preserve + #{@project.import_error.try(:strip)} = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| = render "shared/import_form", f: f -- cgit v1.2.1 From 5e8ddd3c2d898367f6bb377df02e40754850d4f3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 12:53:00 +0100 Subject: Remove unused variable in repository import --- app/workers/repository_import_worker.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 5be2245df28..1de49161997 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -16,7 +16,7 @@ class RepositoryImportWorker end else begin - import_result = gitlab_shell.import_repository(project.path_with_namespace, project.import_url) + gitlab_shell.import_repository(project.path_with_namespace, project.import_url) rescue Gitlab::Shell::Error => e project.update(import_error: e.message) project.import_fail -- cgit v1.2.1 From d0ec28827d33d00ca637b1962b370313701ac3a0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 13:46:50 +0100 Subject: Fix specs --- features/steps/dashboard/new_project.rb | 1 - spec/services/projects/fork_service_spec.rb | 17 +++++------------ spec/workers/repository_fork_worker_spec.rb | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb index 44a4aa9844a..a0aad66184d 100644 --- a/features/steps/dashboard/new_project.rb +++ b/features/steps/dashboard/new_project.rb @@ -44,7 +44,6 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps git_import_instructions = first('.js-toggle-content') expect(git_import_instructions).to be_visible expect(git_import_instructions).to have_content "Git repository URL" - expect(git_import_instructions).to have_content "The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL:" end step 'I click on "Google Code"' do diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index e397b2b9b4a..1feba6ce048 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -25,13 +25,6 @@ describe Projects::ForkService do end end - context 'fork project failure' do - it "fails due to transaction failure" do - @to_project = fork_project(@from_project, @to_user, false) - expect(@to_project.import_failed?) - end - end - context 'project already exists' do it "should fail due to validation, not transaction failure" do @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace) @@ -66,7 +59,7 @@ describe Projects::ForkService do context 'fork project for group' do it 'group owner successfully forks project into the group' do - to_project = fork_project(@project, @group_owner, true, @opts) + to_project = fork_project(@project, @group_owner, @opts) expect(to_project.owner).to eq(@group) expect(to_project.namespace).to eq(@group) expect(to_project.name).to eq(@project.name) @@ -78,7 +71,7 @@ describe Projects::ForkService do context 'fork project for group when user not owner' do it 'group developer should fail to fork project into the group' do - to_project = fork_project(@project, @developer, true, @opts) + to_project = fork_project(@project, @developer, @opts) expect(to_project.errors[:namespace]).to eq(['is not valid']) end end @@ -87,7 +80,7 @@ describe Projects::ForkService do it 'should fail due to validation, not transaction failure' do existing_project = create(:project, name: @project.name, namespace: @group) - to_project = fork_project(@project, @group_owner, true, @opts) + to_project = fork_project(@project, @group_owner, @opts) expect(existing_project.persisted?).to be_truthy expect(to_project.errors[:name]).to eq(['has already been taken']) expect(to_project.errors[:path]).to eq(['has already been taken']) @@ -95,8 +88,8 @@ describe Projects::ForkService do end end - def fork_project(from_project, user, fork_success = true, params = {}) - allow(RepositoryForkWorker).to receive(:perform_async).and_return(fork_success) + def fork_project(from_project, user, params = {}) + allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) Projects::ForkService.new(from_project, user, params).execute end end diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb index aa031106968..245f066df1f 100644 --- a/spec/workers/repository_fork_worker_spec.rb +++ b/spec/workers/repository_fork_worker_spec.rb @@ -12,7 +12,6 @@ describe RepositoryForkWorker do project.path_with_namespace, fork_project.namespace.path). and_return(true) - expect(ProjectCacheWorker).to receive(:perform_async) subject.perform(project.id, project.path_with_namespace, -- cgit v1.2.1 From 8937c5ff7adb309f7b52a3ecf7dfe3181e2ceb1b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 12 Nov 2015 13:47:48 +0100 Subject: Load raw repository lazily to recover from failed read --- app/models/repository.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index fd60c7edbb1..c1836103463 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -6,7 +6,7 @@ class Repository include Gitlab::ShellAdapter - attr_accessor :raw_repository, :path_with_namespace, :project + attr_accessor :path_with_namespace, :project def self.clean_old_archives repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path @@ -19,14 +19,18 @@ class Repository def initialize(path_with_namespace, default_branch = nil, project = nil) @path_with_namespace = path_with_namespace @project = project + end - if path_with_namespace - @raw_repository = Gitlab::Git::Repository.new(path_to_repo) - @raw_repository.autocrlf = :input - end + def raw_repository + return nil unless path_with_namespace - rescue Gitlab::Git::Repository::NoRepository - nil + @raw_repository ||= begin + repo = Gitlab::Git::Repository.new(path_to_repo) + repo.autocrlf = :input + repo + rescue Gitlab::Git::Repository::NoRepository + nil + end end # Return absolute path to repository -- cgit v1.2.1 From e809669383d528e80d6408c4838566cf9f0bb8ac Mon Sep 17 00:00:00 2001 From: Arseny Zarechnev Date: Tue, 17 Nov 2015 17:47:39 +0000 Subject: Fix github importer to handle empty issues --- lib/gitlab/github_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index bd7340a80f1..b5720f6e2cb 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -19,7 +19,7 @@ module Gitlab if issue.pull_request.nil? body = @formatter.author_line(issue.user.login) - body += issue.body + body += issue.body || "" if issue.comments > 0 body += @formatter.comments_header -- cgit v1.2.1 From 7763e61ae59b9b610cb87b30ef5ca90c94a011ea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 12:09:33 +0100 Subject: Use gitlab-shell 2.6.7 --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 338a5b5d8fe..e261122d5c4 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.6 +2.6.7 -- cgit v1.2.1 From f5e3d93c28ff9e1a7e0ba9bddbbc242709f0fa8b Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:01:35 +0100 Subject: Check which folders and archives should be packed before passing to tar command. --- lib/backup/manager.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 9e15d5411a1..69922eb66ea 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -150,17 +150,15 @@ module Backup private def backup_contents - folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"] + folders_to_backup + archives_to_backup + ["backup_information.yml"] end - def folders_to_backup - folders = %w{repositories db} - - if ENV["SKIP"] - return folders.reject{ |folder| ENV["SKIP"].include?(folder) } - end + def archives_to_backup + %w{uploads builds artifacts}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact + end - folders + def folders_to_backup + %w{repositories db}.map{ |name| name unless skipped?(name) }.compact end def settings -- cgit v1.2.1 From 054f2f98eda60682fd796a1f66681accc6f7ce1c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 17:14:47 +0100 Subject: Faster way of obtaining latest event update time Instead of using MAX(events.updated_at) we can simply sort the events in descending order by the "id" column and grab the first row. In other words, instead of this: SELECT max(events.updated_at) AS max_id FROM events LEFT OUTER JOIN projects ON projects.id = events.project_id LEFT OUTER JOIN namespaces ON namespaces.id = projects.namespace_id WHERE events.author_id IS NOT NULL AND events.project_id IN (13083); we can use this: SELECT events.updated_at AS max_id FROM events LEFT OUTER JOIN projects ON projects.id = events.project_id LEFT OUTER JOIN namespaces ON namespaces.id = projects.namespace_id WHERE events.author_id IS NOT NULL AND events.project_id IN (13083) ORDER BY events.id DESC LIMIT 1; This has the benefit that on PostgreSQL a backwards index scan can be used, which due to the "LIMIT 1" will at most process only a single row. This in turn greatly speeds up the process of grabbing the latest update time. This can be confirmed by looking at the query plans. The first query produces the following plan: Aggregate (cost=43779.84..43779.85 rows=1 width=12) (actual time=2142.462..2142.462 rows=1 loops=1) -> Index Scan using index_events_on_project_id on events (cost=0.43..43704.69 rows=30060 width=12) (actual time=0.033..2138.086 rows=32769 loops=1) Index Cond: (project_id = 13083) Filter: (author_id IS NOT NULL) Planning time: 1.248 ms Execution time: 2142.548 ms The second query in turn produces the following plan: Limit (cost=0.43..41.65 rows=1 width=16) (actual time=1.394..1.394 rows=1 loops=1) -> Index Scan Backward using events_pkey on events (cost=0.43..1238907.96 rows=30060 width=16) (actual time=1.394..1.394 rows=1 loops=1) Filter: ((author_id IS NOT NULL) AND (project_id = 13083)) Rows Removed by Filter: 2104 Planning time: 0.166 ms Execution time: 1.408 ms According to the above plans the 2nd query is around 1500 times faster. However, re-running the first query produces timings of around 80 ms, making the 2nd query "only" around 55 times faster. --- app/models/event.rb | 6 ++++++ app/views/dashboard/projects/index.atom.builder | 2 +- app/views/groups/show.atom.builder | 2 +- app/views/projects/show.atom.builder | 2 +- app/views/users/show.atom.builder | 2 +- spec/models/event_spec.rb | 21 +++++++++++++++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/models/event.rb b/app/models/event.rb index bf64ac29d32..7618b503d84 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -63,6 +63,12 @@ class Event < ActiveRecord::Base Event::PUSHED, ["MergeRequest", "Issue"], [Event::CREATED, Event::CLOSED, Event::MERGED]) end + + def latest_update_time + row = select(:updated_at, :project_id).reorder(id: :desc).take + + row ? row.updated_at : nil + end end def proper? diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder index d2c51486841..c8c219f4cca 100644 --- a/app/views/dashboard/projects/index.atom.builder +++ b/app/views/dashboard/projects/index.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html" xml.id dashboard_projects_url - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index a91d1a6e94b..7ea574434c3 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: group_url(@group), rel: "alternate", type: "text/html" xml.id group_url(@group) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder index 242684e5c7c..15c49767556 100644 --- a/app/views/projects/show.atom.builder +++ b/app/views/projects/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml" xml.link href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html" xml.id namespace_project_url(@project.namespace, @project) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder index 50232dc7186..2fe5b7fac83 100644 --- a/app/views/users/show.atom.builder +++ b/app/views/users/show.atom.builder @@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml" xml.link href: user_url(@user), rel: "alternate", type: "text/html" xml.id user_url(@user) - xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? + xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any? @events.each do |event| event_to_atom(xml, event) diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 0f32f162a10..f46c50ed6f3 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -64,4 +64,25 @@ describe Event do it { expect(@event.branch_name).to eq("master") } it { expect(@event.author).to eq(@user) } end + + describe '.latest_update_time' do + describe 'when events are present' do + let(:time) { Time.utc(2015, 1, 1) } + + before do + create(:closed_issue_event, updated_at: time) + create(:closed_issue_event, updated_at: time + 5) + end + + it 'returns the latest update time' do + expect(Event.latest_update_time).to eq(time + 5) + end + end + + describe 'when no events exist' do + it 'returns nil' do + expect(Event.latest_update_time).to be_nil + end + end + end end -- cgit v1.2.1 From d769596aec7daa2ff43f86c8fad4211fbc4f607d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:16:49 +0100 Subject: Added Gitlab::SQL::Union class This class can be used to join multiple AcitveRecord::Relation objects together using a SQL UNION statement. ActiveRecord < 5.0 sadly doesn't support UNION and existing Gems out there don't handle prepared statements (e.g. they never incremented the variable bindings). --- lib/gitlab/sql/union.rb | 34 ++++++++++++++++++++++++++++++++++ spec/lib/gitlab/sql/union_spec.rb | 16 ++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 lib/gitlab/sql/union.rb create mode 100644 spec/lib/gitlab/sql/union_spec.rb diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb new file mode 100644 index 00000000000..1a62eff0b31 --- /dev/null +++ b/lib/gitlab/sql/union.rb @@ -0,0 +1,34 @@ +module Gitlab + module SQL + # Class for building SQL UNION statements. + # + # ORDER BYs are dropped from the relations as the final sort order is not + # guaranteed any way. + # + # Example usage: + # + # union = Gitlab::SQL::Union.new(user.personal_projects, user.projects) + # sql = union.to_sql + # + # Project.where("id IN (#{sql})") + class Union + def initialize(relations) + @relations = relations + end + + def to_sql + # Some relations may include placeholders for prepared statements, these + # aren't incremented properly when joining relations together this way. + # By using "unprepared_statements" we remove the usage of placeholders + # (thus fixing this problem), at a slight performance cost. + fragments = ActiveRecord::Base.connection.unprepared_statement do + @relations.map do |rel| + "(#{rel.reorder(nil).to_sql})" + end + end + + fragments.join(' UNION ') + end + end + end +end diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb new file mode 100644 index 00000000000..976360af9b5 --- /dev/null +++ b/spec/lib/gitlab/sql/union_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe Gitlab::SQL::Union do + describe '#to_sql' do + it 'returns a String joining relations together using a UNION' do + rel1 = User.where(email: 'alice@example.com') + rel2 = User.where(email: 'bob@example.com') + union = described_class.new([rel1, rel2]) + + sql1 = rel1.reorder(nil).to_sql + sql2 = rel2.reorder(nil).to_sql + + expect(union.to_sql).to eq("(#{sql1}) UNION (#{sql2})") + end + end +end -- cgit v1.2.1 From 028bd227fb1915edca181331542c433fd171d31a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:29:40 +0100 Subject: Use SQL::Union for User#authorized_projects This allows retrieving of the list of authorized projects using a single query, without having to load any IDs into Ruby. This in turn also means we can remove the method User#authorized_projects_id. --- app/models/user.rb | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 61abea1f6ea..8f34f52be1d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -397,26 +397,9 @@ class User < ActiveRecord::Base end end - def authorized_projects_id - @authorized_projects_id ||= begin - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.pluck(:id)) - project_ids.push(*projects.pluck(:id).uniq) - end - end - - def master_or_owner_projects_id - @master_or_owner_projects_id ||= begin - scope = { access_level: [ Gitlab::Access::MASTER, Gitlab::Access::OWNER ] } - project_ids = personal_projects.pluck(:id) - project_ids.push(*groups_projects.where(members: scope).pluck(:id)) - project_ids.push(*projects.where(members: scope).pluck(:id).uniq) - end - end - # Projects user has access to def authorized_projects - @authorized_projects ||= Project.where(id: authorized_projects_id) + @authorized_projects ||= Project.where("id IN (#{projects_union.to_sql})") end def owned_projects @@ -743,8 +726,8 @@ class User < ActiveRecord::Base Event.contributions.where(author_id: self). where("created_at > ?", Time.now - 1.year). reorder(project_id: :desc). - select(:project_id). - uniq.map(&:project_id) + uniq. + pluck(:project_id) end def restricted_signup_domains @@ -774,11 +757,26 @@ class User < ActiveRecord::Base !solo_owned_groups.present? end + def ci_authorized_projects + @ci_authorized_projects ||= + Ci::Project.where("gitlab_id IN (#{projects_union.to_sql})") + end + def ci_authorized_runners @ci_authorized_runners ||= begin runner_ids = Ci::RunnerProject.joins(:project). - where(ci_projects: { gitlab_id: master_or_owner_projects_id }).select(:runner_id) + where("ci_projects.gitlab_id IN (#{projects_union.to_sql})"). + select(:runner_id) + Ci::Runner.specific.where(id: runner_ids) end end + + private + + def projects_union + Gitlab::SQL::Union.new([personal_projects.select(:id), + groups_projects.select(:id), + projects.select(:id)]) + end end -- cgit v1.2.1 From 656d9ff69b372be8e0aebb94418c487e76e04256 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 16:30:39 +0100 Subject: Make it easier to re-apply default sort orders By moving the default sort order into a separate scope (and calling this from the default scope) we can more easily re-apply a default order without having to specify the exact column/ordering all over the place. --- app/models/concerns/sortable.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 913c747a1c3..7391a77383c 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -8,8 +8,9 @@ module Sortable included do # By default all models should be ordered # by created_at field starting from newest - default_scope { order(id: :desc) } + default_scope { order_id_desc } + scope :order_id_desc, -> { reorder(id: :desc) } scope :order_created_desc, -> { reorder(created_at: :desc) } scope :order_created_asc, -> { reorder(created_at: :asc) } scope :order_updated_desc, -> { reorder(updated_at: :desc) } -- cgit v1.2.1 From 189c40c33d18df08dd40e9f009f6658f89e3af0e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 17:38:20 +0100 Subject: Use SQL::Union for User#authorized_groups This removes the need for plucking any IDs into Ruby. --- app/models/user.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 8f34f52be1d..ddb3158e0f5 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -391,10 +391,13 @@ class User < ActiveRecord::Base # Groups user has access to def authorized_groups - @authorized_groups ||= begin - group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id)) - Group.where(id: group_ids) - end + @authorized_groups ||= + begin + union = Gitlab::SQL::Union. + new([groups.select(:id), authorized_projects.select(:namespace_id)]) + + Group.where("id IN (#{union.to_sql})") + end end # Projects user has access to -- cgit v1.2.1 From bfd9855a2b2a09e8f5bff89e84891d3ad598fe0d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 12 Nov 2015 17:50:43 +0100 Subject: Prefix table names for User UNIONs --- app/models/user.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index ddb3158e0f5..a2258967e27 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -396,13 +396,14 @@ class User < ActiveRecord::Base union = Gitlab::SQL::Union. new([groups.select(:id), authorized_projects.select(:namespace_id)]) - Group.where("id IN (#{union.to_sql})") + Group.where("namespaces.id IN (#{union.to_sql})") end end # Projects user has access to def authorized_projects - @authorized_projects ||= Project.where("id IN (#{projects_union.to_sql})") + @authorized_projects ||= + Project.where("projects.id IN (#{projects_union.to_sql})") end def owned_projects -- cgit v1.2.1 From 5fcd9986b86812786c90a0b8d3461db79ce71051 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 16 Nov 2015 14:28:02 +0100 Subject: Refactor getting user groups/projects/contributions This new setup no longer loads any IDs into memory using "pluck", instead using SQL UNIONs to merge the various datasets together. This results in greatly improved query performance as well as a reduction of memory usage. The old setup was in particular problematic when requesting the authorized projects _including_ public/internal projects as this would result in roughly 65000 project IDs being loaded into memory. These IDs would in turn be passed to other queries. --- app/models/user.rb | 64 ++++++++++++++++++++++++++++++++++++------------ spec/models/user_spec.rb | 52 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 20 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index a2258967e27..d523b3f0491 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -389,21 +389,40 @@ class User < ActiveRecord::Base end end - # Groups user has access to - def authorized_groups - @authorized_groups ||= - begin - union = Gitlab::SQL::Union. - new([groups.select(:id), authorized_projects.select(:namespace_id)]) + # Returns the groups a user has access to, optionally including any public + # groups. + # + # public_internal - When set to "true" all public groups and groups of public + # projects are also included. + # + # Returns an ActiveRecord::Relation + def authorized_groups(public_internal = false) + union = Gitlab::SQL::Union. + new([groups.select(:id), authorized_projects(public_internal). + select(:namespace_id)]) - Group.where("namespaces.id IN (#{union.to_sql})") - end + sql = "namespaces.id IN (#{union.to_sql})" + + if public_internal + sql << ' OR public IS TRUE' + end + + Group.where(sql) end - # Projects user has access to - def authorized_projects - @authorized_projects ||= - Project.where("projects.id IN (#{projects_union.to_sql})") + # Returns the groups a user is authorized to access. + # + # public_internal - When set to "true" all public/internal projects will also + # be included. + def authorized_projects(public_internal = false) + base = "projects.id IN (#{projects_union.to_sql})" + + if public_internal + Project.where("#{base} OR projects.visibility_level IN (?)", + Project.public_and_internal_levels) + else + Project.where(base) + end end def owned_projects @@ -726,12 +745,25 @@ class User < ActiveRecord::Base Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) end - def contributed_projects_ids - Event.contributions.where(author_id: self). + # Returns the projects a user contributed to in the last year. + # + # This method relies on a subquery as this performs significantly better + # compared to a JOIN when coupled with, for example, + # `Project.visible_to_user`. That is, consider the following code: + # + # some_user.contributed_projects.visible_to_user(other_user) + # + # If this method were to use a JOIN the resulting query would take roughly 200 + # ms on a database with a similar size to gitlab.com's database. On the other + # hand, using a subquery means we can get the exact same data in about 40 ms. + def contributed_projects + events = Event.select(:project_id). + contributions.where(author_id: self). where("created_at > ?", Time.now - 1.year). - reorder(project_id: :desc). uniq. - pluck(:project_id) + reorder(nil) + + Project.where(id: events) end def restricted_signup_domains diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 7d716c23120..71160f8dfef 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -686,7 +686,7 @@ describe User do end end - describe "#contributed_projects_ids" do + describe "#contributed_projects" do subject { create(:user) } let!(:project1) { create(:project) } let!(:project2) { create(:project, forked_from_project: project3) } @@ -701,15 +701,15 @@ describe User do end it "includes IDs for projects the user has pushed to" do - expect(subject.contributed_projects_ids).to include(project1.id) + expect(subject.contributed_projects).to include(project1) end it "includes IDs for projects the user has had merge requests merged into" do - expect(subject.contributed_projects_ids).to include(project3.id) + expect(subject.contributed_projects).to include(project3) end it "doesn't include IDs for unrelated projects" do - expect(subject.contributed_projects_ids).not_to include(project2.id) + expect(subject.contributed_projects).not_to include(project2) end end @@ -758,4 +758,48 @@ describe User do expect(subject.recent_push).to eq(nil) end end + + describe '#authorized_groups' do + let!(:user) { create(:user) } + let!(:private_group) { create(:group) } + let!(:public_group) { create(:group, public: true) } + + before do + private_group.add_user(user, Gitlab::Access::MASTER) + end + + describe 'excluding public groups' do + subject { user.authorized_groups } + + it { is_expected.to eq([private_group]) } + end + + describe 'including public groups' do + subject { user.authorized_groups(true) } + + it { is_expected.to eq([public_group, private_group]) } + end + end + + describe '#authorized_projects' do + let!(:user) { create(:user) } + let!(:private_project) { create(:project, :private) } + let!(:public_project) { create(:project, :public) } + + before do + private_project.team << [user, Gitlab::Access::MASTER] + end + + describe 'excluding public projects' do + subject { user.authorized_projects } + + it { is_expected.to eq([private_project]) } + end + + describe 'including public projects' do + subject { user.authorized_projects(true) } + + it { is_expected.to eq([public_project, private_project]) } + end + end end -- cgit v1.2.1 From b4646391eeb23b9f9828f0913e06ae78d298726e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:02:14 +0100 Subject: Renamed GroupsFinder spec file so the name matches --- spec/finders/group_finder_spec.rb | 15 --------------- spec/finders/groups_finder_spec.rb | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) delete mode 100644 spec/finders/group_finder_spec.rb create mode 100644 spec/finders/groups_finder_spec.rb diff --git a/spec/finders/group_finder_spec.rb b/spec/finders/group_finder_spec.rb deleted file mode 100644 index 78dc027837c..00000000000 --- a/spec/finders/group_finder_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe GroupsFinder do - let(:user) { create :user } - let!(:group) { create :group } - let!(:public_group) { create :group, public: true } - - describe :execute do - it 'finds public group' do - groups = GroupsFinder.new.execute(user) - expect(groups.size).to eq(1) - expect(groups.first).to eq(public_group) - end - end -end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb new file mode 100644 index 00000000000..78dc027837c --- /dev/null +++ b/spec/finders/groups_finder_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe GroupsFinder do + let(:user) { create :user } + let!(:group) { create :group } + let!(:public_group) { create :group, public: true } + + describe :execute do + it 'finds public group' do + groups = GroupsFinder.new.execute(user) + expect(groups.size).to eq(1) + expect(groups.first).to eq(public_group) + end + end +end -- cgit v1.2.1 From 2110247f83440f4a1044b999ff0f2630bd36e969 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:03:27 +0100 Subject: Refactoed GroupsFinder into two separate classes In the previous setup the GroupsFinder class had two distinct tasks: 1. Finding the projects user A could see 2. Finding the projects of user A that user B could see Task two was actually handled outside of the GroupsFinder (in the UsersController) by restricting the returned list of groups to those the viewed user was a member of. Moving all this logic into a single finder proved to be far too complex and confusing, hence there are now two finders: * GroupsFinder: for finding groups a user can see * JoinedGroupsFinder: for finding groups that user A is a member of, restricted to either public groups or groups user B can also see. --- app/finders/groups_finder.rb | 69 +++++++++++++++++-------------- app/finders/joined_groups_finder.rb | 49 ++++++++++++++++++++++ spec/finders/groups_finder_spec.rb | 51 +++++++++++++++++++---- spec/finders/joined_groups_finder_spec.rb | 49 ++++++++++++++++++++++ 4 files changed, 177 insertions(+), 41 deletions(-) create mode 100644 app/finders/joined_groups_finder.rb create mode 100644 spec/finders/joined_groups_finder_spec.rb diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb index b5f3176461c..91cb0f228f0 100644 --- a/app/finders/groups_finder.rb +++ b/app/finders/groups_finder.rb @@ -1,39 +1,44 @@ class GroupsFinder - def execute(current_user, options = {}) - all_groups(current_user) + # Finds the groups available to the given user. + # + # current_user - The user to find the groups for. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = groups_visible_to_user(current_user) + else + relation = public_groups + end + + relation.order_id_desc end private - def all_groups(current_user) - group_ids = if current_user - if current_user.authorized_groups.any? - # User has access to groups - # - # Return only: - # groups with public projects - # groups with internal projects - # groups with joined projects - # - Project.public_and_internal_only.pluck(:namespace_id) + - current_user.authorized_groups.pluck(:id) - else - # User has no group membership - # - # Return only: - # groups with public projects - # groups with internal projects - # - Project.public_and_internal_only.pluck(:namespace_id) - end - else - # Not authenticated - # - # Return only: - # groups with public projects - Project.public_only.pluck(:namespace_id) - end - - Group.where("public IS TRUE OR id IN(?)", group_ids) + # This method returns the groups "current_user" can see. + def groups_visible_to_user(current_user) + base = groups_for_projects(public_and_internal_projects) + + union = Gitlab::SQL::Union. + new([base.select(:id), current_user.authorized_groups.select(:id)]) + + Group.where("namespaces.id IN (#{union.to_sql})") + end + + def public_groups + groups_for_projects(public_projects) + end + + def groups_for_projects(projects) + Group.public_and_given_groups(projects.select(:namespace_id)) + end + + def public_projects + Project.unscoped.public_only + end + + def public_and_internal_projects + Project.unscoped.public_and_internal_only end end diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb new file mode 100644 index 00000000000..e7523136fea --- /dev/null +++ b/app/finders/joined_groups_finder.rb @@ -0,0 +1,49 @@ +# Class for finding the groups a user is a member of. +class JoinedGroupsFinder + def initialize(user = nil) + @user = user + end + + # Finds the groups of the source user, optionally limited to those visible to + # the current user. + # + # current_user - If given the groups of "@user" will only include the groups + # "current_user" can also see. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = groups_visible_to_user(current_user) + else + relation = public_groups + end + + relation.order_id_desc + end + + private + + # Returns the groups the user in "current_user" can see. + # + # This list includes all public/internal projects as well as the projects of + # "@user" that "current_user" also has access to. + def groups_visible_to_user(current_user) + base = @user.authorized_groups.visible_to_user(current_user) + extra = public_and_internal_groups + union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + + Group.where("namespaces.id IN (#{union.to_sql})") + end + + def public_groups + groups_for_projects(@user.authorized_projects.public_only) + end + + def public_and_internal_groups + groups_for_projects(@user.authorized_projects.public_and_internal_only) + end + + def groups_for_projects(projects) + @user.groups.public_and_given_groups(projects.select(:namespace_id)) + end +end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index 78dc027837c..4f6a000822e 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -1,15 +1,48 @@ require 'spec_helper' describe GroupsFinder do - let(:user) { create :user } - let!(:group) { create :group } - let!(:public_group) { create :group, public: true } - - describe :execute do - it 'finds public group' do - groups = GroupsFinder.new.execute(user) - expect(groups.size).to eq(1) - expect(groups.first).to eq(public_group) + describe '#execute' do + let(:user) { create(:user) } + + let(:group1) { create(:group) } + let(:group2) { create(:group) } + let(:group3) { create(:group) } + let(:group4) { create(:group, public: true) } + + let!(:public_project) { create(:project, :public, group: group1) } + let!(:internal_project) { create(:project, :internal, group: group2) } + let!(:private_project) { create(:project, :private, group: group3) } + + let(:finder) { described_class.new } + + describe 'with a user' do + subject { finder.execute(user) } + + describe 'when the user is not a member of any groups' do + it { is_expected.to eq([group4, group2, group1]) } + end + + describe 'when the user is a member of a group' do + before do + group3.add_user(user, Gitlab::Access::DEVELOPER) + end + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + + describe 'when the user is a member of a private project' do + before do + private_project.team.add_user(user, Gitlab::Access::DEVELOPER) + end + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + end + + describe 'without a user' do + subject { finder.execute } + + it { is_expected.to eq([group4, group1]) } end end end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb new file mode 100644 index 00000000000..2d9068cc720 --- /dev/null +++ b/spec/finders/joined_groups_finder_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe JoinedGroupsFinder do + describe '#execute' do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:group1) { create(:group) } + let(:group2) { create(:group) } + let(:group3) { create(:group) } + let(:group4) { create(:group, public: true) } + + let!(:public_project) { create(:project, :public, group: group1) } + let!(:internal_project) { create(:project, :internal, group: group2) } + let!(:private_project) { create(:project, :private, group: group3) } + + let(:finder) { described_class.new(source_user) } + + before do + [group1, group2, group3, group4].each do |group| + group.add_user(source_user, Gitlab::Access::MASTER) + end + end + + describe 'with a current user' do + describe 'when the current user has access to the projects of the source user' do + before do + private_project.team.add_user(current_user, Gitlab::Access::DEVELOPER) + end + + subject { finder.execute(current_user) } + + it { is_expected.to eq([group4, group3, group2, group1]) } + end + + describe 'when the current user does not have access to the projects of the source user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([group4, group2, group1]) } + end + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([group4, group1]) } + end + end +end -- cgit v1.2.1 From fbcf3bd3fc94a39982e2bb11aa61ac014326e53a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:21:06 +0100 Subject: Refactor ProjectsFinder to not pluck IDs This class now uses a UNION (when needed) instead of plucking tens of thousands of project IDs into memory. The tests have also been re-written to ensure all different use cases are tested properly (assuming I didn't forget any cases). The finder has also been broken up into 3 different finder classes: * ContributedProjectsFinder: class for getting the projects a user contributed to. * PersonalProjectsFinder: class for getting the personal projects of a user. * ProjectsFinder: class for getting generic projects visible to a given user. Previously a lot of the logic of these finders was handled directly in the users controller. --- app/finders/contributed_projects_finder.rb | 37 +++++++ app/finders/personal_projects_finder.rb | 37 +++++++ app/finders/projects_finder.rb | 121 ++++++++++------------- spec/finders/contributed_projects_finder_spec.rb | 35 +++++++ spec/finders/personal_projects_finder_spec.rb | 34 +++++++ spec/finders/projects_finder_spec.rb | 75 +++++++------- 6 files changed, 237 insertions(+), 102 deletions(-) create mode 100644 app/finders/contributed_projects_finder.rb create mode 100644 app/finders/personal_projects_finder.rb create mode 100644 spec/finders/contributed_projects_finder_spec.rb create mode 100644 spec/finders/personal_projects_finder_spec.rb diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb new file mode 100644 index 00000000000..0209649b017 --- /dev/null +++ b/app/finders/contributed_projects_finder.rb @@ -0,0 +1,37 @@ +class ContributedProjectsFinder + def initialize(user) + @user = user + end + + # Finds the projects "@user" contributed to, limited to either public projects + # or projects visible to the given user. + # + # current_user - When given the list of the projects is limited to those only + # visible by this user. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = projects_visible_to_user(current_user) + else + relation = public_projects + end + + relation.includes(:namespace).order_id_desc + end + + private + + def projects_visible_to_user(current_user) + authorized = @user.contributed_projects.visible_to_user(current_user) + + union = Gitlab::SQL::Union. + new([authorized.select(:id), public_projects.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + end + + def public_projects + @user.contributed_projects.public_only + end +end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb new file mode 100644 index 00000000000..7c039573614 --- /dev/null +++ b/app/finders/personal_projects_finder.rb @@ -0,0 +1,37 @@ +class PersonalProjectsFinder + def initialize(user) + @user = user + end + + # Finds the projects belonging to the user in "@user", limited to either + # public projects or projects visible to the given user. + # + # current_user - When given the list of projects is limited to those only + # visible by this user. + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil) + if current_user + relation = projects_visible_to_user(current_user) + else + relation = public_projects + end + + relation.includes(:namespace).order_id_desc + end + + private + + def projects_visible_to_user(current_user) + authorized = @user.personal_projects.visible_to_user(current_user) + + union = Gitlab::SQL::Union. + new([authorized.select(:id), public_projects.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + end + + def public_projects + @user.personal_projects.public_only + end +end diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index c81bb51583a..dd35c215c50 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -1,11 +1,39 @@ class ProjectsFinder - def execute(current_user, options = {}) + # Returns all projects, optionally including group projects a user has access + # to. + # + # ## Examples + # + # Retrieving all public projects: + # + # ProjectsFinder.new.execute + # + # Retrieving all public/internal projects and those the given user has access + # to: + # + # ProjectsFinder.new.execute(some_user) + # + # Retrieving all public/internal projects as well as the group's projects the + # user has access to: + # + # ProjectsFinder.new.execute(some_user, group: some_group) + # + # Returns an ActiveRecord::Relation. + def execute(current_user = nil, options = {}) group = options[:group] if group - group_projects(current_user, group) + base, extra = group_projects(current_user, group) else - all_projects(current_user) + base, extra = all_projects(current_user) + end + + if base and extra + union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + + Project.where("projects.id IN (#{union.to_sql})") + else + base end end @@ -13,77 +41,36 @@ class ProjectsFinder def group_projects(current_user, group) if current_user - if group.users.include?(current_user) - # User is group member - # - # Return ALL group projects - group.projects - else - projects_members = ProjectMember.in_projects(group.projects). - with_user(current_user) - - if projects_members.any? - # User is a project member - # - # Return only: - # public projects - # internal projects - # joined projects - # - group.projects.where( - "projects.id IN (?) OR projects.visibility_level IN (?)", - projects_members.pluck(:source_id), - Project.public_and_internal_levels - ) - else - # User has no access to group or group projects - # - # Return only: - # public projects - # internal projects - # - group.projects.public_and_internal_only - end - end + [ + group_projects_for_user(current_user, group), + group.projects.public_and_internal_only + ] else - # Not authenticated - # - # Return only: - # public projects - group.projects.public_only + [group.projects.public_only] end end def all_projects(current_user) if current_user - if current_user.authorized_projects.any? - # User has access to private projects - # - # Return only: - # public projects - # internal projects - # joined projects - # - Project.where( - "projects.id IN (?) OR projects.visibility_level IN (?)", - current_user.authorized_projects.pluck(:id), - Project.public_and_internal_levels - ) - else - # User has no access to private projects - # - # Return only: - # public projects - # internal projects - # - Project.public_and_internal_only - end + [current_user.authorized_projects, public_and_internal_projects] else - # Not authenticated - # - # Return only: - # public projects - Project.public_only + [Project.public_only] end end + + def group_projects_for_user(current_user, group) + if group.users.include?(current_user) + group.projects + else + group.projects.visible_to_user(current_user) + end + end + + def public_projects + Project.unscoped.public_only + end + + def public_and_internal_projects + Project.unscoped.public_and_internal_only + end end diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb new file mode 100644 index 00000000000..8c1e46fe9f2 --- /dev/null +++ b/spec/finders/contributed_projects_finder_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe ContributedProjectsFinder do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:finder) { described_class.new(source_user) } + + let!(:public_project) { create(:project, :public) } + let!(:private_project) { create(:project, :private) } + + before do + private_project.team << [source_user, Gitlab::Access::MASTER] + private_project.team << [current_user, Gitlab::Access::DEVELOPER] + public_project.team << [source_user, Gitlab::Access::MASTER] + + create(:event, action: Event::PUSHED, project: public_project, + target: public_project, author: source_user) + + create(:event, action: Event::PUSHED, project: private_project, + target: private_project, author: source_user) + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([public_project]) } + end + + describe 'with a current user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([private_project, public_project]) } + end +end diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb new file mode 100644 index 00000000000..6bd4e6a3f3a --- /dev/null +++ b/spec/finders/personal_projects_finder_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe PersonalProjectsFinder do + let(:source_user) { create(:user) } + let(:current_user) { create(:user) } + + let(:finder) { described_class.new(source_user) } + + let!(:public_project) do + create(:project, :public, namespace: source_user.namespace, name: 'A', + path: 'A') + end + + let!(:private_project) do + create(:project, :private, namespace: source_user.namespace, name: 'B', + path: 'B') + end + + before do + private_project.team << [current_user, Gitlab::Access::DEVELOPER] + end + + describe 'without a current user' do + subject { finder.execute } + + it { is_expected.to eq([public_project]) } + end + + describe 'with a current user' do + subject { finder.execute(current_user) } + + it { is_expected.to eq([private_project, public_project]) } + end +end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index de9d4cd6128..d1dede78f74 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -1,51 +1,56 @@ require 'spec_helper' describe ProjectsFinder do - let(:user) { create :user } - let(:group) { create :group } + describe '#execute' do + let(:user) { create(:user) } - let(:project1) { create(:empty_project, :public, group: group) } - let(:project2) { create(:empty_project, :internal, group: group) } - let(:project3) { create(:empty_project, :private, group: group) } - let(:project4) { create(:empty_project, :private, group: group) } + let!(:private_project) { create(:project, :private) } + let!(:internal_project) { create(:project, :internal) } + let!(:public_project) { create(:project, :public) } - context 'non authenticated' do - subject { ProjectsFinder.new.execute(nil, group: group) } + let(:finder) { described_class.new } - it { is_expected.to include(project1) } - it { is_expected.not_to include(project2) } - it { is_expected.not_to include(project3) } - it { is_expected.not_to include(project4) } - end + describe 'without a group' do + describe 'without a user' do + subject { finder.execute } - context 'authenticated' do - subject { ProjectsFinder.new.execute(user, group: group) } + it { is_expected.to eq([public_project]) } + end - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.not_to include(project3) } - it { is_expected.not_to include(project4) } - end + describe 'with a user' do + subject { finder.execute(user) } - context 'authenticated, project member' do - before { project3.team << [user, :developer] } + describe 'without private projects' do + it { is_expected.to eq([public_project, internal_project]) } + end - subject { ProjectsFinder.new.execute(user, group: group) } + describe 'with private projects' do + before do + private_project.team.add_user(user, Gitlab::Access::MASTER) + end - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.to include(project3) } - it { is_expected.not_to include(project4) } - end + it do + is_expected.to eq([public_project, internal_project, + private_project]) + end + end + end + end + + describe 'with a group' do + let(:group) { public_project.group } + + describe 'without a user' do + subject { finder.execute(nil, group: group) } - context 'authenticated, group member' do - before { group.add_developer(user) } + it { is_expected.to eq([public_project]) } + end - subject { ProjectsFinder.new.execute(user, group: group) } + describe 'with a user' do + subject { finder.execute(user, group: group) } - it { is_expected.to include(project1) } - it { is_expected.to include(project2) } - it { is_expected.to include(project3) } - it { is_expected.to include(project4) } + it { is_expected.to eq([public_project, internal_project]) } + end + end end end -- cgit v1.2.1 From 01620dd7e7f3015e31ac0288ef71fcfc4f268a14 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:25:37 +0100 Subject: Added Event.limit_recent This will be used to move some querying logic from the users controller to the Event model (where it belongs). --- app/models/event.rb | 4 ++++ spec/models/event_spec.rb | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/app/models/event.rb b/app/models/event.rb index 7618b503d84..9afd223bce5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -69,6 +69,10 @@ class Event < ActiveRecord::Base row ? row.updated_at : nil end + + def limit_recent(limit = 20, offset = nil) + recent.limit(limit).offset(offset) + end end def proper? diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index f46c50ed6f3..ae53f7a536b 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -85,4 +85,21 @@ describe Event do end end end + + describe '.limit_recent' do + let!(:event1) { create(:closed_issue_event) } + let!(:event2) { create(:closed_issue_event) } + + describe 'without an explicit limit' do + subject { Event.limit_recent } + + it { is_expected.to eq([event2, event1]) } + end + + describe 'with an explicit limit' do + subject { Event.limit_recent(1) } + + it { is_expected.to eq([event2]) } + end + end end -- cgit v1.2.1 From a74d6d204366c862657a545d999cb33dfde300dd Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:27:21 +0100 Subject: Group methods for filtering public/visible groups These methods will be used to get a list of groups, optionally restricted to only those visible to a given user. --- app/models/group.rb | 8 ++++++++ spec/models/group_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/app/models/group.rb b/app/models/group.rb index 2c9e75496b9..1b5b875a19e 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -49,6 +49,14 @@ class Group < Namespace def reference_pattern User.reference_pattern end + + def public_and_given_groups(ids) + where('public IS TRUE OR namespaces.id IN (?)', ids) + end + + def visible_to_user(user) + where(id: user.authorized_groups.select(:id).reorder(nil)) + end end def to_reference(_from_project = nil) diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index bbfc5535eec..6f166b5ab75 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -38,6 +38,33 @@ describe Group do it { is_expected.not_to validate_presence_of :owner } end + describe '.public_and_given_groups' do + let!(:public_group) { create(:group, public: true) } + + subject { described_class.public_and_given_groups([group.id]) } + + it { is_expected.to eq([public_group, group]) } + end + + describe '.visible_to_user' do + let!(:group) { create(:group) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when the user has access to a group' do + before do + group.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([group]) } + end + + describe 'when the user does not have access to any groups' do + it { is_expected.to eq([]) } + end + end + describe '#to_reference' do it 'returns a String reference to the object' do expect(group.to_reference).to eq "@#{group.name}" -- cgit v1.2.1 From a4fc8112df3cf6cb344cfba65f5df46c7a99bef7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:29:45 +0100 Subject: Added Project.visible_to_user This method can be used to filter projects to those visible to a given user. --- app/models/project.rb | 4 ++++ spec/models/project_spec.rb | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/app/models/project.rb b/app/models/project.rb index a099a67cf63..750df0f1ae1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -286,6 +286,10 @@ class Project < ActiveRecord::Base joins(join_body).reorder('join_note_counts.amount DESC') end + + def visible_to_user(user) + where(id: user.authorized_projects.select(:id).reorder(nil)) + end end def team diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8d7e6e76766..c42e8870f8c 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -464,4 +464,23 @@ describe Project do end end end + + describe '.visible_to_user' do + let!(:project) { create(:project, :private) } + let!(:user) { create(:user) } + + subject { described_class.visible_to_user(user) } + + describe 'when a user has access to a project' do + before do + project.team.add_user(user, Gitlab::Access::MASTER) + end + + it { is_expected.to eq([project]) } + end + + describe 'when a user does not have access to any projects' do + it { is_expected.to eq([]) } + end + end end -- cgit v1.2.1 From e116a356b8ac07bd3a935c40ceb274d67d808c83 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:30:24 +0100 Subject: Refactor User#authorized_groups/projects These methods no longer include public groups/projects (that don't belong to the actual user) as this is handled by the various finder classes now. This also removes the need for passing extra arguments. Note that memoizing was removed _explicitly_. For whatever reason doing so messes up the users controller to a point where it claims a certain user does _not_ have access to certain groups/projects when it does have access. Existing code shouldn't be affected as these methods are only called in ways that they'd run queries anyway (e.g. a combination of "any?" and "each" which would run 2 queries regardless of memoizing). --- app/models/user.rb | 35 ++++++----------------------------- spec/models/user_spec.rb | 26 ++++---------------------- 2 files changed, 10 insertions(+), 51 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index d523b3f0491..20a2457eec9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -389,40 +389,17 @@ class User < ActiveRecord::Base end end - # Returns the groups a user has access to, optionally including any public - # groups. - # - # public_internal - When set to "true" all public groups and groups of public - # projects are also included. - # - # Returns an ActiveRecord::Relation - def authorized_groups(public_internal = false) + # Returns the groups a user has access to + def authorized_groups union = Gitlab::SQL::Union. - new([groups.select(:id), authorized_projects(public_internal). - select(:namespace_id)]) - - sql = "namespaces.id IN (#{union.to_sql})" - - if public_internal - sql << ' OR public IS TRUE' - end + new([groups.select(:id), authorized_projects.select(:namespace_id)]) - Group.where(sql) + Group.where("namespaces.id IN (#{union.to_sql})") end # Returns the groups a user is authorized to access. - # - # public_internal - When set to "true" all public/internal projects will also - # be included. - def authorized_projects(public_internal = false) - base = "projects.id IN (#{projects_union.to_sql})" - - if public_internal - Project.where("#{base} OR projects.visibility_level IN (?)", - Project.public_and_internal_levels) - else - Project.where(base) - end + def authorized_projects + Project.where("projects.id IN (#{projects_union.to_sql})") end def owned_projects diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 71160f8dfef..4631b12faf1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -762,44 +762,26 @@ describe User do describe '#authorized_groups' do let!(:user) { create(:user) } let!(:private_group) { create(:group) } - let!(:public_group) { create(:group, public: true) } before do private_group.add_user(user, Gitlab::Access::MASTER) end - describe 'excluding public groups' do - subject { user.authorized_groups } + subject { user.authorized_groups } - it { is_expected.to eq([private_group]) } - end - - describe 'including public groups' do - subject { user.authorized_groups(true) } - - it { is_expected.to eq([public_group, private_group]) } - end + it { is_expected.to eq([private_group]) } end describe '#authorized_projects' do let!(:user) { create(:user) } let!(:private_project) { create(:project, :private) } - let!(:public_project) { create(:project, :public) } before do private_project.team << [user, Gitlab::Access::MASTER] end - describe 'excluding public projects' do - subject { user.authorized_projects } + subject { user.authorized_projects } - it { is_expected.to eq([private_project]) } - end - - describe 'including public projects' do - subject { user.authorized_projects(true) } - - it { is_expected.to eq([public_project, private_project]) } - end + it { is_expected.to eq([private_project]) } end end -- cgit v1.2.1 From fbdf3767495cd60b002f24ab4e9aa4d0c019de95 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 12:32:35 +0100 Subject: Refactor UsersController to not kill the database Previously this controller would in multiple places load tons (read: around 65000) project and/or group IDs into memory. These changes in combination with the previous commits significantly cut down loading times of user profile pages and the Atom feeds of users. --- app/controllers/users_controller.rb | 29 +++++++++++------------------ spec/controllers/users_controller_spec.rb | 23 ++++++++++++++++++----- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1484356a7f4..30cb869eb2a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,14 +3,11 @@ class UsersController < ApplicationController before_action :set_user def show - @contributed_projects = contributed_projects.joined(@user). - reject(&:forked?) + @contributed_projects = contributed_projects.joined(@user).reject(&:forked?) - @projects = @user.personal_projects. - where(id: authorized_projects_ids).includes(:namespace) + @projects = PersonalProjectsFinder.new(@user).execute(current_user) - # Collect only groups common for both users - @groups = @user.groups & GroupsFinder.new.execute(current_user) + @groups = JoinedGroupsFinder.new(@user).execute(current_user) respond_to do |format| format.html @@ -53,16 +50,8 @@ class UsersController < ApplicationController @user = User.find_by_username!(params[:username]) end - def authorized_projects_ids - # Projects user can view - @authorized_projects_ids ||= - ProjectsFinder.new.execute(current_user).pluck(:id) - end - def contributed_projects - @contributed_projects = Project. - where(id: authorized_projects_ids & @user.contributed_projects_ids). - includes(:namespace) + ContributedProjectsFinder.new(@user).execute(current_user) end def contributions_calendar @@ -73,9 +62,13 @@ class UsersController < ApplicationController def load_events # Get user activity feed for projects common for both users @events = @user.recent_events. - where(project_id: authorized_projects_ids). - with_associations + merge(projects_for_current_user). + references(:project). + with_associations. + limit_recent(20, params[:offset]) + end - @events = @events.limit(20).offset(params[:offset] || 0) + def projects_for_current_user + ProjectsFinder.new.execute(current_user) end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 9f89101d7f7..104a5f50143 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -16,13 +16,26 @@ describe UsersController do context 'with rendered views' do render_views - it 'renders the show template' do - sign_in(user) + describe 'when logged in' do + before do + sign_in(user) + end - get :show, username: user.username + it 'renders the show template' do + get :show, username: user.username - expect(response).to be_success - expect(response).to render_template('show') + expect(response).to be_success + expect(response).to render_template('show') + end + end + + describe 'when logged out' do + it 'renders the show template' do + get :show, username: user.username + + expect(response).to be_success + expect(response).to render_template('show') + end end end end -- cgit v1.2.1 From 73f302edf98d6c292c048c913f76282058d6d81c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:02:03 +0100 Subject: Apply CI scope changes to the User model These changes are based on those from commit 03f5ff750b107b30a6d306aafb6699a9c9ecff0d, except they use a UNION instead of plucking IDs into memory. --- app/models/user.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 20a2457eec9..47439ce4b00 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -770,15 +770,10 @@ class User < ActiveRecord::Base !solo_owned_groups.present? end - def ci_authorized_projects - @ci_authorized_projects ||= - Ci::Project.where("gitlab_id IN (#{projects_union.to_sql})") - end - def ci_authorized_runners @ci_authorized_runners ||= begin runner_ids = Ci::RunnerProject.joins(:project). - where("ci_projects.gitlab_id IN (#{projects_union.to_sql})"). + where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})"). select(:runner_id) Ci::Runner.specific.where(id: runner_ids) @@ -792,4 +787,13 @@ class User < ActiveRecord::Base groups_projects.select(:id), projects.select(:id)]) end + + def ci_projects_union + scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] } + groups = groups_projects.where(members: scope) + other = projects.where(members: scope) + + Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id), + other.select(:id)]) + end end -- cgit v1.2.1 From 26482bddb091a085e2368ff20c3e3e797da74ea3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:12:44 +0100 Subject: Don't pluck project IDs in User#owned_projects This won't work efficiently if you happen to have a lot of projects. --- app/models/user.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 47439ce4b00..9d75bb3aeb4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -404,10 +404,8 @@ class User < ActiveRecord::Base def owned_projects @owned_projects ||= - begin - namespace_ids = owned_groups.pluck(:id).push(namespace.id) - Project.in_namespace(namespace_ids).joins(:namespace) - end + Project.where('namespace_id IN (?) OR namespace_id = ?', + owned_groups.select(:id), namespace.id).joins(:namespace) end # Team membership in authorized projects -- cgit v1.2.1 From d9c4625c6d5264756f6ca43f7a8f5eb984d753bc Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:16:07 +0100 Subject: Specs that failed before the fix. --- spec/tasks/gitlab/backup_rake_spec.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 06559c3925d..fb5e74af648 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -149,7 +149,7 @@ describe 'gitlab:app namespace rake task' do # Redirect STDOUT and run the rake task orig_stdout = $stdout $stdout = StringIO.new - ENV["SKIP"] = "repositories" + ENV["SKIP"] = "repositories,uploads" run_rake_task('gitlab:backup:create') $stdout = orig_stdout @@ -180,6 +180,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke + expect(Rake::Task["gitlab:backup:uploads:restore"]).not_to receive :invoke expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke -- cgit v1.2.1 From cc11c44ba997eb32dfa48ea225ae4c6942bc8610 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:27:03 +0100 Subject: Align hash literals to keep Rubocop happy --- spec/finders/contributed_projects_finder_spec.rb | 4 ++-- spec/finders/personal_projects_finder_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb index 8c1e46fe9f2..65d7f14c721 100644 --- a/spec/finders/contributed_projects_finder_spec.rb +++ b/spec/finders/contributed_projects_finder_spec.rb @@ -15,10 +15,10 @@ describe ContributedProjectsFinder do public_project.team << [source_user, Gitlab::Access::MASTER] create(:event, action: Event::PUSHED, project: public_project, - target: public_project, author: source_user) + target: public_project, author: source_user) create(:event, action: Event::PUSHED, project: private_project, - target: private_project, author: source_user) + target: private_project, author: source_user) end describe 'without a current user' do diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb index 6bd4e6a3f3a..38817add456 100644 --- a/spec/finders/personal_projects_finder_spec.rb +++ b/spec/finders/personal_projects_finder_spec.rb @@ -8,12 +8,12 @@ describe PersonalProjectsFinder do let!(:public_project) do create(:project, :public, namespace: source_user.namespace, name: 'A', - path: 'A') + path: 'A') end let!(:private_project) do create(:project, :private, namespace: source_user.namespace, name: 'B', - path: 'B') + path: 'B') end before do -- cgit v1.2.1 From 9eefae69171ba199d34bccf504902500a980fcb3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 13:31:18 +0100 Subject: Fix UNION syntax for MySQL Apparently MySQL doesn't support this syntax: (...) UNION (...) instead it only supports: ... UNION ... --- lib/gitlab/sql/union.rb | 4 ++-- spec/lib/gitlab/sql/union_spec.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb index 1a62eff0b31..1cd89b3a9c4 100644 --- a/lib/gitlab/sql/union.rb +++ b/lib/gitlab/sql/union.rb @@ -23,11 +23,11 @@ module Gitlab # (thus fixing this problem), at a slight performance cost. fragments = ActiveRecord::Base.connection.unprepared_statement do @relations.map do |rel| - "(#{rel.reorder(nil).to_sql})" + rel.reorder(nil).to_sql end end - fragments.join(' UNION ') + fragments.join("\nUNION\n") end end end diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb index 976360af9b5..9e1cd4419e0 100644 --- a/spec/lib/gitlab/sql/union_spec.rb +++ b/spec/lib/gitlab/sql/union_spec.rb @@ -10,7 +10,7 @@ describe Gitlab::SQL::Union do sql1 = rel1.reorder(nil).to_sql sql2 = rel2.reorder(nil).to_sql - expect(union.to_sql).to eq("(#{sql1}) UNION (#{sql2})") + expect(union.to_sql).to eq("#{sql1}\nUNION\n#{sql2}") end end end -- cgit v1.2.1 From f486b06c4de26e7eb6468cfc4f864b50e645d5c7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 15:08:28 +0100 Subject: Return internal projects in PersonalProjectsFinder When getting the projects of a user we should get the public _and_ internal projects, not just the public ones. --- app/finders/personal_projects_finder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 7c039573614..a61ffa22990 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -26,7 +26,7 @@ class PersonalProjectsFinder authorized = @user.personal_projects.visible_to_user(current_user) union = Gitlab::SQL::Union. - new([authorized.select(:id), public_projects.select(:id)]) + new([authorized.select(:id), public_and_internal_projects.select(:id)]) Project.where("projects.id IN (#{union.to_sql})") end @@ -34,4 +34,8 @@ class PersonalProjectsFinder def public_projects @user.personal_projects.public_only end + + def public_and_internal_projects + @user.personal_projects.public_and_internal_only + end end -- cgit v1.2.1 From e6c6c2234b7d82211928cc2549e5c53f60330d05 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 18 Nov 2015 15:15:47 +0100 Subject: Fix huge line height for diff files list Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/commit.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index fbd7c363de1..a0e5f7554ed 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -56,6 +56,7 @@ li { padding: 3px 0px; + line-height: 20px; } } .new-file { -- cgit v1.2.1 From cab6efa53fe8c2d9d44d6762fd71f97bd8d4e3d4 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 18 Nov 2015 09:53:08 -0600 Subject: Deploy page should be shown for all pages not just root --- lib/support/nginx/gitlab | 2 +- lib/support/nginx/gitlab-ssl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 93f2ad07aeb..acc547afcce 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -67,7 +67,7 @@ server { location / { ## Serve static files from defined root folder. ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri $uri/index.html $uri.html @gitlab; + try_files $uri /index.html $uri.html @gitlab; } ## We route uploads through GitLab to prevent XSS and enforce access control. diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 90749947fa4..a8f8d48eb1f 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -112,7 +112,7 @@ server { location / { ## Serve static files from defined root folder. ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri $uri/index.html $uri.html @gitlab; + try_files $uri /index.html $uri.html @gitlab; } ## We route uploads through GitLab to prevent XSS and enforce access control. -- cgit v1.2.1 From f3cfd20952411dc7302c78933346a9a11d8e58af Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 18 Nov 2015 17:10:06 +0100 Subject: DRY up code --- app/controllers/projects/blob_controller.rb | 54 +++++++++++++---------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 41ec7bde45d..31a33bfd237 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -23,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController end def create - result = Files::CreateService.new(@project, current_user, @commit_params).execute - - if result[:status] == :success - flash[:notice] = "The changes have been successfully committed" - respond_to do |format| - format.html { redirect_to after_create_path } - format.json { render json: { message: "success", filePath: after_create_path } } - end - else - flash[:alert] = result[:message] - respond_to do |format| - format.html { render :new } - format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } } - end - end + create_commit(Files::CreateService, success_path: after_create_path, + failure_view: :new, + failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref)) end def show @@ -48,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController end def update - result = Files::UpdateService.new(@project, current_user, @commit_params).execute - - if result[:status] == :success - flash[:notice] = "Your changes have been successfully committed" - respond_to do |format| - format.html { redirect_to after_edit_path } - format.json { render json: { message: "success", filePath: after_edit_path } } - end - else - flash[:alert] = result[:message] - respond_to do |format| - format.html { render :edit } - format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } } - end - end + create_commit(Files::UpdateService, success_path: after_edit_path, + failure_view: :edit, + failure_path: namespace_project_blob_path(@project.namespace, @project, @id)) end def preview @@ -132,6 +108,24 @@ class Projects::BlobController < Projects::ApplicationController render_404 end + def create_commit(service, success_path:, failure_view:, failure_path:) + result = service.new(@project, current_user, @commit_params).execute + + if result[:status] == :success + flash[:notice] = "Your changes have been successfully committed" + respond_to do |format| + format.html { redirect_to success_path } + format.json { render json: { message: "success", filePath: success_path } } + end + else + flash[:alert] = result[:message] + respond_to do |format| + format.html { render failure_view } + format.json { render json: { message: "failed", filePath: failure_path } } + end + end + end + def after_create_path @after_create_path ||= if create_merge_request? -- cgit v1.2.1 From ee2739e60682153895ffc4ec274d22acb509c3b3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 17:24:51 +0100 Subject: Added an index on namespaces.public --- db/migrate/20151118162244_add_projects_public_index.rb | 5 +++++ db/schema.rb | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151118162244_add_projects_public_index.rb diff --git a/db/migrate/20151118162244_add_projects_public_index.rb b/db/migrate/20151118162244_add_projects_public_index.rb new file mode 100644 index 00000000000..fded70e3c0c --- /dev/null +++ b/db/migrate/20151118162244_add_projects_public_index.rb @@ -0,0 +1,5 @@ +class AddProjectsPublicIndex < ActiveRecord::Migration + def change + add_index :namespaces, :public + end +end diff --git a/db/schema.rb b/db/schema.rb index aa76cef9fe4..7f95b4cbdbc 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: 20151116144118) do +ActiveRecord::Schema.define(version: 20151118162244) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -538,6 +538,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree + add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: true do |t| -- cgit v1.2.1 From bf3c0aafaa17c2e322d225180fd9708ec2827ac4 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 18:14:13 +0100 Subject: USe reject. --- lib/backup/manager.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index 69922eb66ea..e7eda7c6f45 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -158,7 +158,7 @@ module Backup end def folders_to_backup - %w{repositories db}.map{ |name| name unless skipped?(name) }.compact + %w{repositories db}.reject{ |name| skipped?(name) } end def settings -- cgit v1.2.1 From efd5d93745cede13c3a720fe17388b146a8998ed Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 18 Nov 2015 20:20:55 +0100 Subject: Use "GitLab.com" instead of "gitlab.com" --- app/models/user.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/user.rb b/app/models/user.rb index 9d75bb3aeb4..9374f01f99f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -729,7 +729,7 @@ class User < ActiveRecord::Base # some_user.contributed_projects.visible_to_user(other_user) # # If this method were to use a JOIN the resulting query would take roughly 200 - # ms on a database with a similar size to gitlab.com's database. On the other + # ms on a database with a similar size to GitLab.com's database. On the other # hand, using a subquery means we can get the exact same data in about 40 ms. def contributed_projects events = Event.select(:project_id). -- cgit v1.2.1 From 6f41e3d9caa28b2a654fb775202be632673f2299 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 18 Nov 2015 22:11:15 +0100 Subject: Change forks method to has_many relation --- app/models/project.rb | 11 +++++------ app/services/projects/update_service.rb | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index f287e59b6df..2562c441abe 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -97,9 +97,12 @@ class Project < ActiveRecord::Base has_one :gitlab_issue_tracker_service, dependent: :destroy has_one :external_wiki_service, dependent: :destroy - has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" + has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" + has_one :forked_from_project, through: :forked_project_link + + has_many :forked_project_links, foreign_key: "forked_from_project_id" + has_many :forks, through: :forked_project_links, source: :forked_to_project - has_one :forked_from_project, through: :forked_project_link # Merge Requests for target project should be removed with it has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' # Merge requests from source project should be kept when source project was removed @@ -734,10 +737,6 @@ class Project < ActiveRecord::Base forks.count end - def forks - ForkedProjectLink.where(forked_from_project_id: self.id) - end - def find_label(name) labels.find_by(name: name) end diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 0d2665f298d..67e3429c0bd 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -44,8 +44,7 @@ module Projects end def update_forks_visibility_level(new_level) - project.forks.each do |forked_link| - forked_project = forked_link.forked_to_project + project.forks.each do |forked_project| fork_level = forked_project.visibility_level if fork_level > new_level.to_i -- cgit v1.2.1 From fd2c0fe446c7f761b845c91307ef8110d869e8e8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 11 Nov 2015 15:12:51 +0200 Subject: award emoji --- app/assets/javascripts/awards_handler.coffee | 55 ++++++ app/assets/javascripts/notes.js.coffee | 9 +- app/assets/stylesheets/pages/issuable.scss | 42 +++++ app/controllers/projects/issues_controller.rb | 2 +- .../projects/merge_requests_controller.rb | 2 +- app/controllers/projects/notes_controller.rb | 25 ++- app/helpers/issues_helper.rb | 5 + app/models/concerns/issuable.rb | 49 ------ app/models/note.rb | 46 +---- app/services/notes/create_service.rb | 17 +- app/services/notification_service.rb | 1 + app/views/projects/issues/_issue.html.haml | 2 - .../merge_requests/_merge_request.html.haml | 2 - app/views/projects/notes/_note.html.haml | 20 --- app/views/votes/_votes_block.html.haml | 43 +++-- app/views/votes/_votes_inline.html.haml | 9 - config/routes.rb | 4 + db/migrate/20151106000015_add_is_award_to_notes.rb | 5 + db/schema.rb | 3 +- doc/api/merge_requests.md | 12 -- doc/api/notes.md | 8 +- lib/api/entities.rb | 4 +- spec/lib/votes_spec.rb | 188 --------------------- spec/models/project_spec.rb | 11 -- 24 files changed, 203 insertions(+), 361 deletions(-) create mode 100644 app/assets/javascripts/awards_handler.coffee delete mode 100644 app/views/votes/_votes_inline.html.haml create mode 100644 db/migrate/20151106000015_add_is_award_to_notes.rb delete mode 100644 spec/lib/votes_spec.rb diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee new file mode 100644 index 00000000000..aab3179f10e --- /dev/null +++ b/app/assets/javascripts/awards_handler.coffee @@ -0,0 +1,55 @@ +class @AwardsHandler + constructor: (@post_emoji_url, @noteable_type, @noteable_id) -> + + addAward: (emoji) -> + @postEmoji emoji, => + if @exist(emoji) + if @isActive(emoji) + @decrementCounter(emoji) + else + counter = $(".icon." + emoji).siblings(".counter") + counter.text(parseInt(counter.text()) + 1) + counter.parent().addClass("active") + else + @createEmoji(emoji) + + + exist: (emoji) -> + $(".icon").hasClass(emoji) + + isActive: (emoji) -> + $(".icon." + emoji).parent().hasClass("active") + + decrementCounter: (emoji) -> + counter = $(".icon." + emoji).siblings(".counter") + + if parseInt(counter.text()) > 1 + counter.text(parseInt(counter.text()) - 1) + counter.parent().removeClass("active") + else + counter.parent().remove() + + + createEmoji: (emoji) -> + nodes = [] + nodes.push("
") + nodes.push("
") + nodes.push(@getImage(emoji)) + nodes.push("
") + nodes.push("
1") + nodes.push("
") + + $(".awards").append(nodes.join("\n")) + + getImage: (emoji) -> + $("li." + emoji).html() + + postEmoji: (emoji, callback) -> + emoji = emoji.replace("emoji-", "") + $.post @post_emoji_url, { + emoji: emoji + noteable_type: @noteable_type + noteable_id: @noteable_id + },(data) -> + if data.ok + callback.call() \ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ea75c656bcc..cd27b20dd7c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -113,13 +113,16 @@ class @Notes renderNote: (note) -> # render note if it not present in loaded list # or skip if rendered - if @isNewNote(note) + if @isNewNote(note) && !note.award @note_ids.push(note.id) $('ul.main-notes-list'). append(note.html). syntaxHighlight() @initTaskList() + if note.award + awards_handler.addAward("emoji-" + note.note) + ### Check if note does not exists on page ### @@ -255,7 +258,6 @@ class @Notes ### addNote: (xhr, note, status) => @renderNote(note) - @updateVotes() ### Called in response to the new note form being submitted @@ -473,9 +475,6 @@ class @Notes form = $(e.target).closest(".js-discussion-note-form") @removeDiscussionNoteForm(form) - updateVotes: -> - true - ### Called after an attachment file has been selected. diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index abc27a19e32..efeb2393165 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -101,3 +101,45 @@ background-color: $background-color; } } + +.awards { + .award { + border: 1px solid; + padding: 1px 3px; + width: 50px; + border-radius: 5px; + float:left; + margin: 0 3px; + border-color: #ccc; + cursor: pointer; + + &.active { + border-color: rgba(79,176,252,.4); + background-color: rgba(79,176,252,.08); + + .counter { + font-weight: bold; + } + } + + .icon { + float: left; + margin-right: 10px; + } + } + + #add-award { + font-size: 20px; + border-radius: 5px; + float: left; + width: 50px; + font-weight: bold; + } + + .awards-menu{ + li { + float: left; + margin: 3px; + } + } +} diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index e74c2905e48..5250a0f5e67 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController def show @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) - @notes = @issue.notes.with_associations.fresh + @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue respond_with(@issue) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 188f0cc4cea..a0468c65d5a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.mr_and_commit_notes.inc_author.fresh + @notes = @merge_request.nonawards.mr_and_commit_notes.inc_author.fresh @discussions = Note.discussions_from_notes(@notes) @noteable = @merge_request diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 41cd08c93c6..357b292980d 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :delete_attachment] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]] def index current_fetched_at = Time.now.to_i @@ -58,6 +58,27 @@ class Projects::NotesController < Projects::ApplicationController end end + def award_toggle + noteable = params[:noteable_type] == "Issue" ? Issue : MergeRequest + noteable = noteable.find(params[:noteable_id]) + data = { + noteable: noteable, + author: current_user, + is_award: true, + note: params[:emoji] + } + + note = project.notes.find_by(data) + + if note + note.destroy + else + project.notes.create(data) + end + + render json: {ok: true} + end + private def note @@ -111,6 +132,8 @@ class Projects::NotesController < Projects::ApplicationController id: note.id, discussion_id: note.discussion_id, html: note_to_html(note), + award: note.is_award, + note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index beb083d82dc..ff3e0911954 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -87,6 +87,11 @@ module IssuesHelper merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') end + def url_to_emoji(name) + emoji_path = "emoji/#{Emoji.emoji_filename(name)}.png" + url_to_image(emoji_path) + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 492a026add9..91da6797df7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -89,41 +89,6 @@ module Issuable opened? || reopened? end - # - # Votes - # - - # Return the number of -1 comments (downvotes) - def downvotes - filter_superceded_votes(notes.select(&:downvote?), notes).size - end - - def downvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 - upvotes_in_percent - end - end - - # Return the number of +1 comments (upvotes) - def upvotes - filter_superceded_votes(notes.select(&:upvote?), notes).size - end - - def upvotes_in_percent - if votes_count.zero? - 0 - else - 100.0 / votes_count * upvotes - end - end - - # Return the total number of votes - def votes_count - upvotes + downvotes - end - def subscribed?(user) subscription = subscriptions.find_by_user_id(user.id) @@ -183,18 +148,4 @@ module Issuable def notes_with_associations notes.includes(:author, :project) end - - private - - def filter_superceded_votes(votes, notes) - filteredvotes = [] + votes - - votes.each do |vote| - if vote.superceded?(notes) - filteredvotes.delete(vote) - end - end - - filteredvotes - end end diff --git a/app/models/note.rb b/app/models/note.rb index 0b3aa30abd7..458d433211c 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -50,6 +50,8 @@ class Note < ActiveRecord::Base mount_uploader :attachment, AttachmentUploader # Scopes + scope :awards, ->{ where("is_award IS TRUE") } + scope :nonawards, ->{ where("is_award IS FALSE") } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :inline, ->{ where("line_code IS NOT NULL") } scope :not_inline, ->{ where(line_code: [nil, '']) } @@ -97,6 +99,12 @@ class Note < ActiveRecord::Base def search(query) where("LOWER(note) like :query", query: "%#{query.downcase}%") end + + def grouped_awards + select(:note).distinct.map do |note| + [ note.note, where(note: note.note) ] + end + end end def cross_reference? @@ -288,44 +296,6 @@ class Note < ActiveRecord::Base nil end - DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:) - - # Check if the note is a downvote - def downvote? - votable? && note.start_with?(*DOWNVOTES) - end - - UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:) - - # Check if the note is an upvote - def upvote? - votable? && note.start_with?(*UPVOTES) - end - - def superceded?(notes) - return false unless vote? - - notes.each do |note| - next if note == self - - if note.vote? && - self[:author_id] == note[:author_id] && - self[:created_at] <= note[:created_at] - return true - end - end - - false - end - - def vote? - upvote? || downvote? - end - - def votable? - for_issue? || (for_merge_request? && !for_diff_line?) - end - # Mentionable override. def gfm_reference(from_project = nil) noteable.gfm_reference(from_project) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 2001dc89c33..f448f61cc86 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,11 +5,16 @@ module Notes note.author = current_user note.system = false + if contains_emoji_only?(params[:note]) + note.is_award = true + note.note = emoji_name(params[:note]) + end + if note.save notification_service.new_note(note) - # Skip system notes, like status changes and cross-references. - unless note.system + # Skip system notes, like status changes and cross-references and awards + unless note.system || note.is_award event_service.leave_note(note, note.author) note.create_cross_references! execute_hooks(note) @@ -28,5 +33,13 @@ module Notes note.project.execute_hooks(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks) end + + def contains_emoji_only?(note) + note =~ /^:[-_+[:alnum:]]*:\s?/ + end + + def emoji_name(note) + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index bbfe755f44a..d6550fbb555 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -102,6 +102,7 @@ class NotificationService # ignore gitlab service messages return true if note.note.start_with?('Status changed to closed') return true if note.cross_reference? && note.system == true + return true if note.is_award target = note.noteable diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 55ce912829d..d7657ee7e40 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -29,8 +29,6 @@ .issue-info = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe - - if issue.votes_count > 0 - = render 'votes/votes_inline', votable: issue - if issue.milestone   %span diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index c5234c0618c..83e8ad11989 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -34,8 +34,6 @@ .merge-request-info = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe - - if merge_request.votes_count > 0 - = render 'votes/votes_inline', votable: merge_request - if merge_request.milestone_id?   %span diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index efa7dd01cc2..dd0abc8c746 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -35,26 +35,6 @@ - if note.updated_by && note.updated_by != note.author by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)} - - if note.superceded?(@notes) - - if note.upvote? - %span.vote.upvote.label.label-gray.strikethrough - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-gray.strikethrough - = icon('thumbs-down') - \-1 - - else - - if note.upvote? - %span.vote.upvote.label.label-success - = icon('thumbs-up') - \+1 - - if note.downvote? - %span.vote.downvote.label.label-danger - = icon('thumbs-down') - \-1 - - .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-text = preserve do diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 36ea6742064..3c3ae9dd0b2 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,10 +1,33 @@ -.votes.votes-block - .btn-group - - unless votable.upvotes.zero? - .btn.btn-sm.disabled.cgreen - %i.fa.fa-thumbs-up - = votable.upvotes - - unless votable.downvotes.zero? - .btn.btn-sm.disabled.cred - %i.fa.fa-thumbs-down - = votable.downvotes +.awards.votes-block + - votable.notes.awards.grouped_awards.each do | vote | + .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} + .icon{class: "emoji-#{vote.first}"} + = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" + .counter + = vote.last.count + + %button.dropdown + %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + + %ul.dropdown-menu.awards-menu + - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| + %li{class: "emoji-#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + + +:coffeescript + post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" + noteable_type = "Issue" + noteable_id = #{@issue.id} + awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + + $ -> + $(".awards-menu li").click (e)-> + emoji = $(this).attr("class") + awards_handler.addAward(emoji) + + $(".awards").on "click", ".award", (e)-> + emoji = /(emoji-\S*)/.exec($(this).find(".icon").attr("class"))[0] + awards_handler.addAward(emoji) + + + + \ No newline at end of file diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml deleted file mode 100644 index 2cb3ae04e1a..00000000000 --- a/app/views/votes/_votes_inline.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.votes.votes-inline - - unless votable.upvotes.zero? - %span.upvotes.cgreen - + #{votable.upvotes} - - unless votable.downvotes.zero? - \/ - - unless votable.downvotes.zero? - %span.downvotes.cred - \- #{votable.downvotes} diff --git a/config/routes.rb b/config/routes.rb index 0bc2c173453..ac81a2aac76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -664,6 +664,10 @@ Gitlab::Application.routes.draw do member do delete :delete_attachment end + + collection do + post :award_toggle + end end resources :uploads, only: [:create] do diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb new file mode 100644 index 00000000000..bffe85df3da --- /dev/null +++ b/db/migrate/20151106000015_add_is_award_to_notes.rb @@ -0,0 +1,5 @@ +class AddIsAwardToNotes < ActiveRecord::Migration + def change + add_column :notes, :is_award, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 440a33e2006..6c322d33682 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -531,7 +531,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.string "type" t.string "description", default: "", null: false t.string "avatar" - t.boolean "public", default: false + t.boolean "visible", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -554,6 +554,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.boolean "system", default: false, null: false t.text "st_diff" t.integer "updated_by_id" + t.boolean "is_award", default: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index ffa7f2cdf14..2f17d4ae06b 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -31,8 +31,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -77,8 +75,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -126,8 +122,6 @@ Parameters: "updated_at": "2015-02-02T20:08:49.959Z", "target_branch": "secret_token", "source_branch": "version-1-9", - "upvotes": 0, - "downvotes": 0, "author": { "name": "Chad Hamill", "username": "jarrett", @@ -198,8 +192,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -250,8 +242,6 @@ Parameters: "title": "test1", "description": "description1", "state": "opened", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -304,8 +294,6 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", - "upvotes": 0, - "downvotes": 0, "author": { "id": 1, "username": "admin", diff --git a/doc/api/notes.md b/doc/api/notes.md index c683cb883d4..bcba1f62151 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -32,9 +32,7 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:22:45Z", - "system": true, - "upvote": false, - "downvote": false + "system": true }, { "id": 305, @@ -49,9 +47,7 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:56:03Z", - "system": false, - "upvote": false, - "downvote": false + "system": false } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d6aec03d7f5..3da6bc415d6 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -162,7 +162,7 @@ module API end class MergeRequest < ProjectEntity - expose :target_branch, :source_branch, :upvotes, :downvotes + expose :target_branch, :source_branch expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id expose :label_names, as: :labels @@ -192,8 +192,6 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system - expose :upvote?, as: :upvote - expose :downvote?, as: :downvote end class MRNote < Grape::Entity diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb deleted file mode 100644 index 39e5d054e62..00000000000 --- a/spec/lib/votes_spec.rb +++ /dev/null @@ -1,188 +0,0 @@ -require 'spec_helper' - -describe Issue, 'Votes' do - let(:issue) { create(:issue) } - - describe "#upvotes" do - it "with no notes has a 0/0 score" do - expect(issue.upvotes).to eq(0) - end - - it "should recognize non-+1 notes" do - add_note "No +1 here" - expect(issue.notes.size).to eq(1) - expect(issue.notes.first.upvote?).to be_falsey - expect(issue.upvotes).to eq(0) - end - - it "should recognize a single +1 note" do - add_note "+1 This is awesome" - expect(issue.upvotes).to eq(1) - end - - it 'should recognize multiple +1 notes' do - add_note '+1 This is awesome', create(:user) - add_note '+1 I want this', create(:user) - expect(issue.upvotes).to eq(2) - end - - it 'should not count 2 +1 votes from the same user' do - add_note '+1 This is awesome' - add_note '+1 I want this' - expect(issue.upvotes).to eq(1) - end - end - - describe "#downvotes" do - it "with no notes has a 0/0 score" do - expect(issue.downvotes).to eq(0) - end - - it "should recognize non--1 notes" do - add_note "Almost got a -1" - expect(issue.notes.size).to eq(1) - expect(issue.notes.first.downvote?).to be_falsey - expect(issue.downvotes).to eq(0) - end - - it "should recognize a single -1 note" do - add_note "-1 This is bad" - expect(issue.downvotes).to eq(1) - end - - it "should recognize multiple -1 notes" do - add_note('-1 This is bad', create(:user)) - add_note('-1 Away with this', create(:user)) - expect(issue.downvotes).to eq(2) - end - end - - describe "#votes_count" do - it "with no notes has a 0/0 score" do - expect(issue.votes_count).to eq(0) - end - - it "should recognize non notes" do - add_note "No +1 here" - expect(issue.notes.size).to eq(1) - expect(issue.votes_count).to eq(0) - end - - it "should recognize a single +1 note" do - add_note "+1 This is awesome" - expect(issue.votes_count).to eq(1) - end - - it "should recognize a single -1 note" do - add_note "-1 This is bad" - expect(issue.votes_count).to eq(1) - end - - it "should recognize multiple notes" do - add_note('+1 This is awesome', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 I want this', create(:user)) - expect(issue.votes_count).to eq(3) - end - - it 'should not count 2 -1 votes from the same user' do - add_note '-1 This is suspicious' - add_note '-1 This is bad' - expect(issue.votes_count).to eq(1) - end - end - - describe "#upvotes_in_percent" do - it "with no notes has a 0% score" do - expect(issue.upvotes_in_percent).to eq(0) - end - - it "should count a single 1 note as 100%" do - add_note "+1 This is awesome" - expect(issue.upvotes_in_percent).to eq(100) - end - - it 'should count multiple +1 notes as 100%' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - expect(issue.upvotes_in_percent).to eq(100) - end - - it 'should count fractions for multiple +1 and -1 notes correctly' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.upvotes_in_percent).to eq(75) - end - end - - describe "#downvotes_in_percent" do - it "with no notes has a 0% score" do - expect(issue.downvotes_in_percent).to eq(0) - end - - it "should count a single -1 note as 100%" do - add_note "-1 This is bad" - expect(issue.downvotes_in_percent).to eq(100) - end - - it 'should count multiple -1 notes as 100%' do - add_note('-1 This is bad', create(:user)) - add_note('-1 Away with this', create(:user)) - expect(issue.downvotes_in_percent).to eq(100) - end - - it 'should count fractions for multiple +1 and -1 notes correctly' do - add_note('+1 This is awesome', create(:user)) - add_note('+1 I want this', create(:user)) - add_note('-1 This is bad', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.downvotes_in_percent).to eq(25) - end - end - - describe '#filter_superceded_votes' do - - it 'should count a users vote only once amongst multiple votes' do - add_note('-1 This needs work before I will accept it') - add_note('+1 I want this', create(:user)) - add_note('+1 This is is awesome', create(:user)) - add_note('+1 this looks good now') - add_note('+1 This is awesome', create(:user)) - add_note('+1 me too', create(:user)) - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(5) - end - - it 'should count each users vote only once' do - add_note '-1 This needs work before it will be accepted' - add_note '+1 I like this' - add_note '+1 I still like this' - add_note '+1 I really like this' - add_note '+1 Give me this now!!!!' - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(1) - end - - it 'should count a users vote only once without caring about comments' do - add_note '-1 This needs work before it will be accepted' - add_note 'Comment 1' - add_note 'Another comment' - add_note '+1 vote' - add_note 'final comment' - expect(issue.downvotes).to eq(0) - expect(issue.upvotes).to eq(1) - end - - end - - def add_note(text, author = issue.author) - created_at = Time.now - 1.hour + Note.count.seconds - issue.notes << create(:note, - note: text, - project: issue.project, - author_id: author.id, - created_at: created_at) - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 8d7e6e76766..3c537220106 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -345,17 +345,6 @@ describe Project do expect(project1.star_count).to eq(0) expect(project2.star_count).to eq(0) end - - it 'is decremented when an upvoter account is deleted' do - user = create :user - project = create :project, :public - user.toggle_star(project) - project.reload - expect(project.star_count).to eq(1) - user.destroy - project.reload - expect(project.star_count).to eq(0) - end end describe :avatar_type do -- cgit v1.2.1 From 06a4fd1035c58d89251fb979dafa8610ba8c5157 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 17 Nov 2015 13:16:16 +0200 Subject: css improvements --- app/assets/javascripts/awards_handler.coffee | 2 +- app/assets/stylesheets/pages/issuable.scss | 20 +++++++++++++++----- app/finders/notes_finder.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 2 +- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index aab3179f10e..1ede7c317c8 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -39,7 +39,7 @@ class @AwardsHandler nodes.push("
1") nodes.push("
") - $(".awards").append(nodes.join("\n")) + $(".awards-controls").before(nodes.join("\n")) getImage: (emoji) -> $("li." + emoji).html() diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index efeb2393165..3f79d0d4967 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -128,12 +128,22 @@ } } - #add-award { - font-size: 20px; - border-radius: 5px; + .awards-controls { + height: 25px; + width: 28px; float: left; - width: 50px; - font-weight: bold; + padding: 0 0 5px 5px; + line-height: 1; + + #add-award { + font-size: 27px; + &:hover { + text-decoration: none; + } + &:link { + text-decoration: none; + } + } } .awards-menu{ diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index ab252821b52..fa4c635f55c 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -12,9 +12,9 @@ class NotesFinder when "commit" project.notes.for_commit_id(target_id).not_inline when "issue" - project.issues.find(target_id).notes.inc_author + project.issues.find(target_id).notes.nonawards.inc_author when "merge_request" - project.merge_requests.find(target_id).mr_and_commit_notes.inc_author + project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author when "snippet", "project_snippet" project.snippets.find(target_id).notes else diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 3c3ae9dd0b2..7f988160ad9 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -6,7 +6,7 @@ .counter = vote.last.count - %button.dropdown + .dropdown.awards-controls %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| -- cgit v1.2.1 From 36d0442e837cd520dec780590040c83108bc14e6 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 17 Nov 2015 16:44:58 +0200 Subject: replace emoji references from class name to data [ci skip] --- app/assets/javascripts/awards_handler.coffee | 36 +++++++++++++++------------- app/assets/javascripts/notes.js.coffee | 2 +- app/views/votes/_votes_block.html.haml | 10 ++++---- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 1ede7c317c8..8803c0cca2d 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -3,25 +3,27 @@ class @AwardsHandler addAward: (emoji) -> @postEmoji emoji, => - if @exist(emoji) - if @isActive(emoji) - @decrementCounter(emoji) - else - counter = $(".icon." + emoji).siblings(".counter") - counter.text(parseInt(counter.text()) + 1) - counter.parent().addClass("active") - else - @createEmoji(emoji) + @addAwardToEmojiBar(emoji) + addAwardToEmojiBar: (emoji) -> + if @exist(emoji) + if @isActive(emoji) + @decrementCounter(emoji) + else + counter = @findEmojiIcon(emoji).siblings(".counter") + counter.text(parseInt(counter.text()) + 1) + counter.parent().addClass("active") + else + @createEmoji(emoji) exist: (emoji) -> - $(".icon").hasClass(emoji) + @findEmojiIcon(emoji).length > 0 isActive: (emoji) -> - $(".icon." + emoji).parent().hasClass("active") + @findEmojiIcon(emoji).parent().hasClass("active") decrementCounter: (emoji) -> - counter = $(".icon." + emoji).siblings(".counter") + counter = @findEmojiIcon(emoji).siblings(".counter") if parseInt(counter.text()) > 1 counter.text(parseInt(counter.text()) - 1) @@ -33,7 +35,7 @@ class @AwardsHandler createEmoji: (emoji) -> nodes = [] nodes.push("
") - nodes.push("
") + nodes.push("
") nodes.push(@getImage(emoji)) nodes.push("
") nodes.push("
1") @@ -42,14 +44,16 @@ class @AwardsHandler $(".awards-controls").before(nodes.join("\n")) getImage: (emoji) -> - $("li." + emoji).html() + $("li[data-emoji='" + emoji + "'").html() postEmoji: (emoji, callback) -> - emoji = emoji.replace("emoji-", "") $.post @post_emoji_url, { emoji: emoji noteable_type: @noteable_type noteable_id: @noteable_id },(data) -> if data.ok - callback.call() \ No newline at end of file + callback.call() + + findEmojiIcon: (emoji) -> + $(".icon[data-emoji='" + emoji + "'") \ No newline at end of file diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index cd27b20dd7c..73a95c455e8 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -121,7 +121,7 @@ class @Notes @initTaskList() if note.award - awards_handler.addAward("emoji-" + note.note) + awards_handler.addAwardToEmojiBar(note.note) ### Check if note does not exists on page diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 7f988160ad9..118a095181f 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,7 +1,7 @@ .awards.votes-block - votable.notes.awards.grouped_awards.each do | vote | .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} - .icon{class: "emoji-#{vote.first}"} + .icon{"data-emoji" => "#{vote.first}"} = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" .counter = vote.last.count @@ -10,22 +10,22 @@ %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| - %li{class: "emoji-#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "Issue" noteable_id = #{@issue.id} - awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) $ -> $(".awards-menu li").click (e)-> - emoji = $(this).attr("class") + emoji = $(this).data("emoji") awards_handler.addAward(emoji) $(".awards").on "click", ".award", (e)-> - emoji = /(emoji-\S*)/.exec($(this).find(".icon").attr("class"))[0] + emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) -- cgit v1.2.1 From f021bc5c6aa79147940ee31e800f519962f4d806 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 15:43:53 +0200 Subject: add stats on hover --- app/assets/javascripts/awards_handler.coffee | 32 ++++++++++++++++++++++++++-- app/helpers/issues_helper.rb | 8 +++++++ app/views/votes/_votes_block.html.haml | 4 +++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 8803c0cca2d..29b11b0cc58 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -13,6 +13,7 @@ class @AwardsHandler counter = @findEmojiIcon(emoji).siblings(".counter") counter.text(parseInt(counter.text()) + 1) counter.parent().addClass("active") + @addMeToAuthorList(emoji) else @createEmoji(emoji) @@ -28,13 +29,38 @@ class @AwardsHandler if parseInt(counter.text()) > 1 counter.text(parseInt(counter.text()) - 1) counter.parent().removeClass("active") + @removeMeFromAuthorList(emoji) else - counter.parent().remove() + award = counter.parent() + award.tooltip("destroy") + award.remove() + removeMeFromAuthorList: (emoji) -> + award_block = @findEmojiIcon(emoji).parent() + authors = award_block.attr("data-original-title").split(", ") + authors = _.without(authors, "me").join(", ") + award_block.attr("title", authors) + @resetTooltip(award_block) + + addMeToAuthorList: (emoji) -> + award_block = @findEmojiIcon(emoji).parent() + authors = award_block.attr("data-original-title").split(", ") + authors.push("me") + award_block.attr("title", authors.join(", ")) + @resetTooltip(award_block) + + resetTooltip: (award) -> + award.tooltip("destroy") + + # "destroy" call is asynchronous, this is why we need to set timeout. + setTimeout (-> + award.tooltip() + ), 200 + createEmoji: (emoji) -> nodes = [] - nodes.push("
") + nodes.push("
") nodes.push("
") nodes.push(@getImage(emoji)) nodes.push("
") @@ -43,6 +69,8 @@ class @AwardsHandler $(".awards-controls").before(nodes.join("\n")) + $(".award").tooltip() + getImage: (emoji) -> $("li[data-emoji='" + emoji + "'").html() diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index ff3e0911954..3aa16b66944 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -92,6 +92,14 @@ module IssuesHelper url_to_image(emoji_path) end + def emoji_author_list(notes, current_user) + list = notes.map do |note| + note.author == current_user ? "me" : note.author.username + end + + list.join(", ") + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 118a095181f..a2298f1813a 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,6 +1,6 @@ .awards.votes-block - votable.notes.awards.grouped_awards.each do | vote | - .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id))} + .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id)), title: emoji_author_list(vote.last, current_user)} .icon{"data-emoji" => "#{vote.first}"} = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" .counter @@ -28,6 +28,8 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) + $(".award").tooltip() + \ No newline at end of file -- cgit v1.2.1 From 2f6f99d300675b0794b2e96be564db9d405fac36 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 16:48:37 +0200 Subject: award for merge requests[ci skip] --- app/assets/stylesheets/pages/issuable.scss | 4 ++-- app/controllers/projects/merge_requests_controller.rb | 2 +- app/views/votes/_votes_block.html.haml | 10 +++------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3f79d0d4967..5b73e20df7d 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -114,8 +114,8 @@ cursor: pointer; &.active { - border-color: rgba(79,176,252,.4); - background-color: rgba(79,176,252,.08); + border-color: rgba(79,176,252,0.4); + background-color: rgba(79,176,252,0.1); .counter { font-weight: bold; diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index a0468c65d5a..6378a1f56b0 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @notes = @merge_request.nonawards.mr_and_commit_notes.inc_author.fresh + @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh @discussions = Note.discussions_from_notes(@notes) @noteable = @merge_request diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index a2298f1813a..2ae832c31f7 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -15,8 +15,8 @@ :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "Issue" - noteable_id = #{@issue.id} + noteable_type = #{votable} + noteable_id = #{votable.id} window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) $ -> @@ -28,8 +28,4 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) - $(".award").tooltip() - - - - \ No newline at end of file + $(".award").tooltip() \ No newline at end of file -- cgit v1.2.1 From 2d1fcd802a291cc8e26fbbe5874e20316a5f93af Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 17:38:15 +0200 Subject: Emoji: refactoring --- app/helpers/issues_helper.rb | 4 ++++ app/views/votes/_votes_block.html.haml | 2 +- lib/award_emoji.rb | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 lib/award_emoji.rb diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 3aa16b66944..bca32322096 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -100,6 +100,10 @@ module IssuesHelper list.join(", ") end + def emoji_list + ::AwardEmoji::EMOJI_LIST + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 2ae832c31f7..02947f2979e 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -9,7 +9,7 @@ .dropdown.awards-controls %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + %ul.dropdown-menu.awards-menu - - ["100", "blush", "heart", "smile", "rage", "beers", "thumbsup", "disappointed", "ok_hand", "helicopter"].each do |emoji| + - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb new file mode 100644 index 00000000000..95b9c8f921a --- /dev/null +++ b/lib/award_emoji.rb @@ -0,0 +1,6 @@ +class AwardEmoji + EMOJI_LIST = ["+1", "-1", "100", "blush", "heart", "smile", "rage", + "beers", "disappointed", "ok_hand", + "helicopter", "shit", "airplane", "alarm_clock", + "ambulance", "anguished", "two_hearts", "wink"] +end \ No newline at end of file -- cgit v1.2.1 From d8676f18fcbd6b78af472b8b00c29f20820a74a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 21:44:48 +0200 Subject: fix --- app/views/votes/_votes_block.html.haml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 02947f2979e..afd1c2745a1 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -15,17 +15,16 @@ :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = #{votable} - noteable_id = #{votable.id} + noteable_type = "#{votable.class}" + noteable_id = "#{votable.id}" window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) - $ -> - $(".awards-menu li").click (e)-> - emoji = $(this).data("emoji") - awards_handler.addAward(emoji) + $(".awards-menu li").click (e)-> + emoji = $(this).data("emoji") + awards_handler.addAward(emoji) - $(".awards").on "click", ".award", (e)-> - emoji = $(this).find(".icon").data("emoji") - awards_handler.addAward(emoji) + $(".awards").on "click", ".award", (e)-> + emoji = $(this).find(".icon").data("emoji") + awards_handler.addAward(emoji) - $(".award").tooltip() \ No newline at end of file + $(".award").tooltip() \ No newline at end of file -- cgit v1.2.1 From 23df515fd09661a690c0c0a651e131bc3a6d0191 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 18 Nov 2015 23:59:58 +0200 Subject: Emoji: fix image of emoji when it is submitted via comment --- app/assets/javascripts/awards_handler.coffee | 16 ++++++++++------ app/assets/javascripts/notes.js.coffee | 2 +- app/controllers/projects/notes_controller.rb | 1 + app/helpers/issues_helper.rb | 2 +- app/views/votes/_votes_block.html.haml | 1 - lib/award_emoji.rb | 4 ++++ 6 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 29b11b0cc58..cac9c10332a 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -5,7 +5,7 @@ class @AwardsHandler @postEmoji emoji, => @addAwardToEmojiBar(emoji) - addAwardToEmojiBar: (emoji) -> + addAwardToEmojiBar: (emoji, custom_path = '') -> if @exist(emoji) if @isActive(emoji) @decrementCounter(emoji) @@ -15,7 +15,7 @@ class @AwardsHandler counter.parent().addClass("active") @addMeToAuthorList(emoji) else - @createEmoji(emoji) + @createEmoji(emoji, custom_path) exist: (emoji) -> @findEmojiIcon(emoji).length > 0 @@ -58,11 +58,11 @@ class @AwardsHandler ), 200 - createEmoji: (emoji) -> + createEmoji: (emoji, custom_path) -> nodes = [] nodes.push("
") nodes.push("
") - nodes.push(@getImage(emoji)) + nodes.push(@getImage(emoji, custom_path)) nodes.push("
") nodes.push("
1") nodes.push("
") @@ -71,8 +71,12 @@ class @AwardsHandler $(".award").tooltip() - getImage: (emoji) -> - $("li[data-emoji='" + emoji + "'").html() + getImage: (emoji, custom_path) -> + if custom_path + $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) + else + $("li[data-emoji='" + emoji + "'").html() + postEmoji: (emoji, callback) -> $.post @post_emoji_url, { diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 73a95c455e8..7de7632201d 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -121,7 +121,7 @@ class @Notes @initTaskList() if note.award - awards_handler.addAwardToEmojiBar(note.note) + awards_handler.addAwardToEmojiBar(note.note, note.emoji_path) ### Check if note does not exists on page diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 357b292980d..98bf056a605 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -133,6 +133,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_id: note.discussion_id, html: note_to_html(note), award: note.is_award, + emoji_path: note.is_award ? ::AwardEmoji.path_to_emoji_image(note.note) : "", note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index bca32322096..bf289c6db14 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -88,7 +88,7 @@ module IssuesHelper end def url_to_emoji(name) - emoji_path = "emoji/#{Emoji.emoji_filename(name)}.png" + emoji_path = ::AwardEmoji.path_to_emoji_image(name) url_to_image(emoji_path) end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index afd1c2745a1..5392915b4dd 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -12,7 +12,6 @@ - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" - :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "#{votable.class}" diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 95b9c8f921a..9e296f0bc3c 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -3,4 +3,8 @@ class AwardEmoji "beers", "disappointed", "ok_hand", "helicopter", "shit", "airplane", "alarm_clock", "ambulance", "anguished", "two_hearts", "wink"] + + def self.path_to_emoji_image(name) + "emoji/#{Emoji.emoji_filename(name)}.png" + end end \ No newline at end of file -- cgit v1.2.1 From db91ef3a2a46511da23dff780791a70594e57312 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:03:45 +0200 Subject: better regexp --- app/services/notes/create_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index f448f61cc86..dbff58dfb9c 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,7 +35,7 @@ module Notes end def contains_emoji_only?(note) - note =~ /^:[-_+[:alnum:]]*:\s?/ + note =~ /\A:[-_+[:alnum:]]*:\s?\z/ end def emoji_name(note) -- cgit v1.2.1 From 92943580cb1647930dbfdd8d2957213326c134d9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:10:58 +0200 Subject: improve style --- app/assets/stylesheets/pages/issuable.scss | 4 +++- app/views/votes/_votes_block.html.haml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 5b73e20df7d..7dd4f239c78 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -135,8 +135,10 @@ padding: 0 0 5px 5px; line-height: 1; - #add-award { + .add-award { font-size: 27px; + color: #ccc; + &:hover { text-decoration: none; } diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 5392915b4dd..fff74745919 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -7,7 +7,8 @@ = vote.last.count .dropdown.awards-controls - %a#add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + + %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + = icon('plus-circle') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" -- cgit v1.2.1 From fdd5a8f2e16cc210f24d93334877f1ca7ce92ce9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 00:59:07 +0200 Subject: addressing comments --- app/helpers/issues_helper.rb | 4 ++++ app/models/note.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 10 +++++----- db/migrate/20151106000015_add_is_award_to_notes.rb | 3 ++- db/schema.rb | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index bf289c6db14..3a238824c0d 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -104,6 +104,10 @@ module IssuesHelper ::AwardEmoji::EMOJI_LIST end + def note_active_class(notes, current_user) + notes.pluck(:author_id).include?(current_user.id) ? "active" : "" + end + # Required for Gitlab::Markdown::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/models/note.rb b/app/models/note.rb index 458d433211c..d53f568a671 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -50,8 +50,8 @@ class Note < ActiveRecord::Base mount_uploader :attachment, AttachmentUploader # Scopes - scope :awards, ->{ where("is_award IS TRUE") } - scope :nonawards, ->{ where("is_award IS FALSE") } + scope :awards, ->{ where(is_award: true) } + scope :nonawards, ->{ where(is_award: false) } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :inline, ->{ where("line_code IS NOT NULL") } scope :not_inline, ->{ where(line_code: [nil, '']) } diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index fff74745919..3eadf209a59 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,10 +1,10 @@ .awards.votes-block - - votable.notes.awards.grouped_awards.each do | vote | - .award{class: ("active" if vote.last.pluck(:author_id).include?(current_user.id)), title: emoji_author_list(vote.last, current_user)} - .icon{"data-emoji" => "#{vote.first}"} - = image_tag url_to_emoji(vote.first), height: "20px", width: "20px" + - votable.notes.awards.grouped_awards.each do | note | + .award{class: (note_active_class(note.last, current_user)), title: emoji_author_list(note.last, current_user)} + .icon{"data-emoji" => "#{note.first}"} + = image_tag url_to_emoji(note.first), height: "20px", width: "20px" .counter - = vote.last.count + = note.last.count .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb index bffe85df3da..02b271637e9 100644 --- a/db/migrate/20151106000015_add_is_award_to_notes.rb +++ b/db/migrate/20151106000015_add_is_award_to_notes.rb @@ -1,5 +1,6 @@ class AddIsAwardToNotes < ActiveRecord::Migration def change - add_column :notes, :is_award, :boolean, default: false + add_column :notes, :is_award, :boolean, default: false, null: false + add_index :notes, :is_award end end diff --git a/db/schema.rb b/db/schema.rb index 6c322d33682..f5511ac1898 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -554,13 +554,14 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.boolean "system", default: false, null: false t.text "st_diff" t.integer "updated_by_id" - t.boolean "is_award", default: false + t.boolean "is_award", default: false, null: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree + add_index "notes", ["is_award"], name: "index_notes_on_is_award", using: :btree add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree -- cgit v1.2.1 From 73bc9edc4410d228db0258c7f0c6fa84dd1d2c49 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:00:56 +0200 Subject: add changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..6bc0d07b1cf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -50,6 +50,7 @@ v 8.2.0 - Fix bug when milestone/label filter was empty for dashboard issues page - Add ability to create milestone in group projects from single form - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez) + - Add Award Emoji to issue and merge request pages v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) -- cgit v1.2.1 From fa2ed94fe9aa9f4d43c2aec7d99103982976063f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:19:32 +0200 Subject: fix schema --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index f5511ac1898..f77f6dfc66d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -531,7 +531,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.string "type" t.string "description", default: "", null: false t.string "avatar" - t.boolean "visible", default: false + t.boolean "public", default: false end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree -- cgit v1.2.1 From a2912074be67deb6345a37787c14b7e640be26f8 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 01:31:15 +0200 Subject: satisfy rubocop --- app/controllers/projects/notes_controller.rb | 4 ++-- db/schema.rb | 4 +--- lib/award_emoji.rb | 8 +++++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 98bf056a605..8159cc50838 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_admin_note!, only: [:update, :destroy] - before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]] + before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle] def index current_fetched_at = Time.now.to_i @@ -76,7 +76,7 @@ class Projects::NotesController < Projects::ApplicationController project.notes.create(data) end - render json: {ok: true} + render json: { ok: true } end private diff --git a/db/schema.rb b/db/schema.rb index f77f6dfc66d..3062bf4d419 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -642,9 +642,7 @@ ActiveRecord::Schema.define(version: 20151116144118) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false - t.text "issues_template" + t.integer "commit_count", default: 0 t.text "import_error" end diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 9e296f0bc3c..d58a196c4ef 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -1,10 +1,12 @@ class AwardEmoji - EMOJI_LIST = ["+1", "-1", "100", "blush", "heart", "smile", "rage", + EMOJI_LIST = [ + "+1", "-1", "100", "blush", "heart", "smile", "rage", "beers", "disappointed", "ok_hand", "helicopter", "shit", "airplane", "alarm_clock", - "ambulance", "anguished", "two_hearts", "wink"] + "ambulance", "anguished", "two_hearts", "wink" + ] def self.path_to_emoji_image(name) "emoji/#{Emoji.emoji_filename(name)}.png" end -end \ No newline at end of file +end -- cgit v1.2.1 From 060e59f05436f29640f6890c69426be1cd79e5cd Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 11:06:12 +0100 Subject: Lfs on by default. --- config/gitlab.yml.example | 2 +- config/initializers/1_settings.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8fdb2603ce8..1788d4c2c7c 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -126,7 +126,7 @@ production: &base ## Git LFS lfs: - enabled: false + enabled: true # The location where LFS objects are stored (default: shared/lfs-objects). # storage_path: shared/lfs-objects diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 6b7990c0ab0..b498fb1e1da 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -203,7 +203,7 @@ Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mail # Git LFS # Settings['lfs'] ||= Settingslogic.new({}) -Settings.lfs['enabled'] = false if Settings.lfs['enabled'].nil? +Settings.lfs['enabled'] = true if Settings.lfs['enabled'].nil? Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root) # -- cgit v1.2.1 From 3cc2b48a82aae8b535ddf11e9057a29f2242524c Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 11:39:26 +0100 Subject: Backup LFS objects same as any upload. --- doc/raketasks/backup_restore.md | 2 +- lib/backup/lfs.rb | 13 +++++++++++++ lib/backup/manager.rb | 2 +- lib/tasks/gitlab/backup.rake | 21 +++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 lib/backup/lfs.rb diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 1a5442cdac7..4e645b21a85 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ``` Also you can choose what should be backed up by adding environment variable SKIP. Available options: db, -uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts). +uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts), lfs (LFS objects). Use a comma to specify several options at the same time. ``` diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb new file mode 100644 index 00000000000..4153467fbee --- /dev/null +++ b/lib/backup/lfs.rb @@ -0,0 +1,13 @@ +require 'backup/files' + +module Backup + class Lfs < Files + def initialize + super('lfs', Settings.lfs.storage_path) + end + + def create_files_dir + Dir.mkdir(app_files_dir, 0700) + end + end +end diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb index e7eda7c6f45..099062eeb8b 100644 --- a/lib/backup/manager.rb +++ b/lib/backup/manager.rb @@ -154,7 +154,7 @@ module Backup end def archives_to_backup - %w{uploads builds artifacts}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact + %w{uploads builds artifacts lfs}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact end def folders_to_backup diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 3c46bcea40e..cb4abe13799 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -13,6 +13,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:uploads:create"].invoke Rake::Task["gitlab:backup:builds:create"].invoke Rake::Task["gitlab:backup:artifacts:create"].invoke + Rake::Task["gitlab:backup:lfs:create"].invoke backup = Backup::Manager.new backup.pack @@ -34,6 +35,7 @@ namespace :gitlab do Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads") Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds") Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts") + Rake::Task["gitlab:backup:lfs:restore"].invoke unless backup.skipped?("lfs") Rake::Task["gitlab:shell:setup"].invoke backup.cleanup @@ -134,6 +136,25 @@ namespace :gitlab do end end + namespace :lfs do + task create: :environment do + $progress.puts "Dumping lfs objects ... ".blue + + if ENV["SKIP"] && ENV["SKIP"].include?("lfs") + $progress.puts "[SKIPPED]".cyan + else + Backup::Lfs.new.dump + $progress.puts "done".green + end + end + + task restore: :environment do + $progress.puts "Restoring lfs objects ... ".blue + Backup::Lfs.new.restore + $progress.puts "done".green + end + end + def configure_cron_mode if ENV['CRON'] # We need an object we can say 'puts' and 'print' to; let's use a -- cgit v1.2.1 From 2219743d5c6bffd80eaab55db76c0f7d19a8ae61 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 18 Nov 2015 13:21:57 +0100 Subject: Add lfs to backup specs. --- spec/tasks/gitlab/backup_rake_spec.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index fb5e74af648..63bed2414df 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do end def reenable_backup_sub_tasks - %w{db repo uploads builds artifacts}.each do |subtask| + %w{db repo uploads builds artifacts lfs}.each do |subtask| Rake::Task["gitlab:backup:#{subtask}:create"].reenable end end @@ -49,7 +49,7 @@ describe 'gitlab:app namespace rake task' do to raise_error(SystemExit) end - it 'should invoke restoration on mach' do + it 'should invoke restoration on match' do allow(YAML).to receive(:load_file). and_return({ gitlab_version: gitlab_version }) expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke) @@ -57,6 +57,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive(:invoke) + expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive(:invoke) expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke) expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end @@ -114,7 +115,7 @@ describe 'gitlab:app namespace rake task' do it 'should set correct permissions on the tar contents' do tar_contents, exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz} ) expect(exit_status).to eq(0) expect(tar_contents).to match('db/') @@ -122,12 +123,13 @@ describe 'gitlab:app namespace rake task' do expect(tar_contents).to match('repositories/') expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('artifacts.tar.gz') + expect(tar_contents).to match('lfs.tar.gz') expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/) end it 'should delete temp directories' do temp_dirs = Dir.glob( - File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}') + File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs}') ) expect(temp_dirs).to be_empty @@ -163,13 +165,14 @@ describe 'gitlab:app namespace rake task' do it "does not contain skipped item" do tar_contents, _exit_status = Gitlab::Popen.popen( - %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz} + %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz} ) expect(tar_contents).to match('db/') expect(tar_contents).to match('uploads.tar.gz') expect(tar_contents).to match('builds.tar.gz') expect(tar_contents).to match('artifacts.tar.gz') + expect(tar_contents).to match('lfs.tar.gz') expect(tar_contents).not_to match('repositories/') end @@ -183,6 +186,7 @@ describe 'gitlab:app namespace rake task' do expect(Rake::Task["gitlab:backup:uploads:restore"]).not_to receive :invoke expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke + expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive :invoke expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error end -- cgit v1.2.1 From 9b724baa7f03e02ca847d80eff4df529e63e79de Mon Sep 17 00:00:00 2001 From: Ferdinand Rosario Date: Thu, 19 Nov 2015 15:17:12 +0530 Subject: Updated rails patch --- Gemfile | 2 +- Gemfile.lock | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/Gemfile b/Gemfile index 8a19885bcb1..98890108562 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem 'rails', '4.1.12' +gem 'rails', '4.1.14' # Specify a sprockets version due to security issue # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY diff --git a/Gemfile.lock b/Gemfile.lock index 99cdc2a50ae..8dd49bcf968 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,25 +4,25 @@ GEM CFPropertyList (2.3.1) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.12) - actionpack (= 4.1.12) - actionview (= 4.1.12) + actionmailer (4.1.14) + actionpack (= 4.1.14) + actionview (= 4.1.14) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.12) - actionview (= 4.1.12) - activesupport (= 4.1.12) + actionpack (4.1.14) + actionview (= 4.1.14) + activesupport (= 4.1.14) rack (~> 1.5.2) rack-test (~> 0.6.2) - actionview (4.1.12) - activesupport (= 4.1.12) + actionview (4.1.14) + activesupport (= 4.1.14) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.12) - activesupport (= 4.1.12) + activemodel (4.1.14) + activesupport (= 4.1.14) builder (~> 3.1) - activerecord (4.1.12) - activemodel (= 4.1.12) - activesupport (= 4.1.12) + activerecord (4.1.14) + activemodel (= 4.1.14) + activesupport (= 4.1.14) arel (~> 5.0.0) activerecord-deprecated_finders (1.0.4) activerecord-session_store (0.1.1) @@ -33,7 +33,7 @@ GEM activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.12) + activesupport (4.1.14) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) @@ -509,21 +509,21 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.1.12) - actionmailer (= 4.1.12) - actionpack (= 4.1.12) - actionview (= 4.1.12) - activemodel (= 4.1.12) - activerecord (= 4.1.12) - activesupport (= 4.1.12) + rails (4.1.14) + actionmailer (= 4.1.14) + actionpack (= 4.1.14) + actionview (= 4.1.14) + activemodel (= 4.1.14) + activerecord (= 4.1.14) + activesupport (= 4.1.14) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.12) + railties (= 4.1.14) sprockets-rails (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.12) - actionpack (= 4.1.12) - activesupport (= 4.1.12) + railties (4.1.14) + actionpack (= 4.1.14) + activesupport (= 4.1.14) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) @@ -687,7 +687,7 @@ GEM multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) - sprockets-rails (2.3.2) + sprockets-rails (2.3.3) actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) @@ -886,7 +886,7 @@ DEPENDENCIES rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.0.5) - rails (= 4.1.12) + rails (= 4.1.14) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) -- cgit v1.2.1 From 618b54910ef3183cd1a3bf71ffe6301e029973fb Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Nov 2015 10:50:38 +0100 Subject: Improve UI for emoji awards Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/issuable.scss | 51 ++++++++++++++++--------- app/views/projects/issues/_discussion.html.haml | 2 +- app/views/votes/_votes_block.html.haml | 4 +- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 7dd4f239c78..affa34a5f83 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -103,19 +103,23 @@ } .awards { + @include clearfix; + line-height: 32px; + margin: 5px 0; + .award { + @include border-radius(5px); + border: 1px solid; - padding: 1px 3px; - width: 50px; - border-radius: 5px; - float:left; - margin: 0 3px; - border-color: #ccc; + padding: 0px 10px; + float: left; + margin: 0 5px; + border-color: $border-color; cursor: pointer; &.active { - border-color: rgba(79,176,252,0.4); - background-color: rgba(79,176,252,0.1); + border-color: $border-gray-light; + background-color: $gray-light; .counter { font-weight: bold; @@ -126,28 +130,39 @@ float: left; margin-right: 10px; } + + .counter { + float: left; + } } .awards-controls { - height: 25px; - width: 28px; + line-height: 32px; + margin-left: 10px; float: left; - padding: 0 0 5px 5px; - line-height: 1; .add-award { - font-size: 27px; - color: #ccc; + font-size: 24px; + color: $gl-gray; + position: relative; + top: 2px; - &:hover { - text-decoration: none; - } + &:hover, &:link { text-decoration: none; } } + + .awards-menu { + padding: $gl-padding; + min-width: 214px; + + > li { + margin: 5px; + } + } } - + .awards-menu{ li { float: left; diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index c5fd863ae99..020952dd001 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -7,7 +7,7 @@ = render 'shared/show_aside' -.gray-content-block.second-block +.gray-content-block.second-block.oneline-block .row .col-md-9 .votes-holder.pull-right diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 3eadf209a59..f32e5193d1a 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -8,7 +8,7 @@ .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-circle') + = icon('plus-square-o') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" @@ -27,4 +27,4 @@ emoji = $(this).find(".icon").data("emoji") awards_handler.addAward(emoji) - $(".award").tooltip() \ No newline at end of file + $(".award").tooltip() -- cgit v1.2.1 From caf1c9d85bb9df05b8e7f90e1a844fe8ecb5a857 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 10:53:40 +0100 Subject: Fix CHANGELOG --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..3d0cd3550ec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,9 @@ v 8.2.0 - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu) - Improved performance of finding users by one of their Email addresses - Add allow_failure field to commit status API (Stan Hu) + - Commits without .gitlab-ci.yml are marked as skipped + - Save detailed error when YAML syntax is invalid + - Added build artifacts - Improved performance of replacing references in comments - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL @@ -119,7 +122,6 @@ v 8.1.0 - Show CI status on Your projects page and Starred projects page - Remove "Continuous Integration" page from dashboard - Add notes and SSL verification entries to hook APIs (Ben Boeckel) - - Added build artifacts - Fix grammar in admin area "labels" .nothing-here-block when no labels exist. - Move CI runners page to project settings area - Move CI variables page to project settings area -- cgit v1.2.1 From 2c7d8678623a9e207d54e2e39d7eef9e2f77cb47 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 19 Nov 2015 10:59:58 +0100 Subject: Few minor improvements to emoji awards UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/issuable.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index affa34a5f83..3a08ee70bc7 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -104,8 +104,8 @@ .awards { @include clearfix; - line-height: 32px; - margin: 5px 0; + line-height: 34px; + margin: 2px 0; .award { @include border-radius(5px); @@ -137,7 +137,6 @@ } .awards-controls { - line-height: 32px; margin-left: 10px; float: left; -- cgit v1.2.1 From 96cdacd4eae3fb939f2da4ba0240f7dfa10b63da Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 11:05:21 +0100 Subject: Changelog entries for atom/profile improvements --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 94f07a31689..c979c23e06b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) v 8.2.0 + - Improved performance of finding projects and groups in various places + - Improved performance of rendering user profile pages and Atom feeds - Fix grouping of contributors by email in graph. - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) -- cgit v1.2.1 From 50f654161036ac0f97d9f931dc11678a9509d083 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 17:17:40 +0100 Subject: Added index on issues.state This field is queried when filtering issues and due to the lack of an index would end up triggering a sequence scan. --- db/migrate/20151109134526_add_issues_state_index.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20151109134526_add_issues_state_index.rb diff --git a/db/migrate/20151109134526_add_issues_state_index.rb b/db/migrate/20151109134526_add_issues_state_index.rb new file mode 100644 index 00000000000..1c4d2e30171 --- /dev/null +++ b/db/migrate/20151109134526_add_issues_state_index.rb @@ -0,0 +1,5 @@ +class AddIssuesStateIndex < ActiveRecord::Migration + def change + add_index :issues, :state + end +end -- cgit v1.2.1 From d63da890943c57694fab0dc250655a2bcd49fabc Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 9 Nov 2015 17:18:26 +0100 Subject: Added index on projects.visibility_level --- db/migrate/20151109134916_add_projects_visibility_level_index.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20151109134916_add_projects_visibility_level_index.rb diff --git a/db/migrate/20151109134916_add_projects_visibility_level_index.rb b/db/migrate/20151109134916_add_projects_visibility_level_index.rb new file mode 100644 index 00000000000..600b4bafd98 --- /dev/null +++ b/db/migrate/20151109134916_add_projects_visibility_level_index.rb @@ -0,0 +1,5 @@ +class AddProjectsVisibilityLevelIndex < ActiveRecord::Migration + def change + add_index :projects, :visibility_level + end +end -- cgit v1.2.1 From e4ae8b1385eecce2e4438f83017799644a8b92ce Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:48:27 +0100 Subject: Updated DB schema with new issues/projects indexes --- db/schema.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 462d5ed3b29..d687e68d098 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -384,6 +384,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree + add_index "issues", ["state"], name: "index_issues_on_state", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree create_table "keys", force: true do |t| @@ -641,9 +642,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "star_count", default: 0, null: false t.string "import_type" t.string "import_source" - t.integer "commit_count", default: 0 - t.boolean "merge_requests_ff_only_enabled", default: false - t.text "issues_template" + t.integer "commit_count", default: 0 t.text "import_error" end @@ -653,6 +652,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree + add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: true do |t| t.integer "project_id", null: false -- cgit v1.2.1 From 45840426a79f5ab251caf71400d3e4ed5f5eedbf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:48:56 +0100 Subject: Added benchmark for IssuesFinder --- spec/benchmarks/finders/issues_finder_spec.rb | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 spec/benchmarks/finders/issues_finder_spec.rb diff --git a/spec/benchmarks/finders/issues_finder_spec.rb b/spec/benchmarks/finders/issues_finder_spec.rb new file mode 100644 index 00000000000..c233fa82cf2 --- /dev/null +++ b/spec/benchmarks/finders/issues_finder_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper' + +describe IssuesFinder, benchmark: true do + describe '#execute' do + let(:user) { create(:user) } + let(:project) { create(:project, :public) } + + let(:label1) { create(:label, project: project, title: 'A') } + let(:label2) { create(:label, project: project, title: 'B') } + + before do + 10.times do |n| + issue = create(:issue, author: user, project: project) + + if n > 4 + create(:label_link, label: label1, target: issue) + create(:label_link, label: label2, target: issue) + end + end + end + + describe 'retrieving issues without labels' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, + state: 'opened') + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(2000) } + end + + describe 'retrieving issues with labels' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: label1.title, + state: 'opened') + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(1000) } + end + + describe 'retrieving issues for a single project' do + let(:finder) do + IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, + state: 'opened', project_id: project.id) + end + + benchmark_subject { finder.execute } + + it { is_expected.to iterate_per_second(2000) } + end + end +end -- cgit v1.2.1 From c232a0f97f9b724a142c728ebeceba25ef15ab32 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:49:16 +0100 Subject: Removed trailing whitespace from IssuableFinder --- app/finders/issuable_finder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index c407dfc163a..30ec8f60098 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -62,10 +62,10 @@ class IssuableFinder if project? @project = Project.find(params[:project_id]) - + unless Ability.abilities.allowed?(current_user, :read_project, @project) @project = nil - end + end else @project = nil end -- cgit v1.2.1 From e9cd58f5d50a7b5cfc14e08cd9526505e24f1071 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:49:31 +0100 Subject: Memoize IssuableFinder#projects Since this method's returned data doesn't change between calls on the same IssuableFinder instance we can just memoize this similar to the "project" method. --- app/finders/issuable_finder.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 30ec8f60098..15b5d6ab34c 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -77,11 +77,11 @@ class IssuableFinder return @projects if defined?(@projects) if project? - project + @projects = project elsif current_user && params[:authorized_only].presence && !current_user_related? - current_user.authorized_projects + @projects = current_user.authorized_projects else - ProjectsFinder.new.execute(current_user) + @projects = ProjectsFinder.new.execute(current_user) end end -- cgit v1.2.1 From 8591cc02be6b12ed60f763a5e0147f2cbbca99e1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 12:50:36 +0100 Subject: Use a JOIN in IssuableFinder#by_project When using IssuableFinder/IssuesFinder to find issues for multiple projects it's more efficient to use a JOIN + a "WHERE project_id IN" condition opposed to running a sub-query. This change means that when finding issues without labels we're now using the following SQL: SELECT issues.* FROM issues JOIN projects ON projects.id = issues.project_id LEFT JOIN label_links ON label_links.target_type = 'Issue' AND label_links.target_id = issues.id WHERE ( projects.id IN (...) OR projects.visibility_level IN (20, 10) ) AND issues.state IN ('opened','reopened') AND label_links.id IS NULL ORDER BY issues.id DESC; instead of: SELECT issues.* FROM issues LEFT JOIN label_links ON label_links.target_type = 'Issue' AND label_links.target_id = issues.id WHERE issues.project_id IN ( SELECT id FROM projects WHERE id IN (...) OR visibility_level IN (20,10) ) AND issues.state IN ('opened','reopened') AND label_links.id IS NULL ORDER BY issues.id DESC; The big benefit here is that in the last case PostgreSQL can't properly use all available indexes. In particular it ends up performing a sequence scan on the "label_links" table (processing around 290 000 rows). The new query is roughly 2x as fast as the old query. --- app/finders/issuable_finder.rb | 10 +++++++--- app/models/concerns/issuable.rb | 3 +++ app/models/merge_request.rb | 3 +++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 15b5d6ab34c..3d5e8b6fbe7 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -190,8 +190,10 @@ class IssuableFinder def by_project(items) items = - if projects - items.of_projects(projects).references(:project) + if project? + items.of_projects(projects).references_project + elsif projects + items.merge(projects.reorder(nil)).join_project else items.none end @@ -206,7 +208,9 @@ class IssuableFinder end def sort(items) - items.sort(params[:sort]) + # Ensure we always have an explicit sort order (instead of inheriting + # multiple orders when combining ActiveRecord::Relation objects). + params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc) end def by_assignee(items) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 492a026add9..97d3cb18f4e 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -35,6 +35,9 @@ module Issuable scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') } scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') } + scope :join_project, -> { joins(:project) } + scope :references_project, -> { references(:project) } + delegate :name, :email, to: :author, diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 2eb03b8ba5b..1e8d9908f0a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -134,6 +134,9 @@ class MergeRequest < ActiveRecord::Base scope :closed, -> { with_state(:closed) } scope :closed_and_merged, -> { with_states(:closed, :merged) } + scope :join_project, -> { joins(:target_project) } + scope :references_project, -> { references(:target_project) } + def self.reference_prefix '!' end -- cgit v1.2.1 From 901d3dee0fdd0f67b429dd3741fe047f69e96d4a Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 11 Nov 2015 13:00:41 +0100 Subject: Changelog entry for finding issues performance --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index ea8d5d6000f..baf7c8e5ad7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.2.0 - Improved performance of finding projects and groups in various places - Improved performance of rendering user profile pages and Atom feeds - Fix grouping of contributors by email in graph. + - Improved performance of finding issues with/without labels - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) -- cgit v1.2.1 From 2b907f61ff5db3ff68b27a9d3bb164745ab7703b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 18 Nov 2015 16:32:00 +0100 Subject: Commits without .gitlab-ci.yml are marked as skipped - Save detailed error when YAML syntax --- app/models/ci/commit.rb | 5 ++++- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 6 +++++- spec/services/ci/create_commit_service_spec.rb | 20 +++++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 33b57173928..73c1570c212 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -188,12 +188,15 @@ module Ci end def config_processor + return nil unless ci_yaml_file @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError => e save_yaml_error(e.message) nil + rescue Psych::SyntaxError => e + save_yaml_error(e.message) + nil rescue Exception => e - logger.error e.message + "\n" + e.backtrace.join("\n") save_yaml_error("Undefined yaml error") nil end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 7d90f9877c6..6f287719ba6 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -425,8 +425,12 @@ module Ci end describe "Error handling" do + it "fails to parse YAML" do + expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError) + end + it "indicates that object is invalid" do - expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError) + expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError) end it "returns errors if tags parameter is invalid" do diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index e3a8fe9681b..392f5fce35f 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -100,7 +100,7 @@ module Ci end it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file') + stub_ci_commit_yaml_file('invalid: file: fiile') commits = [{ message: message }] commit = service.execute(project, user, ref: 'refs/tags/0_1', @@ -110,6 +110,24 @@ module Ci ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to be_nil + end + end + + describe :config_processor do + it "skips builds creation if yaml is invalid" do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "message" } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to_not be_nil end end -- cgit v1.2.1 From 0df7a32ea50baf251f03e6bfc5b91c5ccb68aad0 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:08:30 +0100 Subject: Fix tests --- app/models/ci/commit.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 38 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 73c1570c212..b0c78499e49 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -196,7 +196,7 @@ module Ci rescue Psych::SyntaxError => e save_yaml_error(e.message) nil - rescue Exception => e + rescue Exception save_yaml_error("Undefined yaml error") nil end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 392f5fce35f..58d81c46a6d 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -53,7 +53,7 @@ module Ci end end - it 'fails commits without .gitlab-ci.yml' do + it 'skips commits without .gitlab-ci.yml' do stub_ci_commit_yaml_file(nil) result = service.execute(project, user, ref: 'refs/heads/0_1', @@ -63,7 +63,24 @@ module Ci ) expect(result).to be_persisted expect(result.builds.any?).to be_falsey - expect(result.status).to eq('failed') + expect(result.status).to eq('skipped') + expect(commit.yaml_errors).to_not be_nil + end + + it 'skips commits if yaml is invalid' do + message = 'message' + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq('skipped') + expect(commit.yaml_errors).to_not be_nil end describe :ci_skip? do @@ -114,23 +131,6 @@ module Ci end end - describe :config_processor do - it "skips builds creation if yaml is invalid" do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "message" } - stub_ci_commit_yaml_file('invalid: file: file') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - expect(commit.yaml_errors).to_not be_nil - end - end - it "skips build creation if there are already builds" do allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } -- cgit v1.2.1 From fd6b58949652c5fb9925c061fae823da7e468ca2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:09:24 +0100 Subject: Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml --- CHANGELOG | 1 + app/services/git_push_service.rb | 12 ------------ 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ea8d5d6000f..01f18dea23b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.2.0 - Add allow_failure field to commit status API (Stan Hu) - Commits without .gitlab-ci.yml are marked as skipped - Save detailed error when YAML syntax is invalid + - Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml - Added build artifacts - Improved performance of replacing references in comments - Show last project commit to default branch on project home page diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index ccb6b97858c..f11690aa3f4 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -58,12 +58,6 @@ class GitPushService @push_data = build_push_data(oldrev, newrev, ref) - # If CI was disabled but .gitlab-ci.yml file was pushed - # we enable CI automatically - if !project.builds_enabled? && gitlab_ci_yaml?(newrev) - project.enable_ci - end - EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) @@ -134,10 +128,4 @@ class GitPushService def commit_user(commit) commit.author || user end - - def gitlab_ci_yaml?(sha) - @project.repository.blob_at(sha, '.gitlab-ci.yml') - rescue Rugged::ReferenceError - nil - end end -- cgit v1.2.1 From 1f9a0bd764ed2935c4438dbc81001b0d69df26ea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 12:15:54 +0100 Subject: Bump GitLab Workhorse version to 0.4.2 --- GITLAB_WORKHORSE | 1 - GITLAB_WORKHORSE_VERSION | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 GITLAB_WORKHORSE diff --git a/GITLAB_WORKHORSE b/GITLAB_WORKHORSE deleted file mode 100644 index 267577d47e4..00000000000 --- a/GITLAB_WORKHORSE +++ /dev/null @@ -1 +0,0 @@ -0.4.1 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 9e11b32fcaa..2b7c5ae0184 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.3.1 +0.4.2 -- cgit v1.2.1 From 23c5473cc0bd9b9034c5671fbea1356b0fb531e5 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 13:20:09 +0200 Subject: added spinach tests --- app/assets/javascripts/awards_handler.coffee | 4 +- app/helpers/issues_helper.rb | 6 ++- app/models/note.rb | 1 + app/views/votes/_votes_block.html.haml | 38 +++++++------- features/project/issues/award_emoji.feature | 14 ++++++ features/steps/project/issues/award_emoji.rb | 41 +++++++++++++++ spec/models/note_spec.rb | 75 ---------------------------- 7 files changed, 83 insertions(+), 96 deletions(-) create mode 100644 features/project/issues/award_emoji.feature create mode 100644 features/steps/project/issues/award_emoji.rb diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index cac9c10332a..f5b9adbe9e2 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -75,7 +75,7 @@ class @AwardsHandler if custom_path $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) else - $("li[data-emoji='" + emoji + "'").html() + $("li[data-emoji='" + emoji + "']").html() postEmoji: (emoji, callback) -> @@ -88,4 +88,4 @@ class @AwardsHandler callback.call() findEmojiIcon: (emoji) -> - $(".icon[data-emoji='" + emoji + "'") \ No newline at end of file + $(".icon[data-emoji='" + emoji + "']") \ No newline at end of file diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 3a238824c0d..2c791aa5682 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -105,7 +105,11 @@ module IssuesHelper end def note_active_class(notes, current_user) - notes.pluck(:author_id).include?(current_user.id) ? "active" : "" + if current_user && notes.pluck(:author_id).include?(current_user.id) + "active" + else + "" + end end # Required for Gitlab::Markdown::IssueReferenceFilter diff --git a/app/models/note.rb b/app/models/note.rb index d53f568a671..39645f8f1ad 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -46,6 +46,7 @@ class Note < ActiveRecord::Base validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } + validates :author, presence: true, if: ->(n) { n.is_award } mount_uploader :attachment, AttachmentUploader diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index f32e5193d1a..04e3b5a3814 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -6,25 +6,27 @@ .counter = note.last.count - .dropdown.awards-controls - %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-square-o') - %ul.dropdown-menu.awards-menu - - emoji_list.each do |emoji| - %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" + - if current_user + .dropdown.awards-controls + %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} + = icon('plus-square-o') + %ul.dropdown-menu.awards-menu + - emoji_list.each do |emoji| + %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" -:coffeescript - post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "#{votable.class}" - noteable_id = "#{votable.id}" - window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) +- if current_user + :coffeescript + post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" + noteable_type = "#{votable.class}" + noteable_id = "#{votable.id}" + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) - $(".awards-menu li").click (e)-> - emoji = $(this).data("emoji") - awards_handler.addAward(emoji) + $(".awards-menu li").click (e)-> + emoji = $(this).data("emoji") + awards_handler.addAward(emoji) - $(".awards").on "click", ".award", (e)-> - emoji = $(this).find(".icon").data("emoji") - awards_handler.addAward(emoji) + $(".awards").on "click", ".award", (e)-> + emoji = $(this).find(".icon").data("emoji") + awards_handler.addAward(emoji) - $(".award").tooltip() + $(".award").tooltip() diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature new file mode 100644 index 00000000000..a9bc8ffb9bb --- /dev/null +++ b/features/project/issues/award_emoji.feature @@ -0,0 +1,14 @@ +Feature: Award Emoji + Background: + Given I sign in as a user + And I own project "Shop" + And project "Shop" has issue "Bugfix" + And I visit "Bugfix" issue page + + @javascript + Scenario: I add and remove award in the issue + Given I click to emoji-picker + And I click to emoji in the picker + Then I have award added + And I can remove it by clicking to icon + \ No newline at end of file diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb new file mode 100644 index 00000000000..8f7a45dec0e --- /dev/null +++ b/features/steps/project/issues/award_emoji.rb @@ -0,0 +1,41 @@ +class Spinach::Features::AwardEmoji < Spinach::FeatureSteps + include SharedAuthentication + include SharedProject + include SharedPaths + include Select2Helper + + step 'I visit "Bugfix" issue page' do + visit namespace_project_issue_path(@project.namespace, @project, @issue) + end + + step 'I click to emoji-picker' do + page.within ".awards-controls" do + page.find(".add-award").click + end + end + + step 'I click to emoji in the picker' do + page.within ".awards-menu" do + page.first("img").click + end + end + + step 'I can remove it by clicking to icon' do + page.within ".awards" do + page.first(".award").click + expect(page).to_not have_selector ".award" + end + end + + step 'I have award added' do + page.within ".awards" do + expect(page).to have_selector ".award" + expect(page.find(".award .counter")).to have_content "1" + end + end + + step 'project "Shop" has issue "Bugfix"' do + @project = Project.find_by(name: "Shop") + @issue = create(:issue, title: "Bugfix", project: project) + end +end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 75564839dcf..6e37dae07d0 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -32,77 +32,6 @@ describe Note do it { is_expected.to validate_presence_of(:project) } end - describe '#votable?' do - it 'is true for issue notes' do - note = build(:note_on_issue) - expect(note).to be_votable - end - - it 'is true for merge request notes' do - note = build(:note_on_merge_request) - expect(note).to be_votable - end - - it 'is false for merge request diff notes' do - note = build(:note_on_merge_request_diff) - expect(note).not_to be_votable - end - - it 'is false for commit notes' do - note = build(:note_on_commit) - expect(note).not_to be_votable - end - - it 'is false for commit diff notes' do - note = build(:note_on_commit_diff) - expect(note).not_to be_votable - end - end - - describe 'voting score' do - it 'recognizes a neutral note' do - note = build(:votable_note, note: 'This is not a +1 note') - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a neutral emoji note' do - note = build(:votable_note, note: "I would :+1: this, but I don't want to") - expect(note).not_to be_upvote - expect(note).not_to be_downvote - end - - it 'recognizes a +1 note' do - note = build(:votable_note, note: '+1 for this') - expect(note).to be_upvote - end - - it 'recognizes a +1 emoji as a vote' do - note = build(:votable_note, note: ':+1: for this') - expect(note).to be_upvote - end - - it 'recognizes a thumbsup emoji as a vote' do - note = build(:votable_note, note: ':thumbsup: for this') - expect(note).to be_upvote - end - - it 'recognizes a -1 note' do - note = build(:votable_note, note: '-1 for this') - expect(note).to be_downvote - end - - it 'recognizes a -1 emoji as a vote' do - note = build(:votable_note, note: ':-1: for this') - expect(note).to be_downvote - end - - it 'recognizes a thumbsdown emoji as a vote' do - note = build(:votable_note, note: ':thumbsdown: for this') - expect(note).to be_downvote - end - end - describe "Commit notes" do let!(:note) { create(:note_on_commit, note: "+1 from me") } let!(:commit) { note.noteable } @@ -139,10 +68,6 @@ describe Note do it "should be recognized by #for_commit_diff_line?" do expect(note).to be_for_commit_diff_line end - - it "should not be votable" do - expect(note).not_to be_votable - end end describe 'authorization' do -- cgit v1.2.1 From 671a49cfd53230b57acf579a609bab958e066982 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 14:41:05 +0200 Subject: added specs --- spec/helpers/issues_helper_spec.rb | 26 +++++++++++++++++++++++ spec/models/note_spec.rb | 13 ++++++++++++ spec/services/notes/create_service_spec.rb | 34 ++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 78a6b631eb2..1f2c4ee77b5 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -127,4 +127,30 @@ describe IssuesHelper do it { is_expected.to eq("!1, !2, or !3") } end + describe "#url_to_emoji" do + it "returns url" do + expect(url_to_emoji("smile")).to include("emoji/1F604.png") + end + end + + describe "#emoji_list" do + it "returns url" do + expect(emoji_list).to be_kind_of(Array) + end + end + + describe "#note_active_class" do + before do + @note = create :note + @note1 = create :note + end + + it "returns empty string for unauthenticated user" do + expect(note_active_class(Note.all, nil)).to eq("") + end + + it "returns active string for author" do + expect(note_active_class(Note.all, @note.author)).to eq("active") + end + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 6e37dae07d0..86b6c27a182 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -129,4 +129,17 @@ describe Note do it { expect(Note.search('wow')).to include(note) } end + + describe :grouped_awards do + before do + create :note, note: "smile" + create :note, note: "smile" + end + + it "returns grouped array of notes" do + grouped_array = Note.grouped_awards + expect(Note.grouped_awards.first.first).to eq("smile") + expect(Note.grouped_awards.first.last).to match_array(Note.all) + end + end end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index f2ea0805b2f..066461f5a12 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -24,4 +24,38 @@ describe Notes::CreateService do it { expect(@note.note).to eq('Awesome comment') } end end + + describe "award emoji" do + before do + project.team << [user, :master] + end + + it "creates emoji note" do + opts = { + note: ':smile: ', + noteable_type: 'Issue', + noteable_id: issue.id + } + + @note = Notes::CreateService.new(project, user, opts).execute + + expect(@note).to be_valid + expect(@note.note).to eq('smile') + expect(@note.is_award).to be_truthy + end + + it "creates regular note if emoji name is invalid" do + opts = { + note: ':smile: moretext: ', + noteable_type: 'Issue', + noteable_id: issue.id + } + + @note = Notes::CreateService.new(project, user, opts).execute + + expect(@note).to be_valid + expect(@note.note).to eq(opts[:note]) + expect(@note.is_award).to be_falsy + end + end end -- cgit v1.2.1 From 372dcc217eeb379f603c10c81115dd7579762b59 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 19 Nov 2015 13:51:18 +0100 Subject: Do not limit workhorse POST/PUT size in NGINX Limiting, if any, should happen in gitlab-workhorse. --- lib/support/nginx/gitlab | 4 +--- lib/support/nginx/gitlab-ssl | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 93f2ad07aeb..f38e2e62097 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -114,7 +114,6 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -140,7 +139,6 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -148,13 +146,13 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location @gitlab-workhorse { + client_max_body_size 0; ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. # gzip off; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 90749947fa4..4cc6a17db04 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -161,7 +161,6 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -187,7 +186,6 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -195,13 +193,13 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location @gitlab-workhorse { + client_max_body_size 0; ## If you use HTTPS make sure you disable gzip compression ## to be safe against BREACH attack. gzip off; -- cgit v1.2.1 From d70da301f61f3333b65a5b92de79eea4b7e032f6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 14:13:17 +0100 Subject: Add link to git-lfs client [ci skip] --- doc/workflow/git_lfs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index 4990a9b5aac..e1064051fe8 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -17,7 +17,7 @@ Once the request is authorized, Git LFS client receives instructions from where ## Requirements * Git LFS is supported in GitLab starting with version 8.2 -* Git LFS client version 0.6.0 and up +* Git LFS [client](https://git-lfs.github.com) version 0.6.0 and up ## GitLab and Git LFS -- cgit v1.2.1 From 97842cea7409d80a3bf22f3ab8993a2ce3b02195 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 19 Nov 2015 15:37:20 +0100 Subject: Fix 'Attach a file' link in new tag form --- app/views/projects/_zen.html.haml | 9 ++++++--- app/views/projects/releases/edit.html.haml | 3 +-- app/views/projects/tags/new.html.haml | 13 ++----------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml index 63ebfc9381f..7e6301abde8 100644 --- a/app/views/projects/_zen.html.haml +++ b/app/views/projects/_zen.html.haml @@ -2,9 +2,12 @@ %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") .zen-backdrop - classes << ' js-gfm-input markdown-area' - = f.text_area attr, class: classes, placeholder: '' + - if defined?(f) && f + = f.text_area attr, class: classes, placeholder: '' + - else + = text_area_tag attr, nil, class: classes, placeholder: '' %a.zen-enter-link(tabindex="-1" href="#") - %i.fa.fa-expand + = icon('expand') Edit in fullscreen %a.zen-leave-link(href="#") - %i.fa.fa-compress + = icon('compress') diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index e7db09cdaa9..f516b65ecd0 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -11,10 +11,9 @@ .prepend-top-default = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f| = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit' + = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .error-alert .prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" - diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index e106be794f1..86aa15dc5b3 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -10,7 +10,7 @@ New git tag %hr -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' .col-sm-10 @@ -30,16 +30,7 @@ = label_tag :release_description, 'Release notes', class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do - .zennable - %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox") - .zen-backdrop - = text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: '' - %a.zen-enter-link(tabindex="-1" href="#") - = icon('expand') - Edit in fullscreen - %a.zen-leave-link(href="#") - = icon('compress') - + = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page .form-actions -- cgit v1.2.1 From a5b10196e649b57cf7cc698fe6fb34f73eec47ea Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 15:56:03 +0100 Subject: Fix tests --- spec/services/ci/create_commit_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index 58d81c46a6d..e0ede1d58b7 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -64,7 +64,7 @@ module Ci expect(result).to be_persisted expect(result.builds.any?).to be_falsey expect(result.status).to eq('skipped') - expect(commit.yaml_errors).to_not be_nil + expect(result.yaml_errors).to be_nil end it 'skips commits if yaml is invalid' do @@ -79,7 +79,7 @@ module Ci commits: commits ) expect(commit.builds.any?).to be false - expect(commit.status).to eq('skipped') + expect(commit.status).to eq('failed') expect(commit.yaml_errors).to_not be_nil end -- cgit v1.2.1 From cf61b8e22c9c78165ce55c7c2f78ae0b3c7af7aa Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:01:55 +0100 Subject: Update gitlab-workhorse docs to 0.4.2 --- doc/install/installation.md | 4 ++-- doc/update/8.1-to-8.2.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 7ef46b04065..71b0ef3ebb0 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -327,12 +327,12 @@ GitLab Shell is an SSH access and repository management software developed speci cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse - sudo -u git -H git checkout 0.4.1 + sudo -u git -H git checkout 0.4.2 sudo -u git -H make ### Initialize Database and Activate Advanced Features - # Go to Gitlab installation folder + # Go to GitLab installation folder cd /home/git/gitlab diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 73d899f9c2e..81e13f714b7 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -81,7 +81,7 @@ from GitLab 8.1. cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse -sudo -u git -H git checkout 0.4.1 +sudo -u git -H git checkout 0.4.2 sudo -u git -H make ``` -- cgit v1.2.1 From 094e1cc01b4f98ea4b8cd664344f3b8b583af471 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 16:02:21 +0100 Subject: Align hash literals in IssuesFinder spec --- spec/benchmarks/finders/issues_finder_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/benchmarks/finders/issues_finder_spec.rb b/spec/benchmarks/finders/issues_finder_spec.rb index c233fa82cf2..b57a33004a4 100644 --- a/spec/benchmarks/finders/issues_finder_spec.rb +++ b/spec/benchmarks/finders/issues_finder_spec.rb @@ -22,7 +22,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues without labels' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, - state: 'opened') + state: 'opened') end benchmark_subject { finder.execute } @@ -33,7 +33,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues with labels' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: label1.title, - state: 'opened') + state: 'opened') end benchmark_subject { finder.execute } @@ -44,7 +44,7 @@ describe IssuesFinder, benchmark: true do describe 'retrieving issues for a single project' do let(:finder) do IssuesFinder.new(user, scope: 'all', label_name: Label::None.title, - state: 'opened', project_id: project.id) + state: 'opened', project_id: project.id) end benchmark_subject { finder.execute } -- cgit v1.2.1 From aec4aac0c2825befe8baff392107dcf7702d3d04 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:06:13 +0100 Subject: Update gitlab-shell documentation [ci skip] --- doc/install/installation.md | 2 +- doc/update/8.1-to-8.2.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 7ef46b04065..52ae30af805 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.6] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.7] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 73d899f9c2e..261031442ac 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -68,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.6 +sudo -u git -H git checkout v2.6.7 ``` ### 5. Replace gitlab-git-http-server with gitlab-workhorse -- cgit v1.2.1 From e1e67d383e4b56c9e1848aebc175402a71502e82 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 19 Nov 2015 16:18:13 +0100 Subject: Move Markdown filters and pipelines into folders. --- lib/gitlab/markdown.rb | 80 ++++----- lib/gitlab/markdown/asciidoc_pipeline.rb | 13 -- lib/gitlab/markdown/atom_pipeline.rb | 14 -- lib/gitlab/markdown/autolink_filter.rb | 107 ----------- .../markdown/commit_range_reference_filter.rb | 92 ---------- lib/gitlab/markdown/commit_reference_filter.rb | 88 --------- lib/gitlab/markdown/description_pipeline.rb | 14 -- lib/gitlab/markdown/email_pipeline.rb | 13 -- lib/gitlab/markdown/emoji_filter.rb | 80 --------- .../markdown/external_issue_reference_filter.rb | 63 ------- lib/gitlab/markdown/external_link_filter.rb | 34 ---- lib/gitlab/markdown/filter/autolink_filter.rb | 107 +++++++++++ .../filter/commit_range_reference_filter.rb | 92 ++++++++++ .../markdown/filter/commit_reference_filter.rb | 88 +++++++++ lib/gitlab/markdown/filter/emoji_filter.rb | 80 +++++++++ .../filter/external_issue_reference_filter.rb | 63 +++++++ lib/gitlab/markdown/filter/external_link_filter.rb | 34 ++++ .../markdown/filter/issue_reference_filter.rb | 23 +++ .../markdown/filter/label_reference_filter.rb | 87 +++++++++ lib/gitlab/markdown/filter/markdown_filter.rb | 39 ++++ .../filter/merge_request_reference_filter.rb | 25 +++ lib/gitlab/markdown/filter/redactor_filter.rb | 40 +++++ .../markdown/filter/reference_gatherer_filter.rb | 63 +++++++ lib/gitlab/markdown/filter/relative_link_filter.rb | 157 ++++++++++++++++ lib/gitlab/markdown/filter/sanitization_filter.rb | 99 ++++++++++ .../markdown/filter/snippet_reference_filter.rb | 25 +++ .../markdown/filter/syntax_highlight_filter.rb | 45 +++++ .../markdown/filter/table_of_contents_filter.rb | 63 +++++++ lib/gitlab/markdown/filter/task_list_filter.rb | 24 +++ lib/gitlab/markdown/filter/upload_link_filter.rb | 47 +++++ .../markdown/filter/user_reference_filter.rb | 125 +++++++++++++ lib/gitlab/markdown/full_pipeline.rb | 9 - lib/gitlab/markdown/gfm_pipeline.rb | 41 ----- lib/gitlab/markdown/issue_reference_filter.rb | 23 --- lib/gitlab/markdown/label_reference_filter.rb | 87 --------- lib/gitlab/markdown/markdown_filter.rb | 39 ---- .../markdown/merge_request_reference_filter.rb | 25 --- lib/gitlab/markdown/note_pipeline.rb | 14 -- lib/gitlab/markdown/pipeline.rb | 5 + lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb | 13 ++ lib/gitlab/markdown/pipeline/atom_pipeline.rb | 14 ++ .../markdown/pipeline/description_pipeline.rb | 14 ++ lib/gitlab/markdown/pipeline/email_pipeline.rb | 13 ++ lib/gitlab/markdown/pipeline/full_pipeline.rb | 9 + lib/gitlab/markdown/pipeline/gfm_pipeline.rb | 41 +++++ lib/gitlab/markdown/pipeline/note_pipeline.rb | 14 ++ .../markdown/pipeline/plain_markdown_pipeline.rb | 13 ++ .../markdown/pipeline/post_process_pipeline.rb | 20 +++ .../pipeline/reference_extraction_pipeline.rb | 13 ++ .../markdown/pipeline/single_line_pipeline.rb | 9 + lib/gitlab/markdown/plain_markdown_pipeline.rb | 13 -- lib/gitlab/markdown/post_process_pipeline.rb | 20 --- lib/gitlab/markdown/redactor_filter.rb | 40 ----- .../markdown/reference_extraction_pipeline.rb | 13 -- lib/gitlab/markdown/reference_filter.rb | 4 + lib/gitlab/markdown/reference_gatherer_filter.rb | 63 ------- lib/gitlab/markdown/relative_link_filter.rb | 157 ---------------- lib/gitlab/markdown/sanitization_filter.rb | 99 ---------- lib/gitlab/markdown/single_line_pipeline.rb | 9 - lib/gitlab/markdown/snippet_reference_filter.rb | 25 --- lib/gitlab/markdown/syntax_highlight_filter.rb | 45 ----- lib/gitlab/markdown/table_of_contents_filter.rb | 63 ------- lib/gitlab/markdown/task_list_filter.rb | 24 --- lib/gitlab/markdown/upload_link_filter.rb | 47 ----- lib/gitlab/markdown/user_reference_filter.rb | 125 ------------- lib/gitlab/reference_extractor.rb | 3 +- spec/lib/gitlab/markdown/autolink_filter_spec.rb | 114 ------------ .../markdown/commit_range_reference_filter_spec.rb | 145 --------------- .../markdown/commit_reference_filter_spec.rb | 135 -------------- .../markdown/cross_project_reference_spec.rb | 40 ++--- spec/lib/gitlab/markdown/emoji_filter_spec.rb | 95 ---------- .../external_issue_reference_filter_spec.rb | 79 -------- .../gitlab/markdown/external_link_filter_spec.rb | 31 ---- .../gitlab/markdown/filter/autolink_filter_spec.rb | 112 ++++++++++++ .../filter/commit_range_reference_filter_spec.rb | 143 +++++++++++++++ .../filter/commit_reference_filter_spec.rb | 133 ++++++++++++++ .../gitlab/markdown/filter/emoji_filter_spec.rb | 98 ++++++++++ .../filter/external_issue_reference_filter_spec.rb | 77 ++++++++ .../markdown/filter/external_link_filter_spec.rb | 29 +++ .../markdown/filter/issue_reference_filter_spec.rb | 137 ++++++++++++++ .../markdown/filter/label_reference_filter_spec.rb | 142 +++++++++++++++ .../filter/merge_request_reference_filter_spec.rb | 118 ++++++++++++ .../gitlab/markdown/filter/redactor_filter_spec.rb | 89 +++++++++ .../filter/reference_gatherer_filter_spec.rb | 87 +++++++++ .../markdown/filter/relative_link_filter_spec.rb | 147 +++++++++++++++ .../markdown/filter/sanitization_filter_spec.rb | 197 ++++++++++++++++++++ .../filter/snippet_reference_filter_spec.rb | 116 ++++++++++++ .../filter/syntax_highlight_filter_spec.rb | 17 ++ .../filter/table_of_contents_filter_spec.rb | 97 ++++++++++ .../markdown/filter/task_list_filter_spec.rb | 10 ++ .../markdown/filter/upload_link_filter_spec.rb | 73 ++++++++ .../markdown/filter/user_reference_filter_spec.rb | 120 +++++++++++++ .../gitlab/markdown/issue_reference_filter_spec.rb | 139 -------------- .../gitlab/markdown/label_reference_filter_spec.rb | 144 --------------- .../merge_request_reference_filter_spec.rb | 120 ------------- spec/lib/gitlab/markdown/redactor_filter_spec.rb | 91 ---------- .../markdown/reference_gatherer_filter_spec.rb | 89 --------- .../gitlab/markdown/relative_link_filter_spec.rb | 149 --------------- .../gitlab/markdown/sanitization_filter_spec.rb | 199 --------------------- .../markdown/snippet_reference_filter_spec.rb | 118 ------------ .../markdown/syntax_highlight_filter_spec.rb | 19 -- .../markdown/table_of_contents_filter_spec.rb | 99 ---------- spec/lib/gitlab/markdown/task_list_filter_spec.rb | 12 -- .../lib/gitlab/markdown/upload_link_filter_spec.rb | 75 -------- .../gitlab/markdown/user_reference_filter_spec.rb | 122 ------------- 105 files changed, 3506 insertions(+), 3541 deletions(-) delete mode 100644 lib/gitlab/markdown/asciidoc_pipeline.rb delete mode 100644 lib/gitlab/markdown/atom_pipeline.rb delete mode 100644 lib/gitlab/markdown/autolink_filter.rb delete mode 100644 lib/gitlab/markdown/commit_range_reference_filter.rb delete mode 100644 lib/gitlab/markdown/commit_reference_filter.rb delete mode 100644 lib/gitlab/markdown/description_pipeline.rb delete mode 100644 lib/gitlab/markdown/email_pipeline.rb delete mode 100644 lib/gitlab/markdown/emoji_filter.rb delete mode 100644 lib/gitlab/markdown/external_issue_reference_filter.rb delete mode 100644 lib/gitlab/markdown/external_link_filter.rb create mode 100644 lib/gitlab/markdown/filter/autolink_filter.rb create mode 100644 lib/gitlab/markdown/filter/commit_range_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/commit_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/emoji_filter.rb create mode 100644 lib/gitlab/markdown/filter/external_issue_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/external_link_filter.rb create mode 100644 lib/gitlab/markdown/filter/issue_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/label_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/markdown_filter.rb create mode 100644 lib/gitlab/markdown/filter/merge_request_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/redactor_filter.rb create mode 100644 lib/gitlab/markdown/filter/reference_gatherer_filter.rb create mode 100644 lib/gitlab/markdown/filter/relative_link_filter.rb create mode 100644 lib/gitlab/markdown/filter/sanitization_filter.rb create mode 100644 lib/gitlab/markdown/filter/snippet_reference_filter.rb create mode 100644 lib/gitlab/markdown/filter/syntax_highlight_filter.rb create mode 100644 lib/gitlab/markdown/filter/table_of_contents_filter.rb create mode 100644 lib/gitlab/markdown/filter/task_list_filter.rb create mode 100644 lib/gitlab/markdown/filter/upload_link_filter.rb create mode 100644 lib/gitlab/markdown/filter/user_reference_filter.rb delete mode 100644 lib/gitlab/markdown/full_pipeline.rb delete mode 100644 lib/gitlab/markdown/gfm_pipeline.rb delete mode 100644 lib/gitlab/markdown/issue_reference_filter.rb delete mode 100644 lib/gitlab/markdown/label_reference_filter.rb delete mode 100644 lib/gitlab/markdown/markdown_filter.rb delete mode 100644 lib/gitlab/markdown/merge_request_reference_filter.rb delete mode 100644 lib/gitlab/markdown/note_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/atom_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/description_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/email_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/full_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/gfm_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/note_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/post_process_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb create mode 100644 lib/gitlab/markdown/pipeline/single_line_pipeline.rb delete mode 100644 lib/gitlab/markdown/plain_markdown_pipeline.rb delete mode 100644 lib/gitlab/markdown/post_process_pipeline.rb delete mode 100644 lib/gitlab/markdown/redactor_filter.rb delete mode 100644 lib/gitlab/markdown/reference_extraction_pipeline.rb delete mode 100644 lib/gitlab/markdown/reference_gatherer_filter.rb delete mode 100644 lib/gitlab/markdown/relative_link_filter.rb delete mode 100644 lib/gitlab/markdown/sanitization_filter.rb delete mode 100644 lib/gitlab/markdown/single_line_pipeline.rb delete mode 100644 lib/gitlab/markdown/snippet_reference_filter.rb delete mode 100644 lib/gitlab/markdown/syntax_highlight_filter.rb delete mode 100644 lib/gitlab/markdown/table_of_contents_filter.rb delete mode 100644 lib/gitlab/markdown/task_list_filter.rb delete mode 100644 lib/gitlab/markdown/upload_link_filter.rb delete mode 100644 lib/gitlab/markdown/user_reference_filter.rb delete mode 100644 spec/lib/gitlab/markdown/autolink_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/commit_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/emoji_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/external_link_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/external_link_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb create mode 100644 spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/issue_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/label_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/redactor_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/relative_link_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/sanitization_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/task_list_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/upload_link_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/user_reference_filter_spec.rb diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 969607558ef..f4e2cefca51 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -33,7 +33,7 @@ module Gitlab end def self.render_result(text, context = {}) - pipeline_by_name(context[:pipeline]).call(text, context) + Pipeline[context[:pipeline]].call(text, context) end # Perform post-processing on an HTML String @@ -50,11 +50,9 @@ module Gitlab # # Returns an HTML-safe String def self.post_process(html, context) - pipeline = pipeline_by_name(context[:pipeline]) - context = pipeline.transform_context(context) - - pipeline = pipeline_by_name(:post_process) + context = Pipeline[context[:pipeline]].transform_context(context) + pipeline = Pipeline[:post_process] if context[:xhtml] pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML) else @@ -66,6 +64,7 @@ module Gitlab def self.cacheless_render(text, context = {}) result = render_result(text, context) + output = result[:output] if output.respond_to?(:to_html) output.to_html @@ -76,48 +75,41 @@ module Gitlab def self.full_cache_key(cache_key, pipeline_name) return unless cache_key - pipeline_name ||= :full - ["markdown", *cache_key, pipeline_name] - end - - def self.pipeline_by_name(pipeline_name) - pipeline_name ||= :full - const_get("#{pipeline_name.to_s.camelize}Pipeline") + ["markdown", *cache_key, pipeline_name || :full] end # Provide autoload paths for filters to prevent a circular dependency error - autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter' - autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter' - autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter' - autoload :EmojiFilter, 'gitlab/markdown/emoji_filter' - autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter' - autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' - autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' - autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' - autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter' - autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' - autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' - autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' - autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter' - autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter' - autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter' - 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' + autoload :AutolinkFilter, 'gitlab/markdown/filter/autolink_filter' + autoload :CommitRangeReferenceFilter, 'gitlab/markdown/filter/commit_range_reference_filter' + autoload :CommitReferenceFilter, 'gitlab/markdown/filter/commit_reference_filter' + autoload :EmojiFilter, 'gitlab/markdown/filter/emoji_filter' + autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/filter/external_issue_reference_filter' + autoload :ExternalLinkFilter, 'gitlab/markdown/filter/external_link_filter' + autoload :IssueReferenceFilter, 'gitlab/markdown/filter/issue_reference_filter' + autoload :LabelReferenceFilter, 'gitlab/markdown/filter/label_reference_filter' + autoload :MarkdownFilter, 'gitlab/markdown/filter/markdown_filter' + autoload :MergeRequestReferenceFilter, 'gitlab/markdown/filter/merge_request_reference_filter' + autoload :RedactorFilter, 'gitlab/markdown/filter/redactor_filter' + autoload :ReferenceGathererFilter, 'gitlab/markdown/filter/reference_gatherer_filter' + autoload :RelativeLinkFilter, 'gitlab/markdown/filter/relative_link_filter' + autoload :SanitizationFilter, 'gitlab/markdown/filter/sanitization_filter' + autoload :SnippetReferenceFilter, 'gitlab/markdown/filter/snippet_reference_filter' + autoload :SyntaxHighlightFilter, 'gitlab/markdown/filter/syntax_highlight_filter' + autoload :TableOfContentsFilter, 'gitlab/markdown/filter/table_of_contents_filter' + autoload :TaskListFilter, 'gitlab/markdown/filter/task_list_filter' + autoload :UserReferenceFilter, 'gitlab/markdown/filter/user_reference_filter' + autoload :UploadLinkFilter, 'gitlab/markdown/filter/upload_link_filter' - autoload :AsciidocPipeline, 'gitlab/markdown/asciidoc_pipeline' - autoload :AtomPipeline, 'gitlab/markdown/atom_pipeline' - autoload :CombinedPipeline, 'gitlab/markdown/combined_pipeline' - autoload :DescriptionPipeline, 'gitlab/markdown/description_pipeline' - autoload :EmailPipeline, 'gitlab/markdown/email_pipeline' - autoload :FullPipeline, 'gitlab/markdown/full_pipeline' - autoload :GfmPipeline, 'gitlab/markdown/gfm_pipeline' - autoload :NotePipeline, 'gitlab/markdown/note_pipeline' - autoload :Pipeline, 'gitlab/markdown/pipeline' - autoload :PlainMarkdownPipeline, 'gitlab/markdown/plain_markdown_pipeline' - autoload :PostProcessPipeline, 'gitlab/markdown/post_process_pipeline' - autoload :ReferenceExtractionPipeline, 'gitlab/markdown/reference_extraction_pipeline' - autoload :SingleLinePipeline, 'gitlab/markdown/single_line_pipeline' + autoload :AsciidocPipeline, 'gitlab/markdown/pipeline/asciidoc_pipeline' + autoload :AtomPipeline, 'gitlab/markdown/pipeline/atom_pipeline' + autoload :DescriptionPipeline, 'gitlab/markdown/pipeline/description_pipeline' + autoload :EmailPipeline, 'gitlab/markdown/pipeline/email_pipeline' + autoload :FullPipeline, 'gitlab/markdown/pipeline/full_pipeline' + autoload :GfmPipeline, 'gitlab/markdown/pipeline/gfm_pipeline' + autoload :NotePipeline, 'gitlab/markdown/pipeline/note_pipeline' + autoload :PlainMarkdownPipeline, 'gitlab/markdown/pipeline/plain_markdown_pipeline' + autoload :PostProcessPipeline, 'gitlab/markdown/pipeline/post_process_pipeline' + autoload :ReferenceExtractionPipeline, 'gitlab/markdown/pipeline/reference_extraction_pipeline' + autoload :SingleLinePipeline, 'gitlab/markdown/pipeline/single_line_pipeline' end end diff --git a/lib/gitlab/markdown/asciidoc_pipeline.rb b/lib/gitlab/markdown/asciidoc_pipeline.rb deleted file mode 100644 index 6829b4acb95..00000000000 --- a/lib/gitlab/markdown/asciidoc_pipeline.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class AsciidocPipeline < Pipeline - def self.filters - [ - Gitlab::Markdown::RelativeLinkFilter - ] - end - end - end -end diff --git a/lib/gitlab/markdown/atom_pipeline.rb b/lib/gitlab/markdown/atom_pipeline.rb deleted file mode 100644 index e151f8f5e5a..00000000000 --- a/lib/gitlab/markdown/atom_pipeline.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class AtomPipeline < FullPipeline - def self.transform_context(context) - super(context).merge( - only_path: false, - xhtml: true - ) - end - end - end -end diff --git a/lib/gitlab/markdown/autolink_filter.rb b/lib/gitlab/markdown/autolink_filter.rb deleted file mode 100644 index c37c3bc55bf..00000000000 --- a/lib/gitlab/markdown/autolink_filter.rb +++ /dev/null @@ -1,107 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'uri' - -module Gitlab - module Markdown - # HTML Filter for auto-linking URLs in HTML. - # - # Based on HTML::Pipeline::AutolinkFilter - # - # Context options: - # :autolink - Boolean, skips all processing done by this filter when false - # :link_attr - Hash of attributes for the generated links - # - class AutolinkFilter < HTML::Pipeline::Filter - include ActionView::Helpers::TagHelper - - # Pattern to match text that should be autolinked. - # - # A URI scheme begins with a letter and may contain letters, numbers, - # plus, period and hyphen. Schemes are case-insensitive but we're being - # picky here and allowing only lowercase for autolinks. - # - # See http://en.wikipedia.org/wiki/URI_scheme - # - # The negative lookbehind ensures that users can paste a URL followed by a - # period or comma for punctuation without those characters being included - # in the generated link. - # - # Rubular: http://rubular.com/r/cxjPyZc7Sb - LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?#{commit_range}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit range, and an optional String - # of the external project reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(CommitRange.reference_pattern) do |match| - yield match, $~[:commit_range], $~[:project] - end - end - - def self.referenced_by(node) - project = Project.find(node.attr("data-project")) rescue nil - return unless project - - id = node.attr("data-commit-range") - range = CommitRange.new(id, project) - - return unless range.valid_commits? - - { commit_range: range } - end - - def initialize(*args) - super - - @commit_map = {} - end - - def call - replace_text_nodes_matching(CommitRange.reference_pattern) do |content| - commit_range_link_filter(content) - end - end - - # Replace commit range references in text with links to compare the commit - # ranges. - # - # text - String text to replace references in. - # - # Returns a String with commit range references replaced with links. All - # links have `gfm` and `gfm-commit_range` class names attached for - # styling. - def commit_range_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - range = CommitRange.new(id, project) - - if range.valid_commits? - url = url_for_commit_range(project, range) - - title = range.reference_title - klass = reference_class(:commit_range) - data = data_attribute(project: project.id, commit_range: id) - - project_ref += '@' if project_ref - - %(#{project_ref}#{range}) - else - match - end - end - end - - def url_for_commit_range(project, range) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_compare_url(project.namespace, project, - range.to_param.merge(only_path: context[:only_path])) - end - end - end -end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb deleted file mode 100644 index 8cdbeb1f9cf..00000000000 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ /dev/null @@ -1,88 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces commit references with links. - # - # This filter supports cross-project references. - class CommitReferenceFilter < ReferenceFilter - include CrossProjectReference - - # Public: Find commit references in text - # - # CommitReferenceFilter.references_in(text) do |match, commit, project_ref| - # "#{commit}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit identifier, and an optional - # String of the external project reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Commit.reference_pattern) do |match| - yield match, $~[:commit], $~[:project] - end - end - - def self.referenced_by(node) - project = Project.find(node.attr("data-project")) rescue nil - return unless project - - id = node.attr("data-commit") - commit = commit_from_ref(project, id) - - return unless commit - - { commit: commit } - end - - def call - replace_text_nodes_matching(Commit.reference_pattern) do |content| - commit_link_filter(content) - end - end - - # Replace commit references in text with links to the commit specified. - # - # text - String text to replace references in. - # - # Returns a String with commit references replaced with links. All links - # have `gfm` and `gfm-commit` class names attached for styling. - def commit_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if commit = self.class.commit_from_ref(project, id) - url = url_for_commit(project, commit) - - title = escape_once(commit.link_title) - klass = reference_class(:commit) - data = data_attribute(project: project.id, commit: id) - - project_ref += '@' if project_ref - - %(#{project_ref}#{commit.short_id}) - else - match - end - end - end - - def self.commit_from_ref(project, id) - if project && project.valid_repo? - project.commit(id) - end - end - - def url_for_commit(project, commit) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_commit_url(project.namespace, project, commit, - only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/description_pipeline.rb b/lib/gitlab/markdown/description_pipeline.rb deleted file mode 100644 index 76f6948af8f..00000000000 --- a/lib/gitlab/markdown/description_pipeline.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class DescriptionPipeline < FullPipeline - def self.transform_context(context) - super(context).merge( - # SanitizationFilter - inline_sanitization: true - ) - end - end - end -end diff --git a/lib/gitlab/markdown/email_pipeline.rb b/lib/gitlab/markdown/email_pipeline.rb deleted file mode 100644 index b88cb790270..00000000000 --- a/lib/gitlab/markdown/email_pipeline.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class EmailPipeline < FullPipeline - def self.transform_context(context) - super(context).merge( - only_path: false - ) - end - end - end -end diff --git a/lib/gitlab/markdown/emoji_filter.rb b/lib/gitlab/markdown/emoji_filter.rb deleted file mode 100644 index da10e4d3760..00000000000 --- a/lib/gitlab/markdown/emoji_filter.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'action_controller' -require 'gitlab/markdown' -require 'gitlab_emoji' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that replaces :emoji: with images. - # - # Based on HTML::Pipeline::EmojiFilter - # - # Context options: - # :asset_root - # :asset_host - class EmojiFilter < HTML::Pipeline::Filter - IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set - - def call - search_text_nodes(doc).each do |node| - content = node.to_html - next unless content.include?(':') - next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS) - - html = emoji_image_filter(content) - - next if html == content - - node.replace(html) - end - - doc - end - - # Replace :emoji: with corresponding images. - # - # text - String text to replace :emoji: in. - # - # Returns a String with :emoji: replaced with images. - def emoji_image_filter(text) - text.gsub(emoji_pattern) do |match| - name = $1 - ":#{name}:" - end - end - - private - - def emoji_url(name) - emoji_path = "emoji/#{emoji_filename(name)}" - if context[:asset_host] - # Asset host is specified. - url_to_image(emoji_path) - elsif context[:asset_root] - # Gitlab url is specified - File.join(context[:asset_root], url_to_image(emoji_path)) - else - # All other cases - url_to_image(emoji_path) - end - end - - def url_to_image(image) - ActionController::Base.helpers.url_to_image(image) - end - - # Build a regexp that matches all valid :emoji: names. - def self.emoji_pattern - @emoji_pattern ||= /:(#{Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/ - end - - def emoji_pattern - self.class.emoji_pattern - end - - def emoji_filename(name) - "#{Emoji.emoji_filename(name)}.png" - end - end - end -end diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/gitlab/markdown/external_issue_reference_filter.rb deleted file mode 100644 index 8f86f13976a..00000000000 --- a/lib/gitlab/markdown/external_issue_reference_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces external issue tracker references with links. - # References are ignored if the project doesn't use an external issue - # tracker. - class ExternalIssueReferenceFilter < ReferenceFilter - # Public: Find `JIRA-123` issue references in text - # - # ExternalIssueReferenceFilter.references_in(text) do |match, issue| - # "##{issue}" - # end - # - # text - String text to search. - # - # Yields the String match and the String issue reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(ExternalIssue.reference_pattern) do |match| - yield match, $~[:issue] - end - end - - def call - # Early return if the project isn't using an external tracker - return doc if project.nil? || project.default_issues_tracker? - - replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content| - issue_link_filter(content) - end - end - - # Replace `JIRA-123` issue references in text with links to the referenced - # issue's details page. - # - # text - String text to replace references in. - # - # Returns a String with `JIRA-123` references replaced with links. All - # links have `gfm` and `gfm-issue` class names attached for styling. - def issue_link_filter(text) - project = context[:project] - - self.class.references_in(text) do |match, issue| - url = url_for_issue(issue, project, only_path: context[:only_path]) - - title = escape_once("Issue in #{project.external_issue_tracker.title}") - klass = reference_class(:issue) - data = data_attribute(project: project.id) - - %(#{match}) - end - end - - def url_for_issue(*args) - IssuesHelper.url_for_issue(*args) - end - end - end -end diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb deleted file mode 100644 index 29e51b6ade6..00000000000 --- a/lib/gitlab/markdown/external_link_filter.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML Filter to add a `rel="nofollow"` attribute to external links - # - class ExternalLinkFilter < HTML::Pipeline::Filter - def call - doc.search('a').each do |node| - next unless node.has_attribute?('href') - - link = node.attribute('href').value - - # Skip non-HTTP(S) links - next unless link.start_with?('http') - - # Skip internal links - next if link.start_with?(internal_url) - - node.set_attribute('rel', 'nofollow') - end - - doc - end - - private - - def internal_url - @internal_url ||= Gitlab.config.gitlab.url - end - end - end -end diff --git a/lib/gitlab/markdown/filter/autolink_filter.rb b/lib/gitlab/markdown/filter/autolink_filter.rb new file mode 100644 index 00000000000..c37c3bc55bf --- /dev/null +++ b/lib/gitlab/markdown/filter/autolink_filter.rb @@ -0,0 +1,107 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' +require 'uri' + +module Gitlab + module Markdown + # HTML Filter for auto-linking URLs in HTML. + # + # Based on HTML::Pipeline::AutolinkFilter + # + # Context options: + # :autolink - Boolean, skips all processing done by this filter when false + # :link_attr - Hash of attributes for the generated links + # + class AutolinkFilter < HTML::Pipeline::Filter + include ActionView::Helpers::TagHelper + + # Pattern to match text that should be autolinked. + # + # A URI scheme begins with a letter and may contain letters, numbers, + # plus, period and hyphen. Schemes are case-insensitive but we're being + # picky here and allowing only lowercase for autolinks. + # + # See http://en.wikipedia.org/wiki/URI_scheme + # + # The negative lookbehind ensures that users can paste a URL followed by a + # period or comma for punctuation without those characters being included + # in the generated link. + # + # Rubular: http://rubular.com/r/cxjPyZc7Sb + LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?#{commit_range}" + # end + # + # text - String text to search. + # + # Yields the String match, the String commit range, and an optional String + # of the external project reference. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(CommitRange.reference_pattern) do |match| + yield match, $~[:commit_range], $~[:project] + end + end + + def self.referenced_by(node) + project = Project.find(node.attr("data-project")) rescue nil + return unless project + + id = node.attr("data-commit-range") + range = CommitRange.new(id, project) + + return unless range.valid_commits? + + { commit_range: range } + end + + def initialize(*args) + super + + @commit_map = {} + end + + def call + replace_text_nodes_matching(CommitRange.reference_pattern) do |content| + commit_range_link_filter(content) + end + end + + # Replace commit range references in text with links to compare the commit + # ranges. + # + # text - String text to replace references in. + # + # Returns a String with commit range references replaced with links. All + # links have `gfm` and `gfm-commit_range` class names attached for + # styling. + def commit_range_link_filter(text) + self.class.references_in(text) do |match, id, project_ref| + project = self.project_from_ref(project_ref) + + range = CommitRange.new(id, project) + + if range.valid_commits? + url = url_for_commit_range(project, range) + + title = range.reference_title + klass = reference_class(:commit_range) + data = data_attribute(project: project.id, commit_range: id) + + project_ref += '@' if project_ref + + %(#{project_ref}#{range}) + else + match + end + end + end + + def url_for_commit_range(project, range) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_compare_url(project.namespace, project, + range.to_param.merge(only_path: context[:only_path])) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/commit_reference_filter.rb b/lib/gitlab/markdown/filter/commit_reference_filter.rb new file mode 100644 index 00000000000..8cdbeb1f9cf --- /dev/null +++ b/lib/gitlab/markdown/filter/commit_reference_filter.rb @@ -0,0 +1,88 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces commit references with links. + # + # This filter supports cross-project references. + class CommitReferenceFilter < ReferenceFilter + include CrossProjectReference + + # Public: Find commit references in text + # + # CommitReferenceFilter.references_in(text) do |match, commit, project_ref| + # "#{commit}" + # end + # + # text - String text to search. + # + # Yields the String match, the String commit identifier, and an optional + # String of the external project reference. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(Commit.reference_pattern) do |match| + yield match, $~[:commit], $~[:project] + end + end + + def self.referenced_by(node) + project = Project.find(node.attr("data-project")) rescue nil + return unless project + + id = node.attr("data-commit") + commit = commit_from_ref(project, id) + + return unless commit + + { commit: commit } + end + + def call + replace_text_nodes_matching(Commit.reference_pattern) do |content| + commit_link_filter(content) + end + end + + # Replace commit references in text with links to the commit specified. + # + # text - String text to replace references in. + # + # Returns a String with commit references replaced with links. All links + # have `gfm` and `gfm-commit` class names attached for styling. + def commit_link_filter(text) + self.class.references_in(text) do |match, id, project_ref| + project = self.project_from_ref(project_ref) + + if commit = self.class.commit_from_ref(project, id) + url = url_for_commit(project, commit) + + title = escape_once(commit.link_title) + klass = reference_class(:commit) + data = data_attribute(project: project.id, commit: id) + + project_ref += '@' if project_ref + + %(#{project_ref}#{commit.short_id}) + else + match + end + end + end + + def self.commit_from_ref(project, id) + if project && project.valid_repo? + project.commit(id) + end + end + + def url_for_commit(project, commit) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_commit_url(project.namespace, project, commit, + only_path: context[:only_path]) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/emoji_filter.rb b/lib/gitlab/markdown/filter/emoji_filter.rb new file mode 100644 index 00000000000..da10e4d3760 --- /dev/null +++ b/lib/gitlab/markdown/filter/emoji_filter.rb @@ -0,0 +1,80 @@ +require 'action_controller' +require 'gitlab/markdown' +require 'gitlab_emoji' +require 'html/pipeline/filter' + +module Gitlab + module Markdown + # HTML filter that replaces :emoji: with images. + # + # Based on HTML::Pipeline::EmojiFilter + # + # Context options: + # :asset_root + # :asset_host + class EmojiFilter < HTML::Pipeline::Filter + IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set + + def call + search_text_nodes(doc).each do |node| + content = node.to_html + next unless content.include?(':') + next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS) + + html = emoji_image_filter(content) + + next if html == content + + node.replace(html) + end + + doc + end + + # Replace :emoji: with corresponding images. + # + # text - String text to replace :emoji: in. + # + # Returns a String with :emoji: replaced with images. + def emoji_image_filter(text) + text.gsub(emoji_pattern) do |match| + name = $1 + ":#{name}:" + end + end + + private + + def emoji_url(name) + emoji_path = "emoji/#{emoji_filename(name)}" + if context[:asset_host] + # Asset host is specified. + url_to_image(emoji_path) + elsif context[:asset_root] + # Gitlab url is specified + File.join(context[:asset_root], url_to_image(emoji_path)) + else + # All other cases + url_to_image(emoji_path) + end + end + + def url_to_image(image) + ActionController::Base.helpers.url_to_image(image) + end + + # Build a regexp that matches all valid :emoji: names. + def self.emoji_pattern + @emoji_pattern ||= /:(#{Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/ + end + + def emoji_pattern + self.class.emoji_pattern + end + + def emoji_filename(name) + "#{Emoji.emoji_filename(name)}.png" + end + end + end +end diff --git a/lib/gitlab/markdown/filter/external_issue_reference_filter.rb b/lib/gitlab/markdown/filter/external_issue_reference_filter.rb new file mode 100644 index 00000000000..8f86f13976a --- /dev/null +++ b/lib/gitlab/markdown/filter/external_issue_reference_filter.rb @@ -0,0 +1,63 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces external issue tracker references with links. + # References are ignored if the project doesn't use an external issue + # tracker. + class ExternalIssueReferenceFilter < ReferenceFilter + # Public: Find `JIRA-123` issue references in text + # + # ExternalIssueReferenceFilter.references_in(text) do |match, issue| + # "##{issue}" + # end + # + # text - String text to search. + # + # Yields the String match and the String issue reference. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(ExternalIssue.reference_pattern) do |match| + yield match, $~[:issue] + end + end + + def call + # Early return if the project isn't using an external tracker + return doc if project.nil? || project.default_issues_tracker? + + replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content| + issue_link_filter(content) + end + end + + # Replace `JIRA-123` issue references in text with links to the referenced + # issue's details page. + # + # text - String text to replace references in. + # + # Returns a String with `JIRA-123` references replaced with links. All + # links have `gfm` and `gfm-issue` class names attached for styling. + def issue_link_filter(text) + project = context[:project] + + self.class.references_in(text) do |match, issue| + url = url_for_issue(issue, project, only_path: context[:only_path]) + + title = escape_once("Issue in #{project.external_issue_tracker.title}") + klass = reference_class(:issue) + data = data_attribute(project: project.id) + + %(#{match}) + end + end + + def url_for_issue(*args) + IssuesHelper.url_for_issue(*args) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/external_link_filter.rb b/lib/gitlab/markdown/filter/external_link_filter.rb new file mode 100644 index 00000000000..29e51b6ade6 --- /dev/null +++ b/lib/gitlab/markdown/filter/external_link_filter.rb @@ -0,0 +1,34 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' + +module Gitlab + module Markdown + # HTML Filter to add a `rel="nofollow"` attribute to external links + # + class ExternalLinkFilter < HTML::Pipeline::Filter + def call + doc.search('a').each do |node| + next unless node.has_attribute?('href') + + link = node.attribute('href').value + + # Skip non-HTTP(S) links + next unless link.start_with?('http') + + # Skip internal links + next if link.start_with?(internal_url) + + node.set_attribute('rel', 'nofollow') + end + + doc + end + + private + + def internal_url + @internal_url ||= Gitlab.config.gitlab.url + end + end + end +end diff --git a/lib/gitlab/markdown/filter/issue_reference_filter.rb b/lib/gitlab/markdown/filter/issue_reference_filter.rb new file mode 100644 index 00000000000..1ed69e2f431 --- /dev/null +++ b/lib/gitlab/markdown/filter/issue_reference_filter.rb @@ -0,0 +1,23 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces issue references with links. References to + # issues that do not exist are ignored. + # + # This filter supports cross-project references. + class IssueReferenceFilter < AbstractReferenceFilter + def self.object_class + Issue + end + + def find_object(project, id) + project.get_issue(id) + end + + def url_for_object(issue, project) + IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/label_reference_filter.rb b/lib/gitlab/markdown/filter/label_reference_filter.rb new file mode 100644 index 00000000000..618acb7a578 --- /dev/null +++ b/lib/gitlab/markdown/filter/label_reference_filter.rb @@ -0,0 +1,87 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces label references with links. + class LabelReferenceFilter < ReferenceFilter + # Public: Find label references in text + # + # LabelReferenceFilter.references_in(text) do |match, id, name| + # "#{Label.find(id)}" + # end + # + # text - String text to search. + # + # Yields the String match, an optional Integer label ID, and an optional + # String label name. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(Label.reference_pattern) do |match| + yield match, $~[:label_id].to_i, $~[:label_name] + end + end + + def self.referenced_by(node) + { label: LazyReference.new(Label, node.attr("data-label")) } + end + + def call + replace_text_nodes_matching(Label.reference_pattern) do |content| + label_link_filter(content) + end + end + + # Replace label references in text with links to the label specified. + # + # text - String text to replace references in. + # + # Returns a String with label references replaced with links. All links + # have `gfm` and `gfm-label` class names attached for styling. + def label_link_filter(text) + project = context[:project] + + self.class.references_in(text) do |match, id, name| + params = label_params(id, name) + + if label = project.labels.find_by(params) + url = url_for_label(project, label) + klass = reference_class(:label) + data = data_attribute(project: project.id, label: label.id) + + %(#{render_colored_label(label)}) + else + match + end + end + end + + def url_for_label(project, label) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_issues_path(project.namespace, project, + label_name: label.name, + only_path: context[:only_path]) + end + + def render_colored_label(label) + LabelsHelper.render_colored_label(label) + end + + # Parameters to pass to `Label.find_by` based on the given arguments + # + # id - Integer ID to pass. If present, returns {id: id} + # name - String name to pass. If `id` is absent, finds by name without + # surrounding quotes. + # + # Returns a Hash. + def label_params(id, name) + if name + { name: name.tr('"', '') } + else + { id: id } + end + end + end + end +end diff --git a/lib/gitlab/markdown/filter/markdown_filter.rb b/lib/gitlab/markdown/filter/markdown_filter.rb new file mode 100644 index 00000000000..921e2a0794e --- /dev/null +++ b/lib/gitlab/markdown/filter/markdown_filter.rb @@ -0,0 +1,39 @@ +module Gitlab + module Markdown + class MarkdownFilter < HTML::Pipeline::TextFilter + def initialize(text, context = nil, result = nil) + super text, context, result + @text = @text.gsub "\r", '' + end + + def call + html = self.class.renderer.render(@text) + html.rstrip! + html + end + + private + + def self.redcarpet_options + # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + @redcarpet_options ||= { + fenced_code_blocks: true, + footnotes: true, + lax_spacing: true, + no_intra_emphasis: true, + space_after_headers: true, + strikethrough: true, + superscript: true, + tables: true + }.freeze + end + + def self.renderer + @renderer ||= begin + renderer = Redcarpet::Render::HTML.new + Redcarpet::Markdown.new(renderer, redcarpet_options) + end + end + end + end +end diff --git a/lib/gitlab/markdown/filter/merge_request_reference_filter.rb b/lib/gitlab/markdown/filter/merge_request_reference_filter.rb new file mode 100644 index 00000000000..1f47f03c94e --- /dev/null +++ b/lib/gitlab/markdown/filter/merge_request_reference_filter.rb @@ -0,0 +1,25 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces merge request references with links. References + # to merge requests that do not exist are ignored. + # + # This filter supports cross-project references. + class MergeRequestReferenceFilter < AbstractReferenceFilter + def self.object_class + MergeRequest + end + + def find_object(project, id) + project.merge_requests.find_by(iid: id) + end + + def url_for_object(mr, project) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_merge_request_url(project.namespace, project, mr, + only_path: context[:only_path]) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/redactor_filter.rb b/lib/gitlab/markdown/filter/redactor_filter.rb new file mode 100644 index 00000000000..a1f3a8a8ebf --- /dev/null +++ b/lib/gitlab/markdown/filter/redactor_filter.rb @@ -0,0 +1,40 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' + +module Gitlab + module Markdown + # HTML filter that removes references to records that the current user does + # not have permission to view. + # + # Expected to be run in its own post-processing pipeline. + # + class RedactorFilter < HTML::Pipeline::Filter + def call + doc.css('a.gfm').each do |node| + unless user_can_reference?(node) + node.replace(node.text) + end + end + + doc + end + + private + + def user_can_reference?(node) + if node.has_attribute?('data-reference-filter') + reference_type = node.attr('data-reference-filter') + reference_filter = reference_type.constantize + + reference_filter.user_can_reference?(current_user, node, context) + else + true + end + end + + def current_user + context[:current_user] + end + end + end +end diff --git a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb b/lib/gitlab/markdown/filter/reference_gatherer_filter.rb new file mode 100644 index 00000000000..00f983675e6 --- /dev/null +++ b/lib/gitlab/markdown/filter/reference_gatherer_filter.rb @@ -0,0 +1,63 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' + +module Gitlab + module Markdown + # HTML filter that gathers all referenced records that the current user has + # permission to view. + # + # Expected to be run in its own post-processing pipeline. + # + class ReferenceGathererFilter < HTML::Pipeline::Filter + def initialize(*) + super + + result[:references] ||= Hash.new { |hash, type| hash[type] = [] } + end + + def call + doc.css('a.gfm').each do |node| + gather_references(node) + end + + load_lazy_references unless context[:load_lazy_references] == false + + doc + end + + private + + def gather_references(node) + return unless node.has_attribute?('data-reference-filter') + + reference_type = node.attr('data-reference-filter') + reference_filter = reference_type.constantize + + return if context[:reference_filter] && reference_filter != context[:reference_filter] + + return unless reference_filter.user_can_reference?(current_user, node, context) + + references = reference_filter.referenced_by(node) + return unless references + + references.each do |type, values| + Array.wrap(values).each do |value| + result[:references][type] << value + end + end + end + + # Will load all references of one type using one query. + def load_lazy_references + refs = result[:references] + refs.each do |type, values| + refs[type] = ReferenceFilter::LazyReference.load(values) + end + end + + def current_user + context[:current_user] + end + end + end +end diff --git a/lib/gitlab/markdown/filter/relative_link_filter.rb b/lib/gitlab/markdown/filter/relative_link_filter.rb new file mode 100644 index 00000000000..81f60120fcd --- /dev/null +++ b/lib/gitlab/markdown/filter/relative_link_filter.rb @@ -0,0 +1,157 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' +require 'uri' + +module Gitlab + module Markdown + # HTML filter that "fixes" relative links to files in a repository. + # + # Context options: + # :commit + # :project + # :project_wiki + # :ref + # :requested_path + class RelativeLinkFilter < HTML::Pipeline::Filter + def call + return doc unless linkable_files? + + doc.search('a:not(.gfm)').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 linkable_files? + context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty? + end + + def process_link_attr(html_attr) + return if html_attr.blank? + + uri = URI(html_attr.value) + if uri.relative? && uri.path.present? + html_attr.value = rebuild_relative_uri(uri).to_s + end + rescue URI::Error + # noop + end + + def rebuild_relative_uri(uri) + file_path = relative_file_path(uri.path) + + uri.path = [ + relative_url_root, + context[:project].path_with_namespace, + path_type(file_path), + ref || context[:project].default_branch, # if no ref exists, point to the default branch + file_path + ].compact.join('/').squeeze('/').chomp('/') + + uri + end + + def relative_file_path(path) + nested_path = build_relative_path(path, context[:requested_path]) + file_exists?(nested_path) ? nested_path : path + end + + # Convert a relative path into its correct location based on the currently + # requested path + # + # path - Relative path String + # request_path - Currently-requested path String + # + # Examples: + # + # # File in the same directory as the current path + # build_relative_path("users.md", "doc/api/README.md") + # # => "doc/api/users.md" + # + # # File in the same directory, which is also the current path + # build_relative_path("users.md", "doc/api") + # # => "doc/api/users.md" + # + # # Going up one level to a different directory + # build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md") + # # => "doc/update/7.14-to-8.0.md" + # + # Returns a String + def build_relative_path(path, request_path) + return request_path if path.empty? + return path unless request_path + + parts = request_path.split('/') + parts.pop if path_type(request_path) != 'tree' + + while parts.length > 1 && path.start_with?('../') + parts.pop + path.sub!('../', '') + end + + parts.push(path).join('/') + end + + def file_exists?(path) + return false if path.nil? + repository.blob_at(current_sha, path).present? || + repository.tree(current_sha, path).entries.any? + end + + # Get the type of the given path + # + # path - String path to check + # + # Examples: + # + # path_type('doc/README.md') # => 'blob' + # path_type('doc/logo.png') # => 'raw' + # path_type('doc/api') # => 'tree' + # + # Returns a String + def path_type(path) + unescaped_path = Addressable::URI.unescape(path) + + if tree?(unescaped_path) + 'tree' + elsif image?(unescaped_path) + 'raw' + else + 'blob' + end + end + + def tree?(path) + repository.tree(current_sha, path).entries.any? + end + + def image?(path) + repository.blob_at(current_sha, path).try(:image?) + end + + def current_sha + context[:commit].try(:id) || + ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha + end + + def relative_url_root + Gitlab.config.gitlab.relative_url_root.presence || '/' + end + + def ref + context[:ref] + end + + def repository + context[:project].try(:repository) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/sanitization_filter.rb b/lib/gitlab/markdown/filter/sanitization_filter.rb new file mode 100644 index 00000000000..cf153f30622 --- /dev/null +++ b/lib/gitlab/markdown/filter/sanitization_filter.rb @@ -0,0 +1,99 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' +require 'html/pipeline/sanitization_filter' + +module Gitlab + module Markdown + # Sanitize HTML + # + # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. + class SanitizationFilter < HTML::Pipeline::SanitizationFilter + def whitelist + # Descriptions are more heavily sanitized, allowing only a few elements. + # See http://git.io/vkuAN + if context[:inline_sanitization] + whitelist = LIMITED + whitelist[:elements] -= %w(pre code img ol ul li) + else + whitelist = super + end + + customize_whitelist(whitelist) + + whitelist + end + + private + + def customized?(transformers) + transformers.last.source_location[0] == __FILE__ + end + + def customize_whitelist(whitelist) + # Only push these customizations once + return if customized?(whitelist[:transformers]) + + # Allow code highlighting + whitelist[:attributes]['pre'] = %w(class) + whitelist[:attributes]['span'] = %w(class) + + # Allow table alignment + whitelist[:attributes]['th'] = %w(style) + whitelist[:attributes]['td'] = %w(style) + + # Allow span elements + whitelist[:elements].push('span') + + # Allow any protocol in `a` elements... + whitelist[:protocols].delete('a') + + # ...but then remove links with the `javascript` protocol + whitelist[:transformers].push(remove_javascript_links) + + # Remove `rel` attribute from `a` elements + whitelist[:transformers].push(remove_rel) + + # Remove `class` attribute from non-highlight spans + whitelist[:transformers].push(clean_spans) + + whitelist + end + + def remove_javascript_links + lambda do |env| + node = env[:node] + + return unless node.name == 'a' + return unless node.has_attribute?('href') + + if node['href'].start_with?('javascript', ':javascript') + node.remove_attribute('href') + end + end + end + + def remove_rel + lambda do |env| + if env[:node_name] == 'a' + env[:node].remove_attribute('rel') + end + end + end + + def clean_spans + lambda do |env| + node = env[:node] + + return unless node.name == 'span' + return unless node.has_attribute?('class') + + unless has_ancestor?(node, 'pre') + node.remove_attribute('class') + end + + { node_whitelist: [node] } + end + end + end + end +end diff --git a/lib/gitlab/markdown/filter/snippet_reference_filter.rb b/lib/gitlab/markdown/filter/snippet_reference_filter.rb new file mode 100644 index 00000000000..f7bd07c2a34 --- /dev/null +++ b/lib/gitlab/markdown/filter/snippet_reference_filter.rb @@ -0,0 +1,25 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces snippet references with links. References to + # snippets that do not exist are ignored. + # + # This filter supports cross-project references. + class SnippetReferenceFilter < AbstractReferenceFilter + def self.object_class + Snippet + end + + def find_object(project, id) + project.snippets.find_by(id: id) + end + + def url_for_object(snippet, project) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_snippet_url(project.namespace, project, snippet, + only_path: context[:only_path]) + end + end + end +end diff --git a/lib/gitlab/markdown/filter/syntax_highlight_filter.rb b/lib/gitlab/markdown/filter/syntax_highlight_filter.rb new file mode 100644 index 00000000000..8597e02f0de --- /dev/null +++ b/lib/gitlab/markdown/filter/syntax_highlight_filter.rb @@ -0,0 +1,45 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' +require 'rouge/plugins/redcarpet' + +module Gitlab + module Markdown + # HTML Filter to highlight fenced code blocks + # + class SyntaxHighlightFilter < HTML::Pipeline::Filter + include Rouge::Plugins::Redcarpet + + def call + doc.search('pre > code').each do |node| + highlight_node(node) + end + + doc + end + + def highlight_node(node) + language = node.attr('class') + code = node.text + + begin + highlighted = block_code(code, language) + rescue + # Gracefully handle syntax highlighter bugs/errors to ensure + # users can still access an issue/comment/etc. + highlighted = "
#{code}
" + end + + # Replace the parent `pre` element with the entire highlighted block + node.parent.replace(highlighted) + end + + private + + # Override Rouge::Plugins::Redcarpet#rouge_formatter + def rouge_formatter(lexer) + Rouge::Formatters::HTMLGitlab.new( + cssclass: "code highlight js-syntax-highlight #{lexer.tag}") + end + end + end +end diff --git a/lib/gitlab/markdown/filter/table_of_contents_filter.rb b/lib/gitlab/markdown/filter/table_of_contents_filter.rb new file mode 100644 index 00000000000..bbb3bf7fc8b --- /dev/null +++ b/lib/gitlab/markdown/filter/table_of_contents_filter.rb @@ -0,0 +1,63 @@ +require 'gitlab/markdown' +require 'html/pipeline/filter' + +module Gitlab + module Markdown + # HTML filter that adds an anchor child element to all Headers in a + # document, so that they can be linked to. + # + # Generates the Table of Contents with links to each header. See Results. + # + # Based on HTML::Pipeline::TableOfContentsFilter. + # + # Context options: + # :no_header_anchors - Skips all processing done by this filter. + # + # Results: + # :toc - String containing Table of Contents data as a `ul` element with + # `li` child elements. + class TableOfContentsFilter < HTML::Pipeline::Filter + PUNCTUATION_REGEXP = /[^\p{Word}\- ]/u + + def call + return doc if context[:no_header_anchors] + + result[:toc] = "" + + headers = Hash.new(0) + + doc.css('h1, h2, h3, h4, h5, h6').each do |node| + text = node.text + + id = text.downcase + id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation + id.gsub!(' ', '-') # replace spaces with dash + id.squeeze!('-') # replace multiple dashes with one + + uniq = (headers[id] > 0) ? "-#{headers[id]}" : '' + headers[id] += 1 + + if header_content = node.children.first + href = "#{id}#{uniq}" + push_toc(href, text) + header_content.add_previous_sibling(anchor_tag(href)) + end + end + + result[:toc] = %Q{
    \n#{result[:toc]}
} unless result[:toc].empty? + + doc + end + + private + + def anchor_tag(href) + %Q{} + end + + def push_toc(href, text) + result[:toc] << %Q{
  • #{text}
  • \n} + end + end + end +end diff --git a/lib/gitlab/markdown/filter/task_list_filter.rb b/lib/gitlab/markdown/filter/task_list_filter.rb new file mode 100644 index 00000000000..2f133ae8500 --- /dev/null +++ b/lib/gitlab/markdown/filter/task_list_filter.rb @@ -0,0 +1,24 @@ +require 'gitlab/markdown' +require 'task_list/filter' + +module Gitlab + module Markdown + # Work around a bug in the default TaskList::Filter that adds a `task-list` + # class to every list element, regardless of whether or not it contains a + # task list. + # + # This is a (hopefully) temporary fix, pending a new release of the + # task_list gem. + # + # See https://github.com/github/task_list/pull/60 + class TaskListFilter < TaskList::Filter + def add_css_class(node, *new_class_names) + if new_class_names.include?('task-list') + super if node.children.any? { |c| c['class'] == 'task-list-item' } + else + super + end + end + end + end +end diff --git a/lib/gitlab/markdown/filter/upload_link_filter.rb b/lib/gitlab/markdown/filter/upload_link_filter.rb new file mode 100644 index 00000000000..fbada73ab86 --- /dev/null +++ b/lib/gitlab/markdown/filter/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/markdown/filter/user_reference_filter.rb b/lib/gitlab/markdown/filter/user_reference_filter.rb new file mode 100644 index 00000000000..ab5e1f6fe9e --- /dev/null +++ b/lib/gitlab/markdown/filter/user_reference_filter.rb @@ -0,0 +1,125 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + # HTML filter that replaces user or group references with links. + # + # A special `@all` reference is also supported. + class UserReferenceFilter < ReferenceFilter + # Public: Find `@user` user references in text + # + # UserReferenceFilter.references_in(text) do |match, username| + # "@#{user}" + # end + # + # text - String text to search. + # + # Yields the String match, and the String user name. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(User.reference_pattern) do |match| + yield match, $~[:user] + end + end + + def self.referenced_by(node) + if node.has_attribute?('data-group') + group = Group.find(node.attr('data-group')) rescue nil + return unless group + + { user: group.users } + elsif node.has_attribute?('data-user') + { user: LazyReference.new(User, node.attr('data-user')) } + elsif node.has_attribute?('data-project') + project = Project.find(node.attr('data-project')) rescue nil + return unless project + + { user: project.team.members.flatten } + end + end + + def self.user_can_reference?(user, node, context) + if node.has_attribute?('data-group') + group = Group.find(node.attr('data-group')) rescue nil + Ability.abilities.allowed?(user, :read_group, group) + else + super + end + end + + def call + replace_text_nodes_matching(User.reference_pattern) do |content| + user_link_filter(content) + end + end + + # Replace `@user` user references in text with links to the referenced + # user's profile page. + # + # text - String text to replace references in. + # + # Returns a String with `@user` references replaced with links. All links + # have `gfm` and `gfm-project_member` class names attached for styling. + def user_link_filter(text) + self.class.references_in(text) do |match, username| + if username == 'all' + link_to_all + elsif namespace = Namespace.find_by(path: username) + link_to_namespace(namespace) || match + else + match + end + end + end + + private + + def urls + Gitlab::Application.routes.url_helpers + end + + def link_class + reference_class(:project_member) + end + + def link_to_all + project = context[:project] + url = urls.namespace_project_url(project.namespace, project, + only_path: context[:only_path]) + data = data_attribute(project: project.id) + text = User.reference_prefix + 'all' + + link_tag(url, data, text) + end + + def link_to_namespace(namespace) + if namespace.is_a?(Group) + link_to_group(namespace.path, namespace) + else + link_to_user(namespace.path, namespace) + end + end + + def link_to_group(group, namespace) + url = urls.group_url(group, only_path: context[:only_path]) + data = data_attribute(group: namespace.id) + text = Group.reference_prefix + group + + link_tag(url, data, text) + end + + def link_to_user(user, namespace) + url = urls.user_url(user, only_path: context[:only_path]) + data = data_attribute(user: namespace.owner_id) + text = User.reference_prefix + user + + link_tag(url, data, text) + end + + def link_tag(url, data, text) + %(#{text}) + end + end + end +end diff --git a/lib/gitlab/markdown/full_pipeline.rb b/lib/gitlab/markdown/full_pipeline.rb deleted file mode 100644 index 553e9367c1c..00000000000 --- a/lib/gitlab/markdown/full_pipeline.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline) - - end - end -end diff --git a/lib/gitlab/markdown/gfm_pipeline.rb b/lib/gitlab/markdown/gfm_pipeline.rb deleted file mode 100644 index ca90bd75d77..00000000000 --- a/lib/gitlab/markdown/gfm_pipeline.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class GfmPipeline < Pipeline - def self.filters - @filters ||= [ - Gitlab::Markdown::SyntaxHighlightFilter, - Gitlab::Markdown::SanitizationFilter, - - Gitlab::Markdown::UploadLinkFilter, - Gitlab::Markdown::EmojiFilter, - Gitlab::Markdown::TableOfContentsFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, - - Gitlab::Markdown::UserReferenceFilter, - Gitlab::Markdown::IssueReferenceFilter, - Gitlab::Markdown::ExternalIssueReferenceFilter, - Gitlab::Markdown::MergeRequestReferenceFilter, - Gitlab::Markdown::SnippetReferenceFilter, - Gitlab::Markdown::CommitRangeReferenceFilter, - Gitlab::Markdown::CommitReferenceFilter, - Gitlab::Markdown::LabelReferenceFilter, - - Gitlab::Markdown::TaskListFilter - ] - end - - def self.transform_context(context) - context.merge( - only_path: true, - - # EmojiFilter - asset_host: Gitlab::Application.config.asset_host, - asset_root: Gitlab.config.gitlab.base_url - ) - end - end - end -end diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb deleted file mode 100644 index 1ed69e2f431..00000000000 --- a/lib/gitlab/markdown/issue_reference_filter.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces issue references with links. References to - # issues that do not exist are ignored. - # - # This filter supports cross-project references. - class IssueReferenceFilter < AbstractReferenceFilter - def self.object_class - Issue - end - - def find_object(project, id) - project.get_issue(id) - end - - def url_for_object(issue, project) - IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb deleted file mode 100644 index 618acb7a578..00000000000 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces label references with links. - class LabelReferenceFilter < ReferenceFilter - # Public: Find label references in text - # - # LabelReferenceFilter.references_in(text) do |match, id, name| - # "#{Label.find(id)}" - # end - # - # text - String text to search. - # - # Yields the String match, an optional Integer label ID, and an optional - # String label name. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Label.reference_pattern) do |match| - yield match, $~[:label_id].to_i, $~[:label_name] - end - end - - def self.referenced_by(node) - { label: LazyReference.new(Label, node.attr("data-label")) } - end - - def call - replace_text_nodes_matching(Label.reference_pattern) do |content| - label_link_filter(content) - end - end - - # Replace label references in text with links to the label specified. - # - # text - String text to replace references in. - # - # Returns a String with label references replaced with links. All links - # have `gfm` and `gfm-label` class names attached for styling. - def label_link_filter(text) - project = context[:project] - - self.class.references_in(text) do |match, id, name| - params = label_params(id, name) - - if label = project.labels.find_by(params) - url = url_for_label(project, label) - klass = reference_class(:label) - data = data_attribute(project: project.id, label: label.id) - - %(#{render_colored_label(label)}) - else - match - end - end - end - - def url_for_label(project, label) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_issues_path(project.namespace, project, - label_name: label.name, - only_path: context[:only_path]) - end - - def render_colored_label(label) - LabelsHelper.render_colored_label(label) - end - - # Parameters to pass to `Label.find_by` based on the given arguments - # - # id - Integer ID to pass. If present, returns {id: id} - # name - String name to pass. If `id` is absent, finds by name without - # surrounding quotes. - # - # Returns a Hash. - def label_params(id, name) - if name - { name: name.tr('"', '') } - else - { id: id } - end - end - end - end -end diff --git a/lib/gitlab/markdown/markdown_filter.rb b/lib/gitlab/markdown/markdown_filter.rb deleted file mode 100644 index 921e2a0794e..00000000000 --- a/lib/gitlab/markdown/markdown_filter.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Gitlab - module Markdown - class MarkdownFilter < HTML::Pipeline::TextFilter - def initialize(text, context = nil, result = nil) - super text, context, result - @text = @text.gsub "\r", '' - end - - def call - html = self.class.renderer.render(@text) - html.rstrip! - html - end - - private - - def self.redcarpet_options - # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use - @redcarpet_options ||= { - fenced_code_blocks: true, - footnotes: true, - lax_spacing: true, - no_intra_emphasis: true, - space_after_headers: true, - strikethrough: true, - superscript: true, - tables: true - }.freeze - end - - def self.renderer - @renderer ||= begin - renderer = Redcarpet::Render::HTML.new - Redcarpet::Markdown.new(renderer, redcarpet_options) - end - end - end - end -end diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb deleted file mode 100644 index 1f47f03c94e..00000000000 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces merge request references with links. References - # to merge requests that do not exist are ignored. - # - # This filter supports cross-project references. - class MergeRequestReferenceFilter < AbstractReferenceFilter - def self.object_class - MergeRequest - end - - def find_object(project, id) - project.merge_requests.find_by(iid: id) - end - - def url_for_object(mr, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_merge_request_url(project.namespace, project, mr, - only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/note_pipeline.rb b/lib/gitlab/markdown/note_pipeline.rb deleted file mode 100644 index a8bf5f42d8e..00000000000 --- a/lib/gitlab/markdown/note_pipeline.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class NotePipeline < FullPipeline - def self.transform_context(context) - super(context).merge( - # TableOfContentsFilter - no_header_anchors: true - ) - end - end - end -end diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb index 3c0b676a073..d683756f95a 100644 --- a/lib/gitlab/markdown/pipeline.rb +++ b/lib/gitlab/markdown/pipeline.rb @@ -3,6 +3,11 @@ require 'gitlab/markdown' module Gitlab module Markdown class Pipeline + def self.[](name) + name ||= :full + Markdown.const_get("#{name.to_s.camelize}Pipeline") + end + def self.filters [] end diff --git a/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb b/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb new file mode 100644 index 00000000000..6829b4acb95 --- /dev/null +++ b/lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class AsciidocPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::RelativeLinkFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/atom_pipeline.rb b/lib/gitlab/markdown/pipeline/atom_pipeline.rb new file mode 100644 index 00000000000..e151f8f5e5a --- /dev/null +++ b/lib/gitlab/markdown/pipeline/atom_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class AtomPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + only_path: false, + xhtml: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/description_pipeline.rb b/lib/gitlab/markdown/pipeline/description_pipeline.rb new file mode 100644 index 00000000000..76f6948af8f --- /dev/null +++ b/lib/gitlab/markdown/pipeline/description_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class DescriptionPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + # SanitizationFilter + inline_sanitization: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/email_pipeline.rb b/lib/gitlab/markdown/pipeline/email_pipeline.rb new file mode 100644 index 00000000000..b88cb790270 --- /dev/null +++ b/lib/gitlab/markdown/pipeline/email_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class EmailPipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + only_path: false + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/full_pipeline.rb b/lib/gitlab/markdown/pipeline/full_pipeline.rb new file mode 100644 index 00000000000..553e9367c1c --- /dev/null +++ b/lib/gitlab/markdown/pipeline/full_pipeline.rb @@ -0,0 +1,9 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline) + + end + end +end diff --git a/lib/gitlab/markdown/pipeline/gfm_pipeline.rb b/lib/gitlab/markdown/pipeline/gfm_pipeline.rb new file mode 100644 index 00000000000..ca90bd75d77 --- /dev/null +++ b/lib/gitlab/markdown/pipeline/gfm_pipeline.rb @@ -0,0 +1,41 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class GfmPipeline < Pipeline + def self.filters + @filters ||= [ + Gitlab::Markdown::SyntaxHighlightFilter, + Gitlab::Markdown::SanitizationFilter, + + Gitlab::Markdown::UploadLinkFilter, + Gitlab::Markdown::EmojiFilter, + Gitlab::Markdown::TableOfContentsFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, + + Gitlab::Markdown::UserReferenceFilter, + Gitlab::Markdown::IssueReferenceFilter, + Gitlab::Markdown::ExternalIssueReferenceFilter, + Gitlab::Markdown::MergeRequestReferenceFilter, + Gitlab::Markdown::SnippetReferenceFilter, + Gitlab::Markdown::CommitRangeReferenceFilter, + Gitlab::Markdown::CommitReferenceFilter, + Gitlab::Markdown::LabelReferenceFilter, + + Gitlab::Markdown::TaskListFilter + ] + end + + def self.transform_context(context) + context.merge( + only_path: true, + + # EmojiFilter + asset_host: Gitlab::Application.config.asset_host, + asset_root: Gitlab.config.gitlab.base_url + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/note_pipeline.rb b/lib/gitlab/markdown/pipeline/note_pipeline.rb new file mode 100644 index 00000000000..a8bf5f42d8e --- /dev/null +++ b/lib/gitlab/markdown/pipeline/note_pipeline.rb @@ -0,0 +1,14 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class NotePipeline < FullPipeline + def self.transform_context(context) + super(context).merge( + # TableOfContentsFilter + no_header_anchors: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb b/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb new file mode 100644 index 00000000000..0abb93f8a03 --- /dev/null +++ b/lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class PlainMarkdownPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::MarkdownFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/post_process_pipeline.rb b/lib/gitlab/markdown/pipeline/post_process_pipeline.rb new file mode 100644 index 00000000000..60cc32f490e --- /dev/null +++ b/lib/gitlab/markdown/pipeline/post_process_pipeline.rb @@ -0,0 +1,20 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class PostProcessPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::RedactorFilter + ] + end + + def self.transform_context(context) + context.merge( + post_process: true + ) + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb b/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb new file mode 100644 index 00000000000..a89ab462bac --- /dev/null +++ b/lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb @@ -0,0 +1,13 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class ReferenceExtractionPipeline < Pipeline + def self.filters + [ + Gitlab::Markdown::ReferenceGathererFilter + ] + end + end + end +end diff --git a/lib/gitlab/markdown/pipeline/single_line_pipeline.rb b/lib/gitlab/markdown/pipeline/single_line_pipeline.rb new file mode 100644 index 00000000000..2f24927b879 --- /dev/null +++ b/lib/gitlab/markdown/pipeline/single_line_pipeline.rb @@ -0,0 +1,9 @@ +require 'gitlab/markdown' + +module Gitlab + module Markdown + class SingleLinePipeline < GfmPipeline + + end + end +end diff --git a/lib/gitlab/markdown/plain_markdown_pipeline.rb b/lib/gitlab/markdown/plain_markdown_pipeline.rb deleted file mode 100644 index 0abb93f8a03..00000000000 --- a/lib/gitlab/markdown/plain_markdown_pipeline.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class PlainMarkdownPipeline < Pipeline - def self.filters - [ - Gitlab::Markdown::MarkdownFilter - ] - end - end - end -end diff --git a/lib/gitlab/markdown/post_process_pipeline.rb b/lib/gitlab/markdown/post_process_pipeline.rb deleted file mode 100644 index 60cc32f490e..00000000000 --- a/lib/gitlab/markdown/post_process_pipeline.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class PostProcessPipeline < Pipeline - def self.filters - [ - Gitlab::Markdown::RelativeLinkFilter, - Gitlab::Markdown::RedactorFilter - ] - end - - def self.transform_context(context) - context.merge( - post_process: true - ) - end - end - end -end diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb deleted file mode 100644 index a1f3a8a8ebf..00000000000 --- a/lib/gitlab/markdown/redactor_filter.rb +++ /dev/null @@ -1,40 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that removes references to records that the current user does - # not have permission to view. - # - # Expected to be run in its own post-processing pipeline. - # - class RedactorFilter < HTML::Pipeline::Filter - def call - doc.css('a.gfm').each do |node| - unless user_can_reference?(node) - node.replace(node.text) - end - end - - doc - end - - private - - def user_can_reference?(node) - if node.has_attribute?('data-reference-filter') - reference_type = node.attr('data-reference-filter') - reference_filter = reference_type.constantize - - reference_filter.user_can_reference?(current_user, node, context) - else - true - end - end - - def current_user - context[:current_user] - end - end - end -end diff --git a/lib/gitlab/markdown/reference_extraction_pipeline.rb b/lib/gitlab/markdown/reference_extraction_pipeline.rb deleted file mode 100644 index a89ab462bac..00000000000 --- a/lib/gitlab/markdown/reference_extraction_pipeline.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class ReferenceExtractionPipeline < Pipeline - def self.filters - [ - Gitlab::Markdown::ReferenceGathererFilter - ] - end - end - end -end diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index a4c560f578c..22fe4440be3 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -29,6 +29,10 @@ module Gitlab end end + def self.[](name) + Markdown.const_get("#{name.to_s.camelize}ReferenceFilter") + end + def self.user_can_reference?(user, node, context) if node.has_attribute?('data-project') project_id = node.attr('data-project').to_i diff --git a/lib/gitlab/markdown/reference_gatherer_filter.rb b/lib/gitlab/markdown/reference_gatherer_filter.rb deleted file mode 100644 index 00f983675e6..00000000000 --- a/lib/gitlab/markdown/reference_gatherer_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that gathers all referenced records that the current user has - # permission to view. - # - # Expected to be run in its own post-processing pipeline. - # - class ReferenceGathererFilter < HTML::Pipeline::Filter - def initialize(*) - super - - result[:references] ||= Hash.new { |hash, type| hash[type] = [] } - end - - def call - doc.css('a.gfm').each do |node| - gather_references(node) - end - - load_lazy_references unless context[:load_lazy_references] == false - - doc - end - - private - - def gather_references(node) - return unless node.has_attribute?('data-reference-filter') - - reference_type = node.attr('data-reference-filter') - reference_filter = reference_type.constantize - - return if context[:reference_filter] && reference_filter != context[:reference_filter] - - return unless reference_filter.user_can_reference?(current_user, node, context) - - references = reference_filter.referenced_by(node) - return unless references - - references.each do |type, values| - Array.wrap(values).each do |value| - result[:references][type] << value - end - end - end - - # Will load all references of one type using one query. - def load_lazy_references - refs = result[:references] - refs.each do |type, values| - refs[type] = ReferenceFilter::LazyReference.load(values) - end - end - - def current_user - context[:current_user] - end - end - end -end diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb deleted file mode 100644 index 81f60120fcd..00000000000 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ /dev/null @@ -1,157 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'uri' - -module Gitlab - module Markdown - # HTML filter that "fixes" relative links to files in a repository. - # - # Context options: - # :commit - # :project - # :project_wiki - # :ref - # :requested_path - class RelativeLinkFilter < HTML::Pipeline::Filter - def call - return doc unless linkable_files? - - doc.search('a:not(.gfm)').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 linkable_files? - context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty? - end - - def process_link_attr(html_attr) - return if html_attr.blank? - - uri = URI(html_attr.value) - if uri.relative? && uri.path.present? - html_attr.value = rebuild_relative_uri(uri).to_s - end - rescue URI::Error - # noop - end - - def rebuild_relative_uri(uri) - file_path = relative_file_path(uri.path) - - uri.path = [ - relative_url_root, - context[:project].path_with_namespace, - path_type(file_path), - ref || context[:project].default_branch, # if no ref exists, point to the default branch - file_path - ].compact.join('/').squeeze('/').chomp('/') - - uri - end - - def relative_file_path(path) - nested_path = build_relative_path(path, context[:requested_path]) - file_exists?(nested_path) ? nested_path : path - end - - # Convert a relative path into its correct location based on the currently - # requested path - # - # path - Relative path String - # request_path - Currently-requested path String - # - # Examples: - # - # # File in the same directory as the current path - # build_relative_path("users.md", "doc/api/README.md") - # # => "doc/api/users.md" - # - # # File in the same directory, which is also the current path - # build_relative_path("users.md", "doc/api") - # # => "doc/api/users.md" - # - # # Going up one level to a different directory - # build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md") - # # => "doc/update/7.14-to-8.0.md" - # - # Returns a String - def build_relative_path(path, request_path) - return request_path if path.empty? - return path unless request_path - - parts = request_path.split('/') - parts.pop if path_type(request_path) != 'tree' - - while parts.length > 1 && path.start_with?('../') - parts.pop - path.sub!('../', '') - end - - parts.push(path).join('/') - end - - def file_exists?(path) - return false if path.nil? - repository.blob_at(current_sha, path).present? || - repository.tree(current_sha, path).entries.any? - end - - # Get the type of the given path - # - # path - String path to check - # - # Examples: - # - # path_type('doc/README.md') # => 'blob' - # path_type('doc/logo.png') # => 'raw' - # path_type('doc/api') # => 'tree' - # - # Returns a String - def path_type(path) - unescaped_path = Addressable::URI.unescape(path) - - if tree?(unescaped_path) - 'tree' - elsif image?(unescaped_path) - 'raw' - else - 'blob' - end - end - - def tree?(path) - repository.tree(current_sha, path).entries.any? - end - - def image?(path) - repository.blob_at(current_sha, path).try(:image?) - end - - def current_sha - context[:commit].try(:id) || - ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha - end - - def relative_url_root - Gitlab.config.gitlab.relative_url_root.presence || '/' - end - - def ref - context[:ref] - end - - def repository - context[:project].try(:repository) - end - end - end -end diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb deleted file mode 100644 index cf153f30622..00000000000 --- a/lib/gitlab/markdown/sanitization_filter.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'html/pipeline/sanitization_filter' - -module Gitlab - module Markdown - # Sanitize HTML - # - # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. - class SanitizationFilter < HTML::Pipeline::SanitizationFilter - def whitelist - # Descriptions are more heavily sanitized, allowing only a few elements. - # See http://git.io/vkuAN - if context[:inline_sanitization] - whitelist = LIMITED - whitelist[:elements] -= %w(pre code img ol ul li) - else - whitelist = super - end - - customize_whitelist(whitelist) - - whitelist - end - - private - - def customized?(transformers) - transformers.last.source_location[0] == __FILE__ - end - - def customize_whitelist(whitelist) - # Only push these customizations once - return if customized?(whitelist[:transformers]) - - # Allow code highlighting - whitelist[:attributes]['pre'] = %w(class) - whitelist[:attributes]['span'] = %w(class) - - # Allow table alignment - whitelist[:attributes]['th'] = %w(style) - whitelist[:attributes]['td'] = %w(style) - - # Allow span elements - whitelist[:elements].push('span') - - # Allow any protocol in `a` elements... - whitelist[:protocols].delete('a') - - # ...but then remove links with the `javascript` protocol - whitelist[:transformers].push(remove_javascript_links) - - # Remove `rel` attribute from `a` elements - whitelist[:transformers].push(remove_rel) - - # Remove `class` attribute from non-highlight spans - whitelist[:transformers].push(clean_spans) - - whitelist - end - - def remove_javascript_links - lambda do |env| - node = env[:node] - - return unless node.name == 'a' - return unless node.has_attribute?('href') - - if node['href'].start_with?('javascript', ':javascript') - node.remove_attribute('href') - end - end - end - - def remove_rel - lambda do |env| - if env[:node_name] == 'a' - env[:node].remove_attribute('rel') - end - end - end - - def clean_spans - lambda do |env| - node = env[:node] - - return unless node.name == 'span' - return unless node.has_attribute?('class') - - unless has_ancestor?(node, 'pre') - node.remove_attribute('class') - end - - { node_whitelist: [node] } - end - end - end - end -end diff --git a/lib/gitlab/markdown/single_line_pipeline.rb b/lib/gitlab/markdown/single_line_pipeline.rb deleted file mode 100644 index 2f24927b879..00000000000 --- a/lib/gitlab/markdown/single_line_pipeline.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - class SingleLinePipeline < GfmPipeline - - end - end -end diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb deleted file mode 100644 index f7bd07c2a34..00000000000 --- a/lib/gitlab/markdown/snippet_reference_filter.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces snippet references with links. References to - # snippets that do not exist are ignored. - # - # This filter supports cross-project references. - class SnippetReferenceFilter < AbstractReferenceFilter - def self.object_class - Snippet - end - - def find_object(project, id) - project.snippets.find_by(id: id) - end - - def url_for_object(snippet, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_snippet_url(project.namespace, project, snippet, - only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/syntax_highlight_filter.rb b/lib/gitlab/markdown/syntax_highlight_filter.rb deleted file mode 100644 index 8597e02f0de..00000000000 --- a/lib/gitlab/markdown/syntax_highlight_filter.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'rouge/plugins/redcarpet' - -module Gitlab - module Markdown - # HTML Filter to highlight fenced code blocks - # - class SyntaxHighlightFilter < HTML::Pipeline::Filter - include Rouge::Plugins::Redcarpet - - def call - doc.search('pre > code').each do |node| - highlight_node(node) - end - - doc - end - - def highlight_node(node) - language = node.attr('class') - code = node.text - - begin - highlighted = block_code(code, language) - rescue - # Gracefully handle syntax highlighter bugs/errors to ensure - # users can still access an issue/comment/etc. - highlighted = "
    #{code}
    " - end - - # Replace the parent `pre` element with the entire highlighted block - node.parent.replace(highlighted) - end - - private - - # Override Rouge::Plugins::Redcarpet#rouge_formatter - def rouge_formatter(lexer) - Rouge::Formatters::HTMLGitlab.new( - cssclass: "code highlight js-syntax-highlight #{lexer.tag}") - end - end - end -end diff --git a/lib/gitlab/markdown/table_of_contents_filter.rb b/lib/gitlab/markdown/table_of_contents_filter.rb deleted file mode 100644 index bbb3bf7fc8b..00000000000 --- a/lib/gitlab/markdown/table_of_contents_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that adds an anchor child element to all Headers in a - # document, so that they can be linked to. - # - # Generates the Table of Contents with links to each header. See Results. - # - # Based on HTML::Pipeline::TableOfContentsFilter. - # - # Context options: - # :no_header_anchors - Skips all processing done by this filter. - # - # Results: - # :toc - String containing Table of Contents data as a `ul` element with - # `li` child elements. - class TableOfContentsFilter < HTML::Pipeline::Filter - PUNCTUATION_REGEXP = /[^\p{Word}\- ]/u - - def call - return doc if context[:no_header_anchors] - - result[:toc] = "" - - headers = Hash.new(0) - - doc.css('h1, h2, h3, h4, h5, h6').each do |node| - text = node.text - - id = text.downcase - id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation - id.gsub!(' ', '-') # replace spaces with dash - id.squeeze!('-') # replace multiple dashes with one - - uniq = (headers[id] > 0) ? "-#{headers[id]}" : '' - headers[id] += 1 - - if header_content = node.children.first - href = "#{id}#{uniq}" - push_toc(href, text) - header_content.add_previous_sibling(anchor_tag(href)) - end - end - - result[:toc] = %Q{
      \n#{result[:toc]}
    } unless result[:toc].empty? - - doc - end - - private - - def anchor_tag(href) - %Q{} - end - - def push_toc(href, text) - result[:toc] << %Q{
  • #{text}
  • \n} - end - end - end -end diff --git a/lib/gitlab/markdown/task_list_filter.rb b/lib/gitlab/markdown/task_list_filter.rb deleted file mode 100644 index 2f133ae8500..00000000000 --- a/lib/gitlab/markdown/task_list_filter.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'gitlab/markdown' -require 'task_list/filter' - -module Gitlab - module Markdown - # Work around a bug in the default TaskList::Filter that adds a `task-list` - # class to every list element, regardless of whether or not it contains a - # task list. - # - # This is a (hopefully) temporary fix, pending a new release of the - # task_list gem. - # - # See https://github.com/github/task_list/pull/60 - class TaskListFilter < TaskList::Filter - def add_css_class(node, *new_class_names) - if new_class_names.include?('task-list') - super if node.children.any? { |c| c['class'] == 'task-list-item' } - else - super - end - end - end - end -end diff --git a/lib/gitlab/markdown/upload_link_filter.rb b/lib/gitlab/markdown/upload_link_filter.rb deleted file mode 100644 index fbada73ab86..00000000000 --- a/lib/gitlab/markdown/upload_link_filter.rb +++ /dev/null @@ -1,47 +0,0 @@ -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/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb deleted file mode 100644 index ab5e1f6fe9e..00000000000 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ /dev/null @@ -1,125 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces user or group references with links. - # - # A special `@all` reference is also supported. - class UserReferenceFilter < ReferenceFilter - # Public: Find `@user` user references in text - # - # UserReferenceFilter.references_in(text) do |match, username| - # "@#{user}" - # end - # - # text - String text to search. - # - # Yields the String match, and the String user name. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(User.reference_pattern) do |match| - yield match, $~[:user] - end - end - - def self.referenced_by(node) - if node.has_attribute?('data-group') - group = Group.find(node.attr('data-group')) rescue nil - return unless group - - { user: group.users } - elsif node.has_attribute?('data-user') - { user: LazyReference.new(User, node.attr('data-user')) } - elsif node.has_attribute?('data-project') - project = Project.find(node.attr('data-project')) rescue nil - return unless project - - { user: project.team.members.flatten } - end - end - - def self.user_can_reference?(user, node, context) - if node.has_attribute?('data-group') - group = Group.find(node.attr('data-group')) rescue nil - Ability.abilities.allowed?(user, :read_group, group) - else - super - end - end - - def call - replace_text_nodes_matching(User.reference_pattern) do |content| - user_link_filter(content) - end - end - - # Replace `@user` user references in text with links to the referenced - # user's profile page. - # - # text - String text to replace references in. - # - # Returns a String with `@user` references replaced with links. All links - # have `gfm` and `gfm-project_member` class names attached for styling. - def user_link_filter(text) - self.class.references_in(text) do |match, username| - if username == 'all' - link_to_all - elsif namespace = Namespace.find_by(path: username) - link_to_namespace(namespace) || match - else - match - end - end - end - - private - - def urls - Gitlab::Application.routes.url_helpers - end - - def link_class - reference_class(:project_member) - end - - def link_to_all - project = context[:project] - url = urls.namespace_project_url(project.namespace, project, - only_path: context[:only_path]) - data = data_attribute(project: project.id) - text = User.reference_prefix + 'all' - - link_tag(url, data, text) - end - - def link_to_namespace(namespace) - if namespace.is_a?(Group) - link_to_group(namespace.path, namespace) - else - link_to_user(namespace.path, namespace) - end - end - - def link_to_group(group, namespace) - url = urls.group_url(group, only_path: context[:only_path]) - data = data_attribute(group: namespace.id) - text = Group.reference_prefix + group - - link_tag(url, data, text) - end - - def link_to_user(user, namespace) - url = urls.user_url(user, only_path: context[:only_path]) - data = data_attribute(user: namespace.owner_id) - text = User.reference_prefix + user - - link_tag(url, data, text) - end - - def link_tag(url, data, text) - %(#{text}) - end - end - end -end diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index e5cafebdbc0..e83167fa7d7 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -33,8 +33,7 @@ module Gitlab # # Returns the results Array for the requested filter type def pipeline_result(filter_type) - klass = "#{filter_type.to_s.camelize}ReferenceFilter" - filter = Gitlab::Markdown.const_get(klass) + filter = Gitlab::Markdown::ReferenceFilter[filter_type] context = { pipeline: :reference_extraction, diff --git a/spec/lib/gitlab/markdown/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/autolink_filter_spec.rb deleted file mode 100644 index 26332ba5217..00000000000 --- a/spec/lib/gitlab/markdown/autolink_filter_spec.rb +++ /dev/null @@ -1,114 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe AutolinkFilter do - include FilterSpecHelper - - let(:link) { 'http://about.gitlab.com/' } - - it 'does nothing when :autolink is false' do - exp = act = link - expect(filter(act, autolink: false).to_html).to eq exp - end - - it 'does nothing with non-link text' do - exp = act = 'This text contains no links to autolink' - expect(filter(act).to_html).to eq exp - end - - context 'Rinku schemes' do - it 'autolinks http' do - doc = filter("See #{link}") - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'autolinks https' do - link = 'https://google.com/' - doc = filter("See #{link}") - - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'autolinks ftp' do - link = 'ftp://ftp.us.debian.org/debian/' - doc = filter("See #{link}") - - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'autolinks short URLs' do - link = 'http://localhost:3000/' - doc = filter("See #{link}") - - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'accepts link_attr options' do - doc = filter("See #{link}", link_attr: { class: 'custom' }) - - expect(doc.at_css('a')['class']).to eq 'custom' - end - - described_class::IGNORE_PARENTS.each do |elem| - it "ignores valid links contained inside '#{elem}' element" do - exp = act = "<#{elem}>See #{link}" - expect(filter(act).to_html).to eq exp - end - end - end - - context 'other schemes' do - let(:link) { 'foo://bar.baz/' } - - it 'autolinks smb' do - link = 'smb:///Volumes/shared/foo.pdf' - doc = filter("See #{link}") - - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'autolinks irc' do - link = 'irc://irc.freenode.net/git' - doc = filter("See #{link}") - - expect(doc.at_css('a').text).to eq link - expect(doc.at_css('a')['href']).to eq link - end - - it 'does not include trailing punctuation' do - doc = filter("See #{link}.") - expect(doc.at_css('a').text).to eq link - - doc = filter("See #{link}, ok?") - expect(doc.at_css('a').text).to eq link - - doc = filter("See #{link}...") - expect(doc.at_css('a').text).to eq link - end - - it 'does not include trailing HTML entities' do - doc = filter("See <<<#{link}>>>") - - expect(doc.at_css('a')['href']).to eq link - expect(doc.text).to eq "See <<<#{link}>>>" - end - - it 'accepts link_attr options' do - doc = filter("See #{link}", link_attr: { class: 'custom' }) - expect(doc.at_css('a')['class']).to eq 'custom' - end - - described_class::IGNORE_PARENTS.each do |elem| - it "ignores valid links contained inside '#{elem}' element" do - exp = act = "<#{elem}>See #{link}" - expect(filter(act).to_html).to eq exp - end - end - end - end -end diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb deleted file mode 100644 index e5b8d723fe5..00000000000 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ /dev/null @@ -1,145 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe CommitRangeReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:project, :public) } - let(:commit1) { project.commit } - let(:commit2) { project.commit("HEAD~2") } - - let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") } - let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Commit Range #{range.to_reference}" - expect(filter(act).to_html).to eq exp - end - end - - context 'internal reference' do - let(:reference) { range.to_reference } - let(:reference2) { range2.to_reference } - - it 'links to a valid two-dot reference' do - doc = filter("See #{reference2}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) - end - - it 'links to a valid three-dot reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) - end - - it 'links to a valid short ID' do - reference = "#{commit1.short_id}...#{commit2.id}" - reference2 = "#{commit1.id}...#{commit2.short_id}" - - exp = commit1.short_id + '...' + commit2.short_id - - expect(filter("See #{reference}").css('a').first.text).to eq exp - expect(filter("See #{reference2}").css('a').first.text).to eq exp - end - - it 'links with adjacent text' do - doc = filter("See (#{reference}.)") - - exp = Regexp.escape(range.to_s) - expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) - end - - it 'ignores invalid commit IDs' do - exp = act = "See #{commit1.id.reverse}...#{commit2.id}" - - expect(project).to receive(:valid_repo?).and_return(true) - expect(project.repository).to receive(:commit).with(commit1.id.reverse) - expect(filter(act).to_html).to eq exp - end - - it 'includes a title attribute' do - doc = filter("See #{reference}") - expect(doc.css('a').first.attr('title')).to eq range.reference_title - end - - it 'includes default classes' do - doc = filter("See #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' - end - - it 'includes a data-project attribute' do - doc = filter("See #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-project') - expect(link.attr('data-project')).to eq project.id.to_s - end - - it 'includes a data-commit-range attribute' do - doc = filter("See #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-commit-range') - expect(link.attr('data-commit-range')).to eq range.to_reference - end - - it 'supports an :only_path option' do - doc = filter("See #{reference}", only_path: true) - link = doc.css('a').first.attr('href') - - expect(link).not_to match %r(https?://) - expect(link).to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id, only_path: true) - end - - it 'adds to the results hash' do - result = reference_pipeline_result("See #{reference}") - expect(result[:references][:commit_range]).not_to be_empty - end - end - - context 'cross-project reference' do - let(:namespace) { create(:namespace, name: 'cross-reference') } - let(:project2) { create(:project, :public, namespace: namespace) } - let(:reference) { range.to_reference(project) } - - before do - range.project = project2 - end - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) - end - - it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") - - exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") - expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) - end - - it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp - - exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp - end - - it 'adds to the results hash' do - result = reference_pipeline_result("See #{reference}") - expect(result[:references][:commit_range]).not_to be_empty - end - end - end -end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb deleted file mode 100644 index d080efbf3d4..00000000000 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ /dev/null @@ -1,135 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe CommitReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:project, :public) } - let(:commit) { project.commit } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Commit #{commit.id}" - expect(filter(act).to_html).to eq exp - end - end - - context 'internal reference' do - let(:reference) { commit.id } - - # Let's test a variety of commit SHA sizes just to be paranoid - [6, 8, 12, 18, 20, 32, 40].each do |size| - it "links to a valid reference of #{size} characters" do - doc = filter("See #{reference[0...size]}") - - expect(doc.css('a').first.text).to eq commit.short_id - expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_commit_url(project.namespace, project, reference) - end - end - - it 'always uses the short ID as the link text' do - doc = filter("See #{commit.id}") - expect(doc.text).to eq "See #{commit.short_id}" - - doc = filter("See #{commit.id[0...6]}") - expect(doc.text).to eq "See #{commit.short_id}" - end - - it 'links with adjacent text' do - doc = filter("See (#{reference}.)") - expect(doc.to_html).to match(/\(#{commit.short_id}<\/a>\.\)/) - end - - it 'ignores invalid commit IDs' do - invalid = invalidate_reference(reference) - exp = act = "See #{invalid}" - - expect(project).to receive(:valid_repo?).and_return(true) - expect(project.repository).to receive(:commit).with(invalid) - expect(filter(act).to_html).to eq exp - end - - it 'includes a title attribute' do - doc = filter("See #{reference}") - expect(doc.css('a').first.attr('title')).to eq commit.link_title - end - - it 'escapes the title attribute' do - allow_any_instance_of(Commit).to receive(:title).and_return(%{">whatever#{exp}@#{commit.short_id}<\/a>\.\)/) - end - - it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp - end - - it 'adds to the results hash' do - result = reference_pipeline_result("See #{reference}") - expect(result[:references][:commit]).not_to be_empty - end - end - end -end diff --git a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb index 8d4f9e403a6..f86d52b9d0a 100644 --- a/spec/lib/gitlab/markdown/cross_project_reference_spec.rb +++ b/spec/lib/gitlab/markdown/cross_project_reference_spec.rb @@ -1,35 +1,33 @@ require 'spec_helper' -module Gitlab::Markdown - describe CrossProjectReference do - include described_class +describe Gitlab::Markdown::CrossProjectReference do + include described_class - describe '#project_from_ref' do - context 'when no project was referenced' do - it 'returns the project from context' do - project = double + describe '#project_from_ref' do + context 'when no project was referenced' do + it 'returns the project from context' do + project = double - allow(self).to receive(:context).and_return({ project: project }) + allow(self).to receive(:context).and_return({ project: project }) - expect(project_from_ref(nil)).to eq project - end + expect(project_from_ref(nil)).to eq project end + end - context 'when referenced project does not exist' do - it 'returns nil' do - expect(project_from_ref('invalid/reference')).to be_nil - end + context 'when referenced project does not exist' do + it 'returns nil' do + expect(project_from_ref('invalid/reference')).to be_nil end + end - context 'when referenced project exists' do - it 'returns the referenced project' do - project2 = double('referenced project') + context 'when referenced project exists' do + it 'returns the referenced project' do + project2 = double('referenced project') - expect(Project).to receive(:find_with_namespace). - with('cross/reference').and_return(project2) + expect(Project).to receive(:find_with_namespace). + with('cross/reference').and_return(project2) - expect(project_from_ref('cross/reference')).to eq project2 - end + expect(project_from_ref('cross/reference')).to eq project2 end end end diff --git a/spec/lib/gitlab/markdown/emoji_filter_spec.rb b/spec/lib/gitlab/markdown/emoji_filter_spec.rb deleted file mode 100644 index 11efd9bb4cd..00000000000 --- a/spec/lib/gitlab/markdown/emoji_filter_spec.rb +++ /dev/null @@ -1,95 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe EmojiFilter do - include FilterSpecHelper - - before do - ActionController::Base.asset_host = 'https://foo.com' - end - - it 'replaces supported emoji' do - doc = filter('

    :heart:

    ') - expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/2764.png' - end - - it 'ignores unsupported emoji' do - exp = act = '

    :foo:

    ' - doc = filter(act) - expect(doc.to_html).to match Regexp.escape(exp) - end - - it 'correctly encodes the URL' do - doc = filter('

    :+1:

    ') - expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/1F44D.png' - end - - it 'matches at the start of a string' do - doc = filter(':+1:') - expect(doc.css('img').size).to eq 1 - end - - it 'matches at the end of a string' do - doc = filter('This gets a :-1:') - expect(doc.css('img').size).to eq 1 - end - - it 'matches with adjacent text' do - doc = filter('+1 (:+1:)') - expect(doc.css('img').size).to eq 1 - end - - it 'matches multiple emoji in a row' do - doc = filter(':see_no_evil::hear_no_evil::speak_no_evil:') - expect(doc.css('img').size).to eq 3 - end - - it 'has a title attribute' do - doc = filter(':-1:') - expect(doc.css('img').first.attr('title')).to eq ':-1:' - end - - it 'has an alt attribute' do - doc = filter(':-1:') - expect(doc.css('img').first.attr('alt')).to eq ':-1:' - end - - it 'has an align attribute' do - doc = filter(':8ball:') - expect(doc.css('img').first.attr('align')).to eq 'absmiddle' - end - - it 'has an emoji class' do - doc = filter(':cat:') - expect(doc.css('img').first.attr('class')).to eq 'emoji' - end - - it 'has height and width attributes' do - doc = filter(':dog:') - img = doc.css('img').first - - expect(img.attr('width')).to eq '20' - expect(img.attr('height')).to eq '20' - end - - it 'keeps whitespace intact' do - doc = filter('This deserves a :+1:, big time.') - - expect(doc.to_html).to match(/^This deserves a , big time\.\z/) - end - - it 'uses a custom asset_root context' do - root = Gitlab.config.gitlab.url + 'gitlab/root' - - doc = filter(':smile:', asset_root: root) - expect(doc.css('img').first.attr('src')).to start_with(root) - end - - it 'uses a custom asset_host context' do - ActionController::Base.asset_host = 'https://cdn.example.com' - - doc = filter(':frowning:', asset_host: 'https://this-is-ignored-i-guess?') - expect(doc.css('img').first.attr('src')).to start_with('https://cdn.example.com') - end - end -end diff --git a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb deleted file mode 100644 index d8c2970b6bd..00000000000 --- a/spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe ExternalIssueReferenceFilter do - include FilterSpecHelper - - def helper - IssuesHelper - end - - let(:project) { create(:jira_project) } - - context 'JIRA issue references' do - let(:issue) { ExternalIssue.new('JIRA-123', project) } - let(:reference) { issue.to_reference } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Issue #{reference}" - expect(filter(act).to_html).to eq exp - end - end - - it 'ignores valid references when using default tracker' do - expect(project).to receive(:default_issues_tracker?).and_return(true) - - exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp - end - - it 'links to a valid reference' do - doc = filter("Issue #{reference}") - expect(doc.css('a').first.attr('href')) - .to eq helper.url_for_issue(reference, project) - end - - it 'links to the external tracker' do - doc = filter("Issue #{reference}") - link = doc.css('a').first.attr('href') - - expect(link).to eq "http://jira.example/browse/#{reference}" - end - - it 'links with adjacent text' do - doc = filter("Issue (#{reference}.)") - expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) - end - - it 'includes a title attribute' do - doc = filter("Issue #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Issue in JIRA tracker" - end - - it 'escapes the title attribute' do - allow(project.external_issue_tracker).to receive(:title). - and_return(%{">
    whateverIgnore Me) - expect(filter(act).to_html).to eq exp - end - - it 'ignores non-HTTP(S) links' do - exp = act = %q(IRC) - expect(filter(act).to_html).to eq exp - end - - it 'skips internal links' do - internal = Gitlab.config.gitlab.url - exp = act = %Q(Login) - expect(filter(act).to_html).to eq exp - end - - it 'adds rel="nofollow" to external links' do - act = %q(Google) - doc = filter(act) - - expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to eq 'nofollow' - end - end -end diff --git a/spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb b/spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb new file mode 100644 index 00000000000..837bdb56f74 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb @@ -0,0 +1,112 @@ +require 'spec_helper' + +describe Gitlab::Markdown::AutolinkFilter do + include FilterSpecHelper + + let(:link) { 'http://about.gitlab.com/' } + + it 'does nothing when :autolink is false' do + exp = act = link + expect(filter(act, autolink: false).to_html).to eq exp + end + + it 'does nothing with non-link text' do + exp = act = 'This text contains no links to autolink' + expect(filter(act).to_html).to eq exp + end + + context 'Rinku schemes' do + it 'autolinks http' do + doc = filter("See #{link}") + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'autolinks https' do + link = 'https://google.com/' + doc = filter("See #{link}") + + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'autolinks ftp' do + link = 'ftp://ftp.us.debian.org/debian/' + doc = filter("See #{link}") + + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'autolinks short URLs' do + link = 'http://localhost:3000/' + doc = filter("See #{link}") + + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'accepts link_attr options' do + doc = filter("See #{link}", link_attr: { class: 'custom' }) + + expect(doc.at_css('a')['class']).to eq 'custom' + end + + described_class::IGNORE_PARENTS.each do |elem| + it "ignores valid links contained inside '#{elem}' element" do + exp = act = "<#{elem}>See #{link}" + expect(filter(act).to_html).to eq exp + end + end + end + + context 'other schemes' do + let(:link) { 'foo://bar.baz/' } + + it 'autolinks smb' do + link = 'smb:///Volumes/shared/foo.pdf' + doc = filter("See #{link}") + + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'autolinks irc' do + link = 'irc://irc.freenode.net/git' + doc = filter("See #{link}") + + expect(doc.at_css('a').text).to eq link + expect(doc.at_css('a')['href']).to eq link + end + + it 'does not include trailing punctuation' do + doc = filter("See #{link}.") + expect(doc.at_css('a').text).to eq link + + doc = filter("See #{link}, ok?") + expect(doc.at_css('a').text).to eq link + + doc = filter("See #{link}...") + expect(doc.at_css('a').text).to eq link + end + + it 'does not include trailing HTML entities' do + doc = filter("See <<<#{link}>>>") + + expect(doc.at_css('a')['href']).to eq link + expect(doc.text).to eq "See <<<#{link}>>>" + end + + it 'accepts link_attr options' do + doc = filter("See #{link}", link_attr: { class: 'custom' }) + expect(doc.at_css('a')['class']).to eq 'custom' + end + + described_class::IGNORE_PARENTS.each do |elem| + it "ignores valid links contained inside '#{elem}' element" do + exp = act = "<#{elem}>See #{link}" + expect(filter(act).to_html).to eq exp + end + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb new file mode 100644 index 00000000000..05def97a9e8 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb @@ -0,0 +1,143 @@ +require 'spec_helper' + +describe Gitlab::Markdown::CommitRangeReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:project, :public) } + let(:commit1) { project.commit } + let(:commit2) { project.commit("HEAD~2") } + + let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") } + let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Commit Range #{range.to_reference}" + expect(filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + let(:reference) { range.to_reference } + let(:reference2) { range2.to_reference } + + it 'links to a valid two-dot reference' do + doc = filter("See #{reference2}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) + end + + it 'links to a valid three-dot reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) + end + + it 'links to a valid short ID' do + reference = "#{commit1.short_id}...#{commit2.id}" + reference2 = "#{commit1.id}...#{commit2.short_id}" + + exp = commit1.short_id + '...' + commit2.short_id + + expect(filter("See #{reference}").css('a').first.text).to eq exp + expect(filter("See #{reference2}").css('a').first.text).to eq exp + end + + it 'links with adjacent text' do + doc = filter("See (#{reference}.)") + + exp = Regexp.escape(range.to_s) + expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs' do + exp = act = "See #{commit1.id.reverse}...#{commit2.id}" + + expect(project).to receive(:valid_repo?).and_return(true) + expect(project.repository).to receive(:commit).with(commit1.id.reverse) + expect(filter(act).to_html).to eq exp + end + + it 'includes a title attribute' do + doc = filter("See #{reference}") + expect(doc.css('a').first.attr('title')).to eq range.reference_title + end + + it 'includes default classes' do + doc = filter("See #{reference}") + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' + end + + it 'includes a data-project attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-commit-range attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-commit-range') + expect(link.attr('data-commit-range')).to eq range.to_reference + end + + it 'supports an :only_path option' do + doc = filter("See #{reference}", only_path: true) + link = doc.css('a').first.attr('href') + + expect(link).not_to match %r(https?://) + expect(link).to eq urls.namespace_project_compare_url(project.namespace, project, from: commit1.id, to: commit2.id, only_path: true) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit_range]).not_to be_empty + end + end + + context 'cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:reference) { range.to_reference(project) } + + before do + range.project = project2 + end + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + + exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") + expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" + expect(filter(act).to_html).to eq exp + + exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit_range]).not_to be_empty + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb new file mode 100644 index 00000000000..0a892a12b93 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb @@ -0,0 +1,133 @@ +require 'spec_helper' + +describe Gitlab::Markdown::CommitReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:project, :public) } + let(:commit) { project.commit } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Commit #{commit.id}" + expect(filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + let(:reference) { commit.id } + + # Let's test a variety of commit SHA sizes just to be paranoid + [6, 8, 12, 18, 20, 32, 40].each do |size| + it "links to a valid reference of #{size} characters" do + doc = filter("See #{reference[0...size]}") + + expect(doc.css('a').first.text).to eq commit.short_id + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_commit_url(project.namespace, project, reference) + end + end + + it 'always uses the short ID as the link text' do + doc = filter("See #{commit.id}") + expect(doc.text).to eq "See #{commit.short_id}" + + doc = filter("See #{commit.id[0...6]}") + expect(doc.text).to eq "See #{commit.short_id}" + end + + it 'links with adjacent text' do + doc = filter("See (#{reference}.)") + expect(doc.to_html).to match(/\(#{commit.short_id}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs' do + invalid = invalidate_reference(reference) + exp = act = "See #{invalid}" + + expect(project).to receive(:valid_repo?).and_return(true) + expect(project.repository).to receive(:commit).with(invalid) + expect(filter(act).to_html).to eq exp + end + + it 'includes a title attribute' do + doc = filter("See #{reference}") + expect(doc.css('a').first.attr('title')).to eq commit.link_title + end + + it 'escapes the title attribute' do + allow_any_instance_of(Commit).to receive(:title).and_return(%{">whatever#{exp}@#{commit.short_id}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Committed #{invalidate_reference(reference)}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit]).not_to be_empty + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb b/spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb new file mode 100644 index 00000000000..e0c5622e85d --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe Gitlab::Markdown::EmojiFilter do + include FilterSpecHelper + + before do + @original_asset_host = ActionController::Base.asset_host + ActionController::Base.asset_host = 'https://foo.com' + end + + after do + ActionController::Base.asset_host = @original_asset_host + end + + it 'replaces supported emoji' do + doc = filter('

    :heart:

    ') + expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/2764.png' + end + + it 'ignores unsupported emoji' do + exp = act = '

    :foo:

    ' + doc = filter(act) + expect(doc.to_html).to match Regexp.escape(exp) + end + + it 'correctly encodes the URL' do + doc = filter('

    :+1:

    ') + expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/1F44D.png' + end + + it 'matches at the start of a string' do + doc = filter(':+1:') + expect(doc.css('img').size).to eq 1 + end + + it 'matches at the end of a string' do + doc = filter('This gets a :-1:') + expect(doc.css('img').size).to eq 1 + end + + it 'matches with adjacent text' do + doc = filter('+1 (:+1:)') + expect(doc.css('img').size).to eq 1 + end + + it 'matches multiple emoji in a row' do + doc = filter(':see_no_evil::hear_no_evil::speak_no_evil:') + expect(doc.css('img').size).to eq 3 + end + + it 'has a title attribute' do + doc = filter(':-1:') + expect(doc.css('img').first.attr('title')).to eq ':-1:' + end + + it 'has an alt attribute' do + doc = filter(':-1:') + expect(doc.css('img').first.attr('alt')).to eq ':-1:' + end + + it 'has an align attribute' do + doc = filter(':8ball:') + expect(doc.css('img').first.attr('align')).to eq 'absmiddle' + end + + it 'has an emoji class' do + doc = filter(':cat:') + expect(doc.css('img').first.attr('class')).to eq 'emoji' + end + + it 'has height and width attributes' do + doc = filter(':dog:') + img = doc.css('img').first + + expect(img.attr('width')).to eq '20' + expect(img.attr('height')).to eq '20' + end + + it 'keeps whitespace intact' do + doc = filter('This deserves a :+1:, big time.') + + expect(doc.to_html).to match(/^This deserves a , big time\.\z/) + end + + it 'uses a custom asset_root context' do + root = Gitlab.config.gitlab.url + 'gitlab/root' + + doc = filter(':smile:', asset_root: root) + expect(doc.css('img').first.attr('src')).to start_with(root) + end + + it 'uses a custom asset_host context' do + ActionController::Base.asset_host = 'https://cdn.example.com' + + doc = filter(':frowning:', asset_host: 'https://this-is-ignored-i-guess?') + expect(doc.css('img').first.attr('src')).to start_with('https://cdn.example.com') + end +end diff --git a/spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb new file mode 100644 index 00000000000..216fcb66cef --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper' + +describe Gitlab::Markdown::ExternalIssueReferenceFilter do + include FilterSpecHelper + + def helper + IssuesHelper + end + + let(:project) { create(:jira_project) } + + context 'JIRA issue references' do + let(:issue) { ExternalIssue.new('JIRA-123', project) } + let(:reference) { issue.to_reference } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Issue #{reference}" + expect(filter(act).to_html).to eq exp + end + end + + it 'ignores valid references when using default tracker' do + expect(project).to receive(:default_issues_tracker?).and_return(true) + + exp = act = "Issue #{reference}" + expect(filter(act).to_html).to eq exp + end + + it 'links to a valid reference' do + doc = filter("Issue #{reference}") + expect(doc.css('a').first.attr('href')) + .to eq helper.url_for_issue(reference, project) + end + + it 'links to the external tracker' do + doc = filter("Issue #{reference}") + link = doc.css('a').first.attr('href') + + expect(link).to eq "http://jira.example/browse/#{reference}" + end + + it 'links with adjacent text' do + doc = filter("Issue (#{reference}.)") + expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) + end + + it 'includes a title attribute' do + doc = filter("Issue #{reference}") + expect(doc.css('a').first.attr('title')).to eq "Issue in JIRA tracker" + end + + it 'escapes the title attribute' do + allow(project.external_issue_tracker).to receive(:title). + and_return(%{">
    whateverIgnore Me) + expect(filter(act).to_html).to eq exp + end + + it 'ignores non-HTTP(S) links' do + exp = act = %q(IRC) + expect(filter(act).to_html).to eq exp + end + + it 'skips internal links' do + internal = Gitlab.config.gitlab.url + exp = act = %Q(Login) + expect(filter(act).to_html).to eq exp + end + + it 'adds rel="nofollow" to external links' do + act = %q(Google) + doc = filter(act) + + expect(doc.at_css('a')).to have_attribute('rel') + expect(doc.at_css('a')['rel']).to eq 'nofollow' + end +end diff --git a/spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb new file mode 100644 index 00000000000..66eac4f2e34 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb @@ -0,0 +1,137 @@ +require 'spec_helper' + +describe Gitlab::Markdown::IssueReferenceFilter do + include FilterSpecHelper + + def helper + IssuesHelper + end + + let(:project) { create(:empty_project, :public) } + let(:issue) { create(:issue, project: project) } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Issue #{issue.to_reference}" + expect(filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + let(:reference) { issue.to_reference } + + it 'ignores valid references when using non-default tracker' do + expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) + + exp = act = "Issue #{reference}" + expect(filter(act).to_html).to eq exp + end + + it 'links to a valid reference' do + doc = filter("Fixed #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq helper.url_for_issue(issue.iid, project) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid issue IDs' do + invalid = invalidate_reference(reference) + exp = act = "Fixed #{invalid}" + + expect(filter(act).to_html).to eq exp + end + + it 'includes a title attribute' do + doc = filter("Issue #{reference}") + expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" + end + + it 'escapes the title attribute' do + issue.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid issue IDs on the referenced project' do + exp = act = "Fixed #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb new file mode 100644 index 00000000000..5403e811aa3 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb @@ -0,0 +1,142 @@ +require 'spec_helper' +require 'html/pipeline' + +describe Gitlab::Markdown::LabelReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:empty_project, :public) } + let(:label) { create(:label, project: project) } + let(:reference) { label.to_reference } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Label #{reference}" + expect(filter(act).to_html).to eq exp + end + end + + it 'includes default classes' do + doc = filter("Label #{reference}") + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' + end + + it 'includes a data-project attribute' do + doc = filter("Label #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-label attribute' do + doc = filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-label') + expect(link.attr('data-label')).to eq label.id.to_s + end + + it 'supports an :only_path context' do + doc = filter("Label #{reference}", only_path: true) + link = doc.css('a').first.attr('href') + + expect(link).not_to match %r(https?://) + expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Label #{reference}") + expect(result[:references][:label]).to eq [label] + end + + describe 'label span element' do + it 'includes default classes' do + doc = filter("Label #{reference}") + expect(doc.css('a span').first.attr('class')).to eq 'label color-label' + end + + it 'includes a style attribute' do + doc = filter("Label #{reference}") + expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/) + end + end + + context 'Integer-based references' do + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + end + + it 'links with adjacent text' do + doc = filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(#{label.name}\.\))) + end + + it 'ignores invalid label IDs' do + exp = act = "Label #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + end + + context 'String-based single-word references' do + let(:label) { create(:label, name: 'gfm', project: project) } + let(:reference) { "#{Label.reference_prefix}#{label.name}" } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + expect(doc.text).to eq 'See gfm' + end + + it 'links with adjacent text' do + doc = filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(#{label.name}\.\))) + end + + it 'ignores invalid label names' do + exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" + + expect(filter(act).to_html).to eq exp + end + end + + context 'String-based multi-word references in quotes' do + let(:label) { create(:label, name: 'gfm references', project: project) } + let(:reference) { label.to_reference(:name) } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + expect(doc.text).to eq 'See gfm references' + end + + it 'links with adjacent text' do + doc = filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(#{label.name}\.\))) + end + + it 'ignores invalid label names' do + exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") + + expect(filter(act).to_html).to eq exp + end + end + + describe 'edge cases' do + it 'gracefully handles non-references matching the pattern' do + exp = act = '(format nil "~0f" 3.0) ; 3.0' + expect(filter(act).to_html).to eq exp + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb new file mode 100644 index 00000000000..a48b9634cc4 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +describe Gitlab::Markdown::MergeRequestReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:project, :public) } + let(:merge) { create(:merge_request, source_project: project) } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Merge #{merge.to_reference}" + expect(filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + let(:reference) { merge.to_reference } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_merge_request_url(project.namespace, project, merge) + end + + it 'links with adjacent text' do + doc = filter("Merge (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid merge IDs' do + exp = act = "Merge #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'includes a title attribute' do + doc = filter("Merge #{reference}") + expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" + end + + it 'escapes the title attribute' do + merge.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid merge IDs on the referenced project' do + exp = act = "Merge #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Merge #{reference}") + expect(result[:references][:merge_request]).to eq [merge] + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb new file mode 100644 index 00000000000..c177d6950a0 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' + +describe Gitlab::Markdown::RedactorFilter do + include ActionView::Helpers::UrlHelper + include FilterSpecHelper + + it 'ignores non-GFM links' do + html = %(See Google) + doc = filter(html, current_user: double) + + expect(doc.css('a').length).to eq 1 + end + + def reference_link(data) + link_to('text', '', class: 'gfm', data: data) + end + + context 'with data-project' do + it 'removes unpermitted Project references' do + user = create(:user) + project = create(:empty_project) + + link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 0 + end + + it 'allows permitted Project references' do + user = create(:user) + project = create(:empty_project) + project.team << [user, :master] + + link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 1 + end + + it 'handles invalid Project references' do + link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + + expect { filter(link) }.not_to raise_error + end + end + + context "for user references" do + + context 'with data-group' do + it 'removes unpermitted Group references' do + user = create(:user) + group = create(:group) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 0 + end + + it 'allows permitted Group references' do + user = create(:user) + group = create(:group) + group.add_developer(user) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link, current_user: user) + + expect(doc.css('a').length).to eq 1 + end + + it 'handles invalid Group references' do + link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + + expect { filter(link) }.not_to raise_error + end + end + + context 'with data-user' do + it 'allows any User reference' do + user = create(:user) + + link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + doc = filter(link) + + expect(doc.css('a').length).to eq 1 + end + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb new file mode 100644 index 00000000000..0884f53abe5 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe Gitlab::Markdown::ReferenceGathererFilter do + include ActionView::Helpers::UrlHelper + include FilterSpecHelper + + def reference_link(data) + link_to('text', '', class: 'gfm', data: data) + end + + context "for issue references" do + + context 'with data-project' do + it 'removes unpermitted Project references' do + user = create(:user) + project = create(:empty_project) + issue = create(:issue, project: project) + + link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:issue]).to be_empty + end + + it 'allows permitted Project references' do + user = create(:user) + project = create(:empty_project) + issue = create(:issue, project: project) + project.team << [user, :master] + + link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:issue]).to eq([issue]) + end + + it 'handles invalid Project references' do + link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + + expect { pipeline_result(link) }.not_to raise_error + end + end + end + + context "for user references" do + + context 'with data-group' do + it 'removes unpermitted Group references' do + user = create(:user) + group = create(:group) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:user]).to be_empty + end + + it 'allows permitted Group references' do + user = create(:user) + group = create(:group) + group.add_developer(user) + + link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link, current_user: user) + + expect(result[:references][:user]).to eq([user]) + end + + it 'handles invalid Group references' do + link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + + expect { pipeline_result(link) }.not_to raise_error + end + end + + context 'with data-user' do + it 'allows any User reference' do + user = create(:user) + + link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + result = pipeline_result(link) + + expect(result[:references][:user]).to eq([user]) + end + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb new file mode 100644 index 00000000000..3d978505564 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb @@ -0,0 +1,147 @@ +# encoding: UTF-8 + +require 'spec_helper' + +describe Gitlab::Markdown::RelativeLinkFilter do + def filter(doc, contexts = {}) + contexts.reverse_merge!({ + commit: project.commit, + project: project, + project_wiki: project_wiki, + ref: ref, + requested_path: requested_path + }) + + described_class.call(doc, contexts) + end + + def image(path) + %() + end + + def link(path) + %(#{path}) + end + + let(:project) { create(:project) } + let(:project_path) { project.path_with_namespace } + let(:ref) { 'markdown' } + let(:project_wiki) { nil } + let(:requested_path) { '/' } + + 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 + + shared_examples :relative_to_requested do + it 'rebuilds URL relative to the requested path' do + doc = filter(link('users.md')) + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/users.md" + end + end + + context 'with a project_wiki' do + let(:project_wiki) { double('ProjectWiki') } + include_examples :preserve_unchanged + end + + context 'without a repository' do + let(:project) { create(:empty_project) } + include_examples :preserve_unchanged + end + + context 'with an empty repository' do + let(:project) { create(:project_empty_repo) } + include_examples :preserve_unchanged + 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 file in the repo' do + doc = filter(link('doc/api/README.md')) + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" + end + + it 'rebuilds relative URL for a file in the repo up one directory' do + relative_link = link('../api/README.md') + doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md') + + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" + end + + it 'rebuilds relative URL for a file in the repo up multiple directories' do + relative_link = link('../../../api/README.md') + doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md') + + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" + end + + it 'rebuilds relative URL for a file in the repo with an anchor' do + doc = filter(link('README.md#section')) + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/README.md#section" + end + + it 'rebuilds relative URL for a directory in the repo' do + doc = filter(link('doc/api/')) + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/tree/#{ref}/doc/api" + end + + it 'rebuilds relative URL for an image in the repo' do + doc = filter(link('files/images/logo-black.png')) + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png" + end + + it 'does not modify relative URL with an anchor only' do + doc = filter(link('#section-1')) + expect(doc.at_css('a')['href']).to eq '#section-1' + 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 = 'files/images/한글.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 '/raw/' + end + + context 'when requested path is a file in the repo' do + let(:requested_path) { 'doc/api/README.md' } + include_examples :relative_to_requested + end + + context 'when requested path is a directory in the repo' do + let(:requested_path) { 'doc/api' } + include_examples :relative_to_requested + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb new file mode 100644 index 00000000000..33d850d0dd3 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb @@ -0,0 +1,197 @@ +require 'spec_helper' + +describe Gitlab::Markdown::SanitizationFilter do + include FilterSpecHelper + + describe 'default whitelist' do + it 'sanitizes tags that are not whitelisted' do + act = %q{ and no blinks} + exp = 'no inputs and no blinks' + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes tag attributes' do + act = %q{Text} + exp = %q{Text} + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes javascript in attributes' do + act = %q(Text) + exp = 'Text' + expect(filter(act).to_html).to eq exp + end + + it 'allows whitelisted HTML tags from the user' do + exp = act = "
    \n
    Term
    \n
    Definition
    \n
    " + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes `class` attribute on any element' do + act = %q{Strong} + expect(filter(act).to_html).to eq %q{Strong} + end + + it 'sanitizes `id` attribute on any element' do + act = %q{Emphasis} + expect(filter(act).to_html).to eq %q{Emphasis} + end + end + + describe 'custom whitelist' do + it 'customizes the whitelist only once' do + instance = described_class.new('Foo') + 3.times { instance.whitelist } + + expect(instance.whitelist[:transformers].size).to eq 5 + end + + it 'allows syntax highlighting' do + exp = act = %q{
    def
    } + expect(filter(act).to_html).to eq exp + end + + it 'sanitizes `class` attribute from non-highlight spans' do + act = %q{def} + expect(filter(act).to_html).to eq %q{def} + end + + it 'allows `style` attribute on table elements' do + html = <<-HTML.strip_heredoc + + + +
    Head
    Body
    + HTML + + doc = filter(html) + + expect(doc.at_css('th')['style']).to eq 'text-align: center' + expect(doc.at_css('td')['style']).to eq 'text-align: right' + end + + it 'allows `span` elements' do + exp = act = %q{Hello} + expect(filter(act).to_html).to eq exp + end + + it 'removes `rel` attribute from `a` elements' do + act = %q{Link} + exp = %q{Link} + + expect(filter(act).to_html).to eq exp + end + + # Adapted from the Sanitize test suite: http://git.io/vczrM + protocols = { + 'protocol-based JS injection: simple, no spaces' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces before' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces after' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: simple, spaces before and after' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: preceding colon' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: UTF-8 encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long UTF-8 encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long UTF-8 encoding without semicolons' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: hex encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: long hex encoding' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: hex encoding without semicolons' => { + input: 'foo', + output: 'foo' + }, + + 'protocol-based JS injection: null char' => { + input: "foo", + output: '' + }, + + 'protocol-based JS injection: spaces and entities' => { + input: 'foo', + output: 'foo' + }, + } + + protocols.each do |name, data| + it "handles #{name}" do + doc = filter(data[:input]) + + expect(doc.to_html).to eq data[:output] + end + end + + it 'allows non-standard anchor schemes' do + exp = %q{IRC} + act = filter(exp) + + expect(act.to_html).to eq exp + end + + it 'allows relative links' do + exp = %q{foo/bar.md} + act = filter(exp) + + expect(act.to_html).to eq exp + end + end + + context 'when inline_sanitization is true' do + it 'uses a stricter whitelist' do + doc = filter('

    Description

    ', inline_sanitization: true) + expect(doc.to_html.strip).to eq 'Description' + end + + %w(pre code img ol ul li).each do |elem| + it "removes '#{elem}' elements" do + act = "<#{elem}>Description" + expect(filter(act, inline_sanitization: true).to_html.strip). + to eq 'Description' + end + end + + %w(b i strong em a ins del sup sub p).each do |elem| + it "still allows '#{elem}' elements" do + exp = act = "<#{elem}>Description" + expect(filter(act, inline_sanitization: true).to_html).to eq exp + end + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb new file mode 100644 index 00000000000..671aac49598 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb @@ -0,0 +1,116 @@ +require 'spec_helper' + +describe Gitlab::Markdown::SnippetReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:empty_project, :public) } + let(:snippet) { create(:project_snippet, project: project) } + let(:reference) { snippet.to_reference } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Snippet #{reference}" + expect(filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_snippet_url(project.namespace, project, snippet) + end + + it 'links with adjacent text' do + doc = filter("Snippet (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid snippet IDs' do + exp = act = "Snippet #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'includes a title attribute' do + doc = filter("Snippet #{reference}") + expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" + end + + it 'escapes the title attribute' do + snippet.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) + end + + it 'ignores invalid snippet IDs on the referenced project' do + exp = act = "See #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Snippet #{reference}") + expect(result[:references][:snippet]).to eq [snippet] + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb b/spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb new file mode 100644 index 00000000000..2df81282fa2 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Gitlab::Markdown::SyntaxHighlightFilter do + include FilterSpecHelper + + it 'highlights valid code blocks' do + result = filter('
    def fun end')
    +    expect(result.to_html).to eq("
    def fun end
    \n") + end + + it 'passes through invalid code blocks' do + allow_any_instance_of(described_class).to receive(:block_code).and_raise(StandardError) + + result = filter('
    This is a test
    ') + expect(result.to_html).to eq('
    This is a test
    ') + end +end diff --git a/spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb new file mode 100644 index 00000000000..3aba29f0f27 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb @@ -0,0 +1,97 @@ +# encoding: UTF-8 + +require 'spec_helper' + +describe Gitlab::Markdown::TableOfContentsFilter do + include FilterSpecHelper + + def header(level, text) + "#{text}\n" + end + + it 'does nothing when :no_header_anchors is truthy' do + exp = act = header(1, 'Header') + expect(filter(act, no_header_anchors: 1).to_html).to eq exp + end + + it 'does nothing with empty headers' do + exp = act = header(1, nil) + expect(filter(act).to_html).to eq exp + end + + 1.upto(6) do |i| + it "processes h#{i} elements" do + html = header(i, "Header #{i}") + doc = filter(html) + + expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}" + end + end + + describe 'anchor tag' do + it 'has an `anchor` class' do + doc = filter(header(1, 'Header')) + expect(doc.css('h1 a').first.attr('class')).to eq 'anchor' + end + + it 'links to the id' do + doc = filter(header(1, 'Header')) + expect(doc.css('h1 a').first.attr('href')).to eq '#header' + end + + describe 'generated IDs' do + it 'translates spaces to dashes' do + doc = filter(header(1, 'This header has spaces in it')) + expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it' + end + + it 'squeezes multiple spaces and dashes' do + doc = filter(header(1, 'This---header is poorly-formatted')) + expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted' + end + + it 'removes punctuation' do + doc = filter(header(1, "This, header! is, filled. with @ punctuation?")) + expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation' + end + + it 'appends a unique number to duplicates' do + doc = filter(header(1, 'One') + header(2, 'One')) + + expect(doc.css('h1 a').first.attr('id')).to eq 'one' + expect(doc.css('h2 a').first.attr('id')).to eq 'one-1' + end + + it 'supports Unicode' do + doc = filter(header(1, '한글')) + expect(doc.css('h1 a').first.attr('id')).to eq '한글' + expect(doc.css('h1 a').first.attr('href')).to eq '#한글' + end + end + end + + describe 'result' do + def result(html) + HTML::Pipeline.new([described_class]).call(html) + end + + let(:results) { result(header(1, 'Header 1') + header(2, 'Header 2')) } + let(:doc) { Nokogiri::XML::DocumentFragment.parse(results[:toc]) } + + it 'is contained within a `ul` element' do + expect(doc.children.first.name).to eq 'ul' + expect(doc.children.first.attr('class')).to eq 'section-nav' + end + + it 'contains an `li` element for each header' do + expect(doc.css('li').length).to eq 2 + + links = doc.css('li a') + + expect(links.first.attr('href')).to eq '#header-1' + expect(links.first.text).to eq 'Header 1' + expect(links.last.attr('href')).to eq '#header-2' + expect(links.last.text).to eq 'Header 2' + end + end +end diff --git a/spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb b/spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb new file mode 100644 index 00000000000..8b205adbbc4 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb @@ -0,0 +1,10 @@ +require 'spec_helper' + +describe Gitlab::Markdown::TaskListFilter do + include FilterSpecHelper + + it 'does not apply `task-list` class to non-task lists' do + exp = act = %(
    • Item
    ) + expect(filter(act).to_html).to eq exp + end +end diff --git a/spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb new file mode 100644 index 00000000000..aec22ee4168 --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb @@ -0,0 +1,73 @@ +# encoding: UTF-8 + +require 'spec_helper' + +describe Gitlab::Markdown::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 diff --git a/spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb new file mode 100644 index 00000000000..8ccd7744d6c --- /dev/null +++ b/spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb @@ -0,0 +1,120 @@ +require 'spec_helper' + +describe Gitlab::Markdown::UserReferenceFilter do + include FilterSpecHelper + + let(:project) { create(:empty_project, :public) } + let(:user) { create(:user) } + let(:reference) { user.to_reference } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + it 'ignores invalid users' do + exp = act = "Hey #{invalidate_reference(reference)}" + expect(filter(act).to_html).to eq(exp) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>Hey #{reference}" + expect(filter(act).to_html).to eq exp + end + end + + context 'mentioning @all' do + let(:reference) { User.reference_prefix + 'all' } + + before do + project.team << [project.creator, :developer] + end + + it 'supports a special @all mention' do + doc = filter("Hey #{reference}") + expect(doc.css('a').length).to eq 1 + expect(doc.css('a').first.attr('href')) + .to eq urls.namespace_project_url(project.namespace, project) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq [project.creator] + end + end + + context 'mentioning a user' do + it 'links to a User' do + doc = filter("Hey #{reference}") + expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) + end + + it 'links to a User with a period' do + user = create(:user, name: 'alphA.Beta') + + doc = filter("Hey #{user.to_reference}") + expect(doc.css('a').length).to eq 1 + end + + it 'links to a User with an underscore' do + user = create(:user, name: 'ping_pong_king') + + doc = filter("Hey #{user.to_reference}") + expect(doc.css('a').length).to eq 1 + end + + it 'includes a data-user attribute' do + doc = filter("Hey #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-user') + expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq [user] + end + end + + context 'mentioning a group' do + let(:group) { create(:group) } + let(:reference) { group.to_reference } + + it 'links to the Group' do + doc = filter("Hey #{reference}") + expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) + end + + it 'includes a data-group attribute' do + doc = filter("Hey #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-group') + expect(link.attr('data-group')).to eq group.id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq group.users + end + end + + it 'links with adjacent text' do + doc = filter("Mention me (#{reference}.)") + expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) + end + + it 'includes default classes' do + doc = filter("Hey #{reference}") + expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' + end + + it 'supports an :only_path context' do + doc = filter("Hey #{reference}", only_path: true) + link = doc.css('a').first.attr('href') + + expect(link).not_to match %r(https?://) + expect(link).to eq urls.user_path(user) + end +end diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb deleted file mode 100644 index 94c80ae6611..00000000000 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ /dev/null @@ -1,139 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe IssueReferenceFilter do - include FilterSpecHelper - - def helper - IssuesHelper - end - - let(:project) { create(:empty_project, :public) } - let(:issue) { create(:issue, project: project) } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Issue #{issue.to_reference}" - expect(filter(act).to_html).to eq exp - end - end - - context 'internal reference' do - let(:reference) { issue.to_reference } - - it 'ignores valid references when using non-default tracker' do - expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) - - exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp - end - - it 'links to a valid reference' do - doc = filter("Fixed #{reference}") - - expect(doc.css('a').first.attr('href')). - to eq helper.url_for_issue(issue.iid, project) - end - - it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid issue IDs' do - invalid = invalidate_reference(reference) - exp = act = "Fixed #{invalid}" - - expect(filter(act).to_html).to eq exp - end - - it 'includes a title attribute' do - doc = filter("Issue #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" - end - - it 'escapes the title attribute' do - issue.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid issue IDs on the referenced project' do - exp = act = "Fixed #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Fixed #{reference}") - expect(result[:references][:issue]).to eq [issue] - end - end - end -end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb deleted file mode 100644 index fc21b65a843..00000000000 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ /dev/null @@ -1,144 +0,0 @@ -require 'spec_helper' -require 'html/pipeline' - -module Gitlab::Markdown - describe LabelReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:empty_project, :public) } - let(:label) { create(:label, project: project) } - let(:reference) { label.to_reference } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Label #{reference}" - expect(filter(act).to_html).to eq exp - end - end - - it 'includes default classes' do - doc = filter("Label #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' - end - - it 'includes a data-project attribute' do - doc = filter("Label #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-project') - expect(link.attr('data-project')).to eq project.id.to_s - end - - it 'includes a data-label attribute' do - doc = filter("See #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-label') - expect(link.attr('data-label')).to eq label.id.to_s - end - - it 'supports an :only_path context' do - doc = filter("Label #{reference}", only_path: true) - link = doc.css('a').first.attr('href') - - expect(link).not_to match %r(https?://) - expect(link).to eq urls.namespace_project_issues_path(project.namespace, project, label_name: label.name) - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Label #{reference}") - expect(result[:references][:label]).to eq [label] - end - - describe 'label span element' do - it 'includes default classes' do - doc = filter("Label #{reference}") - expect(doc.css('a span').first.attr('class')).to eq 'label color-label' - end - - it 'includes a style attribute' do - doc = filter("Label #{reference}") - expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/) - end - end - - context 'Integer-based references' do - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) - end - - it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") - expect(doc.to_html).to match(%r(\(#{label.name}\.\))) - end - - it 'ignores invalid label IDs' do - exp = act = "Label #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - end - - context 'String-based single-word references' do - let(:label) { create(:label, name: 'gfm', project: project) } - let(:reference) { "#{Label.reference_prefix}#{label.name}" } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) - expect(doc.text).to eq 'See gfm' - end - - it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") - expect(doc.to_html).to match(%r(\(#{label.name}\.\))) - end - - it 'ignores invalid label names' do - exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" - - expect(filter(act).to_html).to eq exp - end - end - - context 'String-based multi-word references in quotes' do - let(:label) { create(:label, name: 'gfm references', project: project) } - let(:reference) { label.to_reference(:name) } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) - expect(doc.text).to eq 'See gfm references' - end - - it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") - expect(doc.to_html).to match(%r(\(#{label.name}\.\))) - end - - it 'ignores invalid label names' do - exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") - - expect(filter(act).to_html).to eq exp - end - end - - describe 'edge cases' do - it 'gracefully handles non-references matching the pattern' do - exp = act = '(format nil "~0f" 3.0) ; 3.0' - expect(filter(act).to_html).to eq exp - end - end - end -end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb deleted file mode 100644 index 3ef6cdfff33..00000000000 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ /dev/null @@ -1,120 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe MergeRequestReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:project, :public) } - let(:merge) { create(:merge_request, source_project: project) } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Merge #{merge.to_reference}" - expect(filter(act).to_html).to eq exp - end - end - - context 'internal reference' do - let(:reference) { merge.to_reference } - - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_merge_request_url(project.namespace, project, merge) - end - - it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") - expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid merge IDs' do - exp = act = "Merge #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - - it 'includes a title attribute' do - doc = filter("Merge #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" - end - - it 'escapes the title attribute' do - merge.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid merge IDs on the referenced project' do - exp = act = "Merge #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Merge #{reference}") - expect(result[:references][:merge_request]).to eq [merge] - end - end - end -end diff --git a/spec/lib/gitlab/markdown/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/redactor_filter_spec.rb deleted file mode 100644 index eea3f1cf370..00000000000 --- a/spec/lib/gitlab/markdown/redactor_filter_spec.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe RedactorFilter do - include ActionView::Helpers::UrlHelper - include FilterSpecHelper - - it 'ignores non-GFM links' do - html = %(See Google) - doc = filter(html, current_user: double) - - expect(doc.css('a').length).to eq 1 - end - - def reference_link(data) - link_to('text', '', class: 'gfm', data: data) - end - - context 'with data-project' do - it 'removes unpermitted Project references' do - user = create(:user) - project = create(:empty_project) - - link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) - doc = filter(link, current_user: user) - - expect(doc.css('a').length).to eq 0 - end - - it 'allows permitted Project references' do - user = create(:user) - project = create(:empty_project) - project.team << [user, :master] - - link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) - doc = filter(link, current_user: user) - - expect(doc.css('a').length).to eq 1 - end - - it 'handles invalid Project references' do - link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name) - - expect { filter(link) }.not_to raise_error - end - end - - context "for user references" do - - context 'with data-group' do - it 'removes unpermitted Group references' do - user = create(:user) - group = create(:group) - - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - doc = filter(link, current_user: user) - - expect(doc.css('a').length).to eq 0 - end - - it 'allows permitted Group references' do - user = create(:user) - group = create(:group) - group.add_developer(user) - - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - doc = filter(link, current_user: user) - - expect(doc.css('a').length).to eq 1 - end - - it 'handles invalid Group references' do - link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - - expect { filter(link) }.not_to raise_error - end - end - - context 'with data-user' do - it 'allows any User reference' do - user = create(:user) - - link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - doc = filter(link) - - expect(doc.css('a').length).to eq 1 - end - end - end - end -end diff --git a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb deleted file mode 100644 index 4fa473ad191..00000000000 --- a/spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb +++ /dev/null @@ -1,89 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe ReferenceGathererFilter do - include ActionView::Helpers::UrlHelper - include FilterSpecHelper - - def reference_link(data) - link_to('text', '', class: 'gfm', data: data) - end - - context "for issue references" do - - context 'with data-project' do - it 'removes unpermitted Project references' do - user = create(:user) - project = create(:empty_project) - issue = create(:issue, project: project) - - link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) - result = pipeline_result(link, current_user: user) - - expect(result[:references][:issue]).to be_empty - end - - it 'allows permitted Project references' do - user = create(:user) - project = create(:empty_project) - issue = create(:issue, project: project) - project.team << [user, :master] - - link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) - result = pipeline_result(link, current_user: user) - - expect(result[:references][:issue]).to eq([issue]) - end - - it 'handles invalid Project references' do - link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) - - expect { pipeline_result(link) }.not_to raise_error - end - end - end - - context "for user references" do - - context 'with data-group' do - it 'removes unpermitted Group references' do - user = create(:user) - group = create(:group) - - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - result = pipeline_result(link, current_user: user) - - expect(result[:references][:user]).to be_empty - end - - it 'allows permitted Group references' do - user = create(:user) - group = create(:group) - group.add_developer(user) - - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - result = pipeline_result(link, current_user: user) - - expect(result[:references][:user]).to eq([user]) - end - - it 'handles invalid Group references' do - link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - - expect { pipeline_result(link) }.not_to raise_error - end - end - - context 'with data-user' do - it 'allows any User reference' do - user = create(:user) - - link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) - result = pipeline_result(link) - - expect(result[:references][:user]).to eq([user]) - end - end - end - end -end diff --git a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb b/spec/lib/gitlab/markdown/relative_link_filter_spec.rb deleted file mode 100644 index 027336ceb73..00000000000 --- a/spec/lib/gitlab/markdown/relative_link_filter_spec.rb +++ /dev/null @@ -1,149 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -module Gitlab::Markdown - describe RelativeLinkFilter do - def filter(doc, contexts = {}) - contexts.reverse_merge!({ - commit: project.commit, - project: project, - project_wiki: project_wiki, - ref: ref, - requested_path: requested_path - }) - - described_class.call(doc, contexts) - end - - def image(path) - %() - end - - def link(path) - %(#{path}) - end - - let(:project) { create(:project) } - let(:project_path) { project.path_with_namespace } - let(:ref) { 'markdown' } - let(:project_wiki) { nil } - let(:requested_path) { '/' } - - 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 - - shared_examples :relative_to_requested do - it 'rebuilds URL relative to the requested path' do - doc = filter(link('users.md')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/doc/api/users.md" - end - end - - context 'with a project_wiki' do - let(:project_wiki) { double('ProjectWiki') } - include_examples :preserve_unchanged - end - - context 'without a repository' do - let(:project) { create(:empty_project) } - include_examples :preserve_unchanged - end - - context 'with an empty repository' do - let(:project) { create(:project_empty_repo) } - include_examples :preserve_unchanged - 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 file in the repo' do - doc = filter(link('doc/api/README.md')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" - end - - it 'rebuilds relative URL for a file in the repo up one directory' do - relative_link = link('../api/README.md') - doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md') - - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" - end - - it 'rebuilds relative URL for a file in the repo up multiple directories' do - relative_link = link('../../../api/README.md') - doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md') - - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" - end - - it 'rebuilds relative URL for a file in the repo with an anchor' do - doc = filter(link('README.md#section')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/blob/#{ref}/README.md#section" - end - - it 'rebuilds relative URL for a directory in the repo' do - doc = filter(link('doc/api/')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/tree/#{ref}/doc/api" - end - - it 'rebuilds relative URL for an image in the repo' do - doc = filter(link('files/images/logo-black.png')) - expect(doc.at_css('a')['href']). - to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png" - end - - it 'does not modify relative URL with an anchor only' do - doc = filter(link('#section-1')) - expect(doc.at_css('a')['href']).to eq '#section-1' - 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 = 'files/images/한글.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 '/raw/' - end - - context 'when requested path is a file in the repo' do - let(:requested_path) { 'doc/api/README.md' } - include_examples :relative_to_requested - end - - context 'when requested path is a directory in the repo' do - let(:requested_path) { 'doc/api' } - include_examples :relative_to_requested - end - end - end -end diff --git a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb b/spec/lib/gitlab/markdown/sanitization_filter_spec.rb deleted file mode 100644 index b089729172d..00000000000 --- a/spec/lib/gitlab/markdown/sanitization_filter_spec.rb +++ /dev/null @@ -1,199 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe SanitizationFilter do - include FilterSpecHelper - - describe 'default whitelist' do - it 'sanitizes tags that are not whitelisted' do - act = %q{ and no blinks} - exp = 'no inputs and no blinks' - expect(filter(act).to_html).to eq exp - end - - it 'sanitizes tag attributes' do - act = %q{Text} - exp = %q{Text} - expect(filter(act).to_html).to eq exp - end - - it 'sanitizes javascript in attributes' do - act = %q(Text) - exp = 'Text' - expect(filter(act).to_html).to eq exp - end - - it 'allows whitelisted HTML tags from the user' do - exp = act = "
    \n
    Term
    \n
    Definition
    \n
    " - expect(filter(act).to_html).to eq exp - end - - it 'sanitizes `class` attribute on any element' do - act = %q{Strong} - expect(filter(act).to_html).to eq %q{Strong} - end - - it 'sanitizes `id` attribute on any element' do - act = %q{Emphasis} - expect(filter(act).to_html).to eq %q{Emphasis} - end - end - - describe 'custom whitelist' do - it 'customizes the whitelist only once' do - instance = described_class.new('Foo') - 3.times { instance.whitelist } - - expect(instance.whitelist[:transformers].size).to eq 5 - end - - it 'allows syntax highlighting' do - exp = act = %q{
    def
    } - expect(filter(act).to_html).to eq exp - end - - it 'sanitizes `class` attribute from non-highlight spans' do - act = %q{def} - expect(filter(act).to_html).to eq %q{def} - end - - it 'allows `style` attribute on table elements' do - html = <<-HTML.strip_heredoc - - - -
    Head
    Body
    - HTML - - doc = filter(html) - - expect(doc.at_css('th')['style']).to eq 'text-align: center' - expect(doc.at_css('td')['style']).to eq 'text-align: right' - end - - it 'allows `span` elements' do - exp = act = %q{Hello} - expect(filter(act).to_html).to eq exp - end - - it 'removes `rel` attribute from `a` elements' do - act = %q{Link} - exp = %q{Link} - - expect(filter(act).to_html).to eq exp - end - - # Adapted from the Sanitize test suite: http://git.io/vczrM - protocols = { - 'protocol-based JS injection: simple, no spaces' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: simple, spaces before' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: simple, spaces after' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: simple, spaces before and after' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: preceding colon' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: UTF-8 encoding' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: long UTF-8 encoding' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: long UTF-8 encoding without semicolons' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: hex encoding' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: long hex encoding' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: hex encoding without semicolons' => { - input: 'foo', - output: 'foo' - }, - - 'protocol-based JS injection: null char' => { - input: "foo", - output: '' - }, - - 'protocol-based JS injection: spaces and entities' => { - input: 'foo', - output: 'foo' - }, - } - - protocols.each do |name, data| - it "handles #{name}" do - doc = filter(data[:input]) - - expect(doc.to_html).to eq data[:output] - end - end - - it 'allows non-standard anchor schemes' do - exp = %q{IRC} - act = filter(exp) - - expect(act.to_html).to eq exp - end - - it 'allows relative links' do - exp = %q{foo/bar.md} - act = filter(exp) - - expect(act.to_html).to eq exp - end - end - - context 'when inline_sanitization is true' do - it 'uses a stricter whitelist' do - doc = filter('

    Description

    ', inline_sanitization: true) - expect(doc.to_html.strip).to eq 'Description' - end - - %w(pre code img ol ul li).each do |elem| - it "removes '#{elem}' elements" do - act = "<#{elem}>Description" - expect(filter(act, inline_sanitization: true).to_html.strip). - to eq 'Description' - end - end - - %w(b i strong em a ins del sup sub p).each do |elem| - it "still allows '#{elem}' elements" do - exp = act = "<#{elem}>Description" - expect(filter(act, inline_sanitization: true).to_html).to eq exp - end - end - end - end -end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb deleted file mode 100644 index 9d9652dba46..00000000000 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe SnippetReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:empty_project, :public) } - let(:snippet) { create(:project_snippet, project: project) } - let(:reference) { snippet.to_reference } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Snippet #{reference}" - expect(filter(act).to_html).to eq exp - end - end - - context 'internal reference' do - it 'links to a valid reference' do - doc = filter("See #{reference}") - - expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_snippet_url(project.namespace, project, snippet) - end - - it 'links with adjacent text' do - doc = filter("Snippet (#{reference}.)") - expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid snippet IDs' do - exp = act = "Snippet #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - - it 'includes a title attribute' do - doc = filter("Snippet #{reference}") - expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" - end - - it 'escapes the title attribute' do - snippet.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) - end - - it 'ignores invalid snippet IDs on the referenced project' do - exp = act = "See #{invalidate_reference(reference)}" - - expect(filter(act).to_html).to eq exp - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Snippet #{reference}") - expect(result[:references][:snippet]).to eq [snippet] - end - end - end -end diff --git a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb b/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb deleted file mode 100644 index 6a490673728..00000000000 --- a/spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe SyntaxHighlightFilter do - include FilterSpecHelper - - it 'highlights valid code blocks' do - result = filter('
    def fun end')
    -      expect(result.to_html).to eq("
    def fun end
    \n") - end - - it 'passes through invalid code blocks' do - allow_any_instance_of(SyntaxHighlightFilter).to receive(:block_code).and_raise(StandardError) - - result = filter('
    This is a test
    ') - expect(result.to_html).to eq('
    This is a test
    ') - end - end -end diff --git a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb b/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb deleted file mode 100644 index ddf583a72c1..00000000000 --- a/spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb +++ /dev/null @@ -1,99 +0,0 @@ -# encoding: UTF-8 - -require 'spec_helper' - -module Gitlab::Markdown - describe TableOfContentsFilter do - include FilterSpecHelper - - def header(level, text) - "#{text}\n" - end - - it 'does nothing when :no_header_anchors is truthy' do - exp = act = header(1, 'Header') - expect(filter(act, no_header_anchors: 1).to_html).to eq exp - end - - it 'does nothing with empty headers' do - exp = act = header(1, nil) - expect(filter(act).to_html).to eq exp - end - - 1.upto(6) do |i| - it "processes h#{i} elements" do - html = header(i, "Header #{i}") - doc = filter(html) - - expect(doc.css("h#{i} a").first.attr('id')).to eq "header-#{i}" - end - end - - describe 'anchor tag' do - it 'has an `anchor` class' do - doc = filter(header(1, 'Header')) - expect(doc.css('h1 a').first.attr('class')).to eq 'anchor' - end - - it 'links to the id' do - doc = filter(header(1, 'Header')) - expect(doc.css('h1 a').first.attr('href')).to eq '#header' - end - - describe 'generated IDs' do - it 'translates spaces to dashes' do - doc = filter(header(1, 'This header has spaces in it')) - expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-has-spaces-in-it' - end - - it 'squeezes multiple spaces and dashes' do - doc = filter(header(1, 'This---header is poorly-formatted')) - expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-poorly-formatted' - end - - it 'removes punctuation' do - doc = filter(header(1, "This, header! is, filled. with @ punctuation?")) - expect(doc.css('h1 a').first.attr('id')).to eq 'this-header-is-filled-with-punctuation' - end - - it 'appends a unique number to duplicates' do - doc = filter(header(1, 'One') + header(2, 'One')) - - expect(doc.css('h1 a').first.attr('id')).to eq 'one' - expect(doc.css('h2 a').first.attr('id')).to eq 'one-1' - end - - it 'supports Unicode' do - doc = filter(header(1, '한글')) - expect(doc.css('h1 a').first.attr('id')).to eq '한글' - expect(doc.css('h1 a').first.attr('href')).to eq '#한글' - end - end - end - - describe 'result' do - def result(html) - HTML::Pipeline.new([described_class]).call(html) - end - - let(:results) { result(header(1, 'Header 1') + header(2, 'Header 2')) } - let(:doc) { Nokogiri::XML::DocumentFragment.parse(results[:toc]) } - - it 'is contained within a `ul` element' do - expect(doc.children.first.name).to eq 'ul' - expect(doc.children.first.attr('class')).to eq 'section-nav' - end - - it 'contains an `li` element for each header' do - expect(doc.css('li').length).to eq 2 - - links = doc.css('li a') - - expect(links.first.attr('href')).to eq '#header-1' - expect(links.first.text).to eq 'Header 1' - expect(links.last.attr('href')).to eq '#header-2' - expect(links.last.text).to eq 'Header 2' - end - end - end -end diff --git a/spec/lib/gitlab/markdown/task_list_filter_spec.rb b/spec/lib/gitlab/markdown/task_list_filter_spec.rb deleted file mode 100644 index 94f39cc966e..00000000000 --- a/spec/lib/gitlab/markdown/task_list_filter_spec.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe TaskListFilter do - include FilterSpecHelper - - it 'does not apply `task-list` class to non-task lists' do - exp = act = %(
    • Item
    ) - expect(filter(act).to_html).to eq exp - end - end -end diff --git a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb b/spec/lib/gitlab/markdown/upload_link_filter_spec.rb deleted file mode 100644 index 9ae45a6f559..00000000000 --- a/spec/lib/gitlab/markdown/upload_link_filter_spec.rb +++ /dev/null @@ -1,75 +0,0 @@ -# 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/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb deleted file mode 100644 index d9e0d7c42db..00000000000 --- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -require 'spec_helper' - -module Gitlab::Markdown - describe UserReferenceFilter do - include FilterSpecHelper - - let(:project) { create(:empty_project, :public) } - let(:user) { create(:user) } - let(:reference) { user.to_reference } - - it 'requires project context' do - expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) - end - - it 'ignores invalid users' do - exp = act = "Hey #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq(exp) - end - - %w(pre code a style).each do |elem| - it "ignores valid references contained inside '#{elem}' element" do - exp = act = "<#{elem}>Hey #{reference}" - expect(filter(act).to_html).to eq exp - end - end - - context 'mentioning @all' do - let(:reference) { User.reference_prefix + 'all' } - - before do - project.team << [project.creator, :developer] - end - - it 'supports a special @all mention' do - doc = filter("Hey #{reference}") - expect(doc.css('a').length).to eq 1 - expect(doc.css('a').first.attr('href')) - .to eq urls.namespace_project_url(project.namespace, project) - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Hey #{reference}") - expect(result[:references][:user]).to eq [project.creator] - end - end - - context 'mentioning a user' do - it 'links to a User' do - doc = filter("Hey #{reference}") - expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) - end - - it 'links to a User with a period' do - user = create(:user, name: 'alphA.Beta') - - doc = filter("Hey #{user.to_reference}") - expect(doc.css('a').length).to eq 1 - end - - it 'links to a User with an underscore' do - user = create(:user, name: 'ping_pong_king') - - doc = filter("Hey #{user.to_reference}") - expect(doc.css('a').length).to eq 1 - end - - it 'includes a data-user attribute' do - doc = filter("Hey #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-user') - expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Hey #{reference}") - expect(result[:references][:user]).to eq [user] - end - end - - context 'mentioning a group' do - let(:group) { create(:group) } - let(:reference) { group.to_reference } - - it 'links to the Group' do - doc = filter("Hey #{reference}") - expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) - end - - it 'includes a data-group attribute' do - doc = filter("Hey #{reference}") - link = doc.css('a').first - - expect(link).to have_attribute('data-group') - expect(link.attr('data-group')).to eq group.id.to_s - end - - it 'adds to the results hash' do - result = reference_pipeline_result("Hey #{reference}") - expect(result[:references][:user]).to eq group.users - end - end - - it 'links with adjacent text' do - doc = filter("Mention me (#{reference}.)") - expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) - end - - it 'includes default classes' do - doc = filter("Hey #{reference}") - expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' - end - - it 'supports an :only_path context' do - doc = filter("Hey #{reference}", only_path: true) - link = doc.css('a').first.attr('href') - - expect(link).not_to match %r(https?://) - expect(link).to eq urls.user_path(user) - end - end -end -- cgit v1.2.1 From 52bcfd037fdc17bd73a674c248045dc750cc55c6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 19 Nov 2015 17:18:07 +0200 Subject: Update public access documentation [ci skip] --- doc/public_access/public_access.md | 51 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index bd439f7c6f3..6e22ea7b72a 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -1,44 +1,59 @@ # Public access -GitLab allows you to open selected projects to be accessed **publicly** or **internally**. +GitLab allows you to change your projects' visibility in order be accessed +**publicly** or **internally**. -Projects with either of these visibility levels will be listed in the [public access directory](/public). +Projects with either of these visibility levels will be listed in the +public access directory (`/public` under your GitLab instance). +Here is the [GitLab.com example](https://gitlab.com/public). Internal projects will only be available to authenticated users. -## Public projects +## Visibility of projects + +### Public projects Public projects can be cloned **without any** authentication. -It will also be listed on the [public access directory](/public). +They will also be listed on the public access directory (`/public`). -**Any logged in user** will have [Guest](../permissions/permissions) permissions on the repository. +**Any logged in user** will have [Guest](../permissions/permissions) +permissions on the repository. -## Internal projects +### Internal projects Internal projects can be cloned by any logged in user. -It will also be listed on the [public access directory](/public) for logged in users. +They will also be listed on the public access directory (`/public`) for logged +in users. -Any logged in user will have [Guest](../permissions/permissions) permissions on the repository. +Any logged in user will have [Guest](../permissions/permissions) permissions on +the repository. -## How to change project visibility +### How to change project visibility -1. Go to your project dashboard -1. Click on the "Edit" tab -1. Change "Visibility Level" +1. Go to your project's **Settings** +1. Change "Visibility Level" to either Public, Internal or Private ## Visibility of users -The public page of users, located at `/u/username` is visible if either: +The public page of a user, located at `/u/username`, is always visible whether +you are logged in or not. + +When visiting the public page of a user, you can only see the projects which +you are privileged to. -- You are logged in. -- You are logged out, and the target user is authorized to (is Guest, Reporter, etc.) at least one public project. +## Visibility of groups -Otherwise, you will be redirected to the sign in page. +The public page of a group, located at `/groups/groupname`, is always visible +to everyone. -When visiting the public page of an user, you will only see listed projects which you can view yourself. +Logged out users will be able to see the description and the avatar of the +group as well as all public projects belonging to that group. ## Restricting the use of public or internal projects -In the Admin area under Settings you can disable public projects or public and internal projects for the entire GitLab installation to prevent people making code public by accident. The restricted visibility settings do not apply to admin users. +In the Admin area under **Settings** (`/admin/application_settings`), you can +restrict the use of visibility levels for users when they create a project or a +snippet. This is useful to prevent people exposing their repositories to public +by accident. The restricted visibility settings do not apply to admin users. -- cgit v1.2.1 From de440ee5e3fd03d0e3de33c474b1889e03aaf04a Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 16:25:17 +0100 Subject: More GitLab spellings. No dedicated CI in omnibus [ci skip] --- CONTRIBUTING.md | 10 +++++----- README.md | 2 -- doc/install/installation.md | 2 +- doc/release/monthly.md | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d85c9f3fca..007e410b677 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,8 +23,8 @@ Issues and merge requests should be in English and contain appropriate language ## Helping others Please help other GitLab users when you can. -The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). -Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel. +The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). +Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. ## Issue tracker @@ -59,7 +59,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). -If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. +If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. @@ -99,7 +99,7 @@ If you contribute to GitLab please know that changes involve more than just code We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html). Please ensure you support the feature you contribute through all of these steps. -1. Description explaning the relevancy (see following item) +1. Description explaining the relevancy (see following item) 1. Working and clean code that is commented where needed 1. Unit and integration tests that pass on the CI server 1. Documented in the /doc directory @@ -163,7 +163,7 @@ If you add a dependency in GitLab (such as an operating system package) please c 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Database Migrations](doc/development/migration_style_guide.md) 1. [Documentation styleguide](doc_styleguide.md) -1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). +1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). diff --git a/README.md b/README.md index 52e2d977620..c59c8593eba 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,6 @@ There are two editions of GitLab: - GitLab Community Edition (CE) is available freely under the MIT Expat license. - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). -Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code. - ## Website On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: diff --git a/doc/install/installation.md b/doc/install/installation.md index 52ae30af805..a710407d4f6 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -320,7 +320,7 @@ GitLab Shell is an SSH access and repository management software developed speci **Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps. -**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)". +**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1 hostname"). This might be necessary for example if you set up GitLab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)". ### Install gitlab-workhorse diff --git a/doc/release/monthly.md b/doc/release/monthly.md index c9ab87671d2..aff3f066b24 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -159,7 +159,7 @@ Please do not raise issues directly in this issue but link to issues that might The decision to create a patch release or not is with the release manager who is assigned to this issue. The release manager will comment here about the plans for patch releases. -Assign the issue to the release manager and at mention all members of gitlab core team. If there are any known bugs in the release add them immediately. +Assign the issue to the release manager and at mention all members of GitLab core team. If there are any known bugs in the release add them immediately. ## Tweet about RC1 -- cgit v1.2.1 From 91a76957e3d18e3cb89bc8320f8513e1002e551e Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 19 Nov 2015 17:05:30 +0100 Subject: Update LFS docs for cloning [ci skip] --- doc/workflow/git_lfs.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md index e1064051fe8..616a71522ae 100644 --- a/doc/workflow/git_lfs.md +++ b/doc/workflow/git_lfs.md @@ -55,7 +55,7 @@ In `config/gitlab.yml`: * When SSH is set as a remote, Git LFS objects still go through HTTPS * Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended * Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported -* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the url to Git config manually (see #troubleshooting-tips) +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting-tips) ## Using Git LFS @@ -77,11 +77,10 @@ git commit -am "Added Debian iso" # commit the file meta data git push origin master # sync the git repo and large file to the GitLab server ``` -Downloading a single large file is also very simple: +Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. ```bash git clone git@gitlab.example.com:group/project.git -git lfs fetch debian.iso # download the large file ``` -- cgit v1.2.1 From bdf4007cb7b18ed6892455d0a9adf78476188563 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 18:12:17 +0200 Subject: adressing comments --- app/assets/javascripts/awards_handler.coffee | 6 +++--- app/controllers/projects/notes_controller.rb | 12 ++++++------ app/models/note.rb | 5 +++-- app/services/notes/create_service.rb | 4 ++-- app/views/votes/_votes_block.html.haml | 14 +++++++------- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index f5b9adbe9e2..ae42e390c43 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -79,11 +79,11 @@ class @AwardsHandler postEmoji: (emoji, callback) -> - $.post @post_emoji_url, { - emoji: emoji + $.post @post_emoji_url, { note: { + note: emoji noteable_type: @noteable_type noteable_id: @noteable_id - },(data) -> + }},(data) -> if data.ok callback.call() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 8159cc50838..263b8b8d94e 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -59,21 +59,21 @@ class Projects::NotesController < Projects::ApplicationController end def award_toggle - noteable = params[:noteable_type] == "Issue" ? Issue : MergeRequest - noteable = noteable.find(params[:noteable_id]) + noteable = note_params[:noteable_type] == "issue" ? Issue : MergeRequest + noteable = noteable.find_by!(id: note_params[:noteable_id], project: project) + data = { - noteable: noteable, author: current_user, is_award: true, - note: params[:emoji] + note: note_params[:note] } - note = project.notes.find_by(data) + note = noteable.notes.find_by(data) if note note.destroy else - project.notes.create(data) + Notes::CreateService.new(project, current_user, note_params).execute end render json: { ok: true } diff --git a/app/models/note.rb b/app/models/note.rb index 39645f8f1ad..e30be444eb5 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -40,13 +40,14 @@ class Note < ActiveRecord::Base delegate :name, :email, to: :author, prefix: true validates :note, :project, presence: true + validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' } validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' } - validates :author, presence: true, if: ->(n) { n.is_award } + validates :author, presence: true mount_uploader :attachment, AttachmentUploader @@ -102,7 +103,7 @@ class Note < ActiveRecord::Base end def grouped_awards - select(:note).distinct.map do |note| + awards.select(:note).distinct.map do |note| [ note.note, where(note: note.note) ] end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index dbff58dfb9c..25a985df4d8 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,11 +35,11 @@ module Notes end def contains_emoji_only?(note) - note =~ /\A:[-_+[:alnum:]]*:\s?\z/ + note =~ /\A:?[-_+[:alnum:]]*:?\s?\z/ end def emoji_name(note) - note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + note.match(/\A:?([-_+[:alnum:]]*):?\s?/)[1] end end end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 04e3b5a3814..7eb27c12d33 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,15 +1,15 @@ .awards.votes-block - - votable.notes.awards.grouped_awards.each do | note | - .award{class: (note_active_class(note.last, current_user)), title: emoji_author_list(note.last, current_user)} - .icon{"data-emoji" => "#{note.first}"} - = image_tag url_to_emoji(note.first), height: "20px", width: "20px" + - votable.notes.awards.grouped_awards.each do |emoji, notes| + .award{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user)} + .icon{"data-emoji" => "#{emoji}"} + = image_tag url_to_emoji(emoji), height: "20px", width: "20px" .counter - = note.last.count + = notes.count - if current_user .dropdown.awards-controls %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"} - = icon('plus-square-o') + = icon('smile-o') %ul.dropdown-menu.awards-menu - emoji_list.each do |emoji| %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px" @@ -17,7 +17,7 @@ - if current_user :coffeescript post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" - noteable_type = "#{votable.class}" + noteable_type = "#{votable.class.name.underscore}" noteable_id = "#{votable.id}" window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) -- cgit v1.2.1 From 888821f9ffb56c6fdf762f28dd42cf3b7226652f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 19:22:46 +0100 Subject: Add support for batch download operation --- lib/gitlab/lfs/response.rb | 105 ++++++++++++++++++++++++--------- lib/gitlab/lfs/router.rb | 2 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 25 +++++--- 3 files changed, 96 insertions(+), 36 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index 4202c786466..ddadc07ebba 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -26,7 +26,7 @@ module Gitlab def render_download_object_response(oid) render_response_to_download do - if check_download_sendfile_header? && check_download_accept_header? + if check_download_sendfile_header? render_lfs_sendfile(oid) else render_not_found @@ -34,20 +34,15 @@ module Gitlab end end - def render_lfs_api_auth - render_response_to_push do - request_body = JSON.parse(@request.body.read) - return render_not_found if request_body.empty? || request_body['objects'].empty? - - response = build_response(request_body['objects']) - [ - 200, - { - "Content-Type" => "application/json; charset=utf-8", - "Cache-Control" => "private", - }, - [JSON.dump(response)] - ] + def render_batch_operation_response + request_body = JSON.parse(@request.body.read) + case request_body["operation"] + when "download" + render_batch_download(request_body) + when "upload" + render_batch_upload(request_body) + else + render_forbidden end end @@ -142,6 +137,38 @@ module Gitlab end end + def render_batch_upload(body) + return render_not_found if body.empty? || body['objects'].nil? + + render_response_to_push do + response = build_upload_batch_response(body['objects']) + [ + 200, + { + "Content-Type" => "application/json; charset=utf-8", + "Cache-Control" => "private", + }, + [JSON.dump(response)] + ] + end + end + + def render_batch_download(body) + return render_not_found if body.empty? || body['objects'].nil? + + render_response_to_download do + response = build_download_batch_response(body['objects']) + [ + 200, + { + "Content-Type" => "application/json; charset=utf-8", + "Cache-Control" => "private", + }, + [JSON.dump(response)] + ] + end + end + def render_lfs_download_hypermedia(oid) return render_not_found unless oid.present? @@ -266,10 +293,16 @@ module Gitlab @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set end - def build_response(objects) + def build_upload_batch_response(objects) selected_objects = select_existing_objects(objects) - upload_hypermedia(objects, selected_objects) + upload_hypermedia_links(objects, selected_objects) + end + + def build_download_batch_response(objects) + selected_objects = select_existing_objects(objects) + + download_hypermedia_links(objects, selected_objects) end def download_hypermedia(oid) @@ -279,7 +312,6 @@ module Gitlab { 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}", 'header' => { - 'Accept' => "application/vnd.git-lfs+json; charset=utf-8", 'Authorization' => @env['HTTP_AUTHORIZATION'] }.compact } @@ -287,21 +319,40 @@ module Gitlab } end - def upload_hypermedia(all_objects, existing_objects) + def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - object['_links'] = hypermedia_links(object) unless existing_objects.include?(object['oid']) + # generate links only for existing objects + next unless existing_objects.include?(object['oid']) + + object['_links'] = { + 'download' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } + } end { 'objects' => all_objects } end - def hypermedia_links(object) - { - "upload" => { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", - 'header' => { 'Authorization' => @env['HTTP_AUTHORIZATION'] } - }.compact - } + def upload_hypermedia_links(all_objects, existing_objects) + all_objects.each do |object| + # generate links only for non-existing objects + next if existing_objects.include?(object['oid']) + + object['_links'] = { + 'upload' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } + } + end + + { 'objects' => all_objects } end end end diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb index 4809e834984..2b3f2e8e958 100644 --- a/lib/gitlab/lfs/router.rb +++ b/lib/gitlab/lfs/router.rb @@ -48,7 +48,7 @@ module Gitlab # Check for Batch API if post_path[0].ends_with?("/info/lfs/objects/batch") - lfs.render_lfs_api_auth + lfs.render_batch_operation_response else nil end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index cebcb5bc887..5eafaad79c9 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -66,7 +66,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth, "Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) end end @@ -107,7 +107,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end @@ -117,7 +117,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end end @@ -191,7 +191,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8") + expect(json_response['_links']['download']['header']).to eq({}) end end @@ -219,7 +219,7 @@ describe Gitlab::Lfs::Router do json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(json_response['_links']['download']['header']).to eq("Accept" => "application/vnd.git-lfs+json; charset=utf-8", "Authorization" => @auth) + expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) end end @@ -250,7 +250,8 @@ describe Gitlab::Lfs::Router do body = { 'objects' => [{ 'oid' => sample_oid, 'size' => sample_size - }] + }], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) end @@ -286,7 +287,8 @@ describe Gitlab::Lfs::Router do 'objects' => [{ 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', 'size' => 1575078 - }] + }], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) end @@ -315,7 +317,8 @@ describe Gitlab::Lfs::Router do { 'oid' => sample_oid, 'size' => sample_size } - ] + ], + 'operation' => 'upload' }.to_json env['rack.input'] = StringIO.new(body) public_project.lfs_objects << lfs_object @@ -351,6 +354,12 @@ describe Gitlab::Lfs::Router do end context 'when user is not authenticated' do + before do + env['rack.input'] = StringIO.new( + { 'objects' => [], 'operation' => 'upload' }.to_json + ) + end + context 'when user has push access' do before do project.team << [user, :master] -- cgit v1.2.1 From 8248314bc9256d3a0252ad6322df098edca7385a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 19 Nov 2015 20:16:56 +0100 Subject: Don't rescue Exception, but StandardError --- app/controllers/ci/lints_controller.rb | 4 ++-- app/models/ci/commit.rb | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index 24dd1b5c93a..a4f6aff49b4 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -15,10 +15,10 @@ module Ci @builds = @config_processor.builds @status = true end - rescue Ci::GitlabCiYamlProcessor::ValidationError => e + rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e @error = e.message @status = false - rescue Exception + rescue @error = "Undefined error" @status = false end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index b0c78499e49..971e899de84 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -190,14 +190,11 @@ module Ci def config_processor return nil unless ci_yaml_file @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) - rescue Ci::GitlabCiYamlProcessor::ValidationError => e + rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e save_yaml_error(e.message) nil - rescue Psych::SyntaxError => e - save_yaml_error(e.message) - nil - rescue Exception - save_yaml_error("Undefined yaml error") + rescue + save_yaml_error("Undefined error") nil end -- cgit v1.2.1 From 22bbb379ae976d94d7ddd7addb7384e0b79ddfd9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 19 Nov 2015 23:00:30 +0200 Subject: fox tests --- spec/models/note_spec.rb | 5 ++- spec/services/notes/create_service_spec.rb | 50 +++++++++++++++--------------- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 86b6c27a182..f347f537550 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -132,12 +132,11 @@ describe Note do describe :grouped_awards do before do - create :note, note: "smile" - create :note, note: "smile" + create :note, note: "smile", is_award: true + create :note, note: "smile", is_award: true end it "returns grouped array of notes" do - grouped_array = Note.grouped_awards expect(Note.grouped_awards.first.first).to eq("smile") expect(Note.grouped_awards.first.last).to match_array(Note.all) end diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index 066461f5a12..cc38d257792 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -26,36 +26,36 @@ describe Notes::CreateService do end describe "award emoji" do - before do - project.team << [user, :master] - end + before do + project.team << [user, :master] + end - it "creates emoji note" do - opts = { - note: ':smile: ', - noteable_type: 'Issue', - noteable_id: issue.id - } + it "creates emoji note" do + opts = { + note: ':smile: ', + noteable_type: 'Issue', + noteable_id: issue.id + } - @note = Notes::CreateService.new(project, user, opts).execute + @note = Notes::CreateService.new(project, user, opts).execute - expect(@note).to be_valid - expect(@note.note).to eq('smile') - expect(@note.is_award).to be_truthy - end + expect(@note).to be_valid + expect(@note.note).to eq('smile') + expect(@note.is_award).to be_truthy + end - it "creates regular note if emoji name is invalid" do - opts = { - note: ':smile: moretext: ', - noteable_type: 'Issue', - noteable_id: issue.id - } + it "creates regular note if emoji name is invalid" do + opts = { + note: ':smile: moretext: ', + noteable_type: 'Issue', + noteable_id: issue.id + } - @note = Notes::CreateService.new(project, user, opts).execute + @note = Notes::CreateService.new(project, user, opts).execute - expect(@note).to be_valid - expect(@note.note).to eq(opts[:note]) - expect(@note.is_award).to be_falsy - end + expect(@note).to be_valid + expect(@note.note).to eq(opts[:note]) + expect(@note.is_award).to be_falsy + end end end -- cgit v1.2.1 From 2a3680219bb5a4d35aa9b98ba2a2c43855aea27a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 19 Nov 2015 15:46:05 -0500 Subject: Add a fallback for Safari copy-to-clipboard Also, hide the tooltip in a less stupid way. Closes #3547 --- app/assets/javascripts/copy_to_clipboard.js.coffee | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee index ec4b80cca6f..9c68c5cc1bc 100644 --- a/app/assets/javascripts/copy_to_clipboard.js.coffee +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -9,13 +9,24 @@ $ -> clipboard.on 'success', (e) -> $(e.trigger). tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). - tooltip('show') + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) # Clear the selection and blur the trigger so it loses its border e.clearSelection() $(e.trigger).blur() - # Manually hide the tooltip after 1 second - setTimeout(-> - $(e.trigger).tooltip('hide') - , 1000) + # Safari doesn't support `execCommand`, so instead we inform the user to + # copy manually. + # + # See http://clipboardjs.com/#browser-support + clipboard.on 'error', (e) -> + if /Mac/i.test(navigator.userAgent) + title = "Press ⌘-C to copy" + else + title = "Press Ctrl-C to copy" + + $(e.trigger). + tooltip(trigger: 'manual', placement: 'auto bottom', html: true, title: title). + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) -- cgit v1.2.1 From 55c2a69f5fcde26299a5a829a146808a77c56267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Thu, 19 Nov 2015 16:22:41 -0500 Subject: Update copy used in alert message when deleting branches or tags. #2993 --- app/views/projects/branches/_branch.html.haml | 2 +- app/views/projects/tags/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index cc0ec9483d2..3f95e2a1bf6 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -26,7 +26,7 @@ Compare - if can_remove_branch?(@project, branch.name) - = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do + = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true do = icon("trash-o") - if commit diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index ebe3718afcc..879c6c7d310 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -15,7 +15,7 @@ = render 'projects/tags/download', ref: @tag.name, project: @project - if can?(current_user, :admin_project, @project) .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o .title %strong= @tag.name -- cgit v1.2.1 From 945e4293cb8f745b2e1a1aebdcf4df3eeba338cc Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 19 Nov 2015 23:40:29 +0100 Subject: Prevent unnecessary forks iteration at parent update --- app/services/projects/update_service.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 67e3429c0bd..cd7a8b18218 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -13,7 +13,7 @@ module Projects end return false unless visibility_level_allowed?(new_visibility) - update_forks_visibility_level(new_visibility) + update_forks_visibility_level(new_visibility.to_i) end new_branch = params[:default_branch] @@ -44,11 +44,13 @@ module Projects end def update_forks_visibility_level(new_level) + return unless new_level < project.visibility_level + project.forks.each do |forked_project| fork_level = forked_project.visibility_level - if fork_level > new_level.to_i - forked_project.visibility_level = new_level.to_i + if fork_level > new_level + forked_project.visibility_level = new_level forked_project.save! end end -- cgit v1.2.1 From 1144b70ab624ee1c1e7f2de0c92de021a7b5ea8e Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Fri, 20 Nov 2015 00:13:56 +0100 Subject: Change update_forks_visibility_level into after_update hook in Project model --- app/models/project.rb | 13 +++++++++++++ app/services/projects/update_service.rb | 14 -------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index 2562c441abe..b8495c6cd4f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -61,6 +61,19 @@ class Project < ActiveRecord::Base update_column(:last_activity_at, self.created_at) end + # update visibility_levet of forks + after_update :update_forks_visibility_level + def update_forks_visibility_level + return unless visibility_level < visibility_level_was + + forks.each do |forked_project| + if forked_project.visibility_level > visibility_level + forked_project.visibility_level = visibility_level + forked_project.save! + end + end + end + ActsAsTaggableOn.strict_case_match = true acts_as_taggable_on :tags diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index cd7a8b18218..895e089bea3 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -13,7 +13,6 @@ module Projects end return false unless visibility_level_allowed?(new_visibility) - update_forks_visibility_level(new_visibility.to_i) end new_branch = params[:default_branch] @@ -42,18 +41,5 @@ module Projects false end - - def update_forks_visibility_level(new_level) - return unless new_level < project.visibility_level - - project.forks.each do |forked_project| - fork_level = forked_project.visibility_level - - if fork_level > new_level - forked_project.visibility_level = new_level - forked_project.save! - end - end - end end end -- cgit v1.2.1 From 2df492fd2f5bfb6f9aa9c3f800fa4a6a39bdeeb1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 19 Nov 2015 19:25:22 -0500 Subject: Add clipboard button to merge request cross-project reference --- app/views/projects/merge_requests/_discussion.html.haml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 7e60782ff5b..cb75bd8c5ba 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -14,8 +14,10 @@ #votes= render 'votes/votes_block', votable: @merge_request = render "projects/merge_requests/show/participants" .col-md-3 - %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) + .input-group.cross-project-reference + %span.slead.has_tooltip{title: 'Cross-project reference'} + = cross_project_reference(@project, @merge_request) + = clipboard_button .row %section.col-md-9 -- cgit v1.2.1 From 97afb84b310cfdaa9305e8b79fc00bdbb866bbca Mon Sep 17 00:00:00 2001 From: Ruben Davila Date: Thu, 22 Oct 2015 10:18:59 -0500 Subject: Generate system note after Task item has been updated on Issue or Merge Request. #2296 Everytime the User check or uncheck a Task Item from the Issue or Merge Request description, a new update is going to be added to the activity logs of the Issue or Merge Request. Note that when using the edit form, you can only update the Task item status or add/delete/modify existing ones. Doing both actions is not fully supported. --- app/models/concerns/issuable.rb | 5 ++ app/models/concerns/taskable.rb | 31 +++++++++-- app/services/issuable_base_service.rb | 17 ++++++ app/services/issues/update_service.rb | 4 -- app/services/merge_requests/update_service.rb | 4 -- app/services/system_note_service.rb | 17 ++++++ config/initializers/task_list_ext.rb | 12 ++++ spec/services/issues/update_service_spec.rb | 64 ++++++++++++++++++++-- .../services/merge_requests/update_service_spec.rb | 51 +++++++++++++++-- 9 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 config/initializers/task_list_ext.rb diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 2dafb5e752f..62e8e66b1e0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -151,4 +151,9 @@ module Issuable def notes_with_associations notes.includes(:author, :project) end + + def updated_tasks + Taskable.get_updated_tasks(old_content: previous_changes['description'].first, + new_content: description) + end end diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 660e58b876d..3daa4dbe24e 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -7,14 +7,37 @@ require 'task_list/filter' # # Used by MergeRequest and Issue module Taskable + ITEM_PATTERN = / + ^ + (?:\s*[-+*]|(?:\d+\.))? # optional list prefix + \s* # optional whitespace prefix + (\[\s\]|\[[xX]\]) # checkbox + (\s.+) # followed by whitespace and some text. + /x + + def self.get_tasks(content) + content.to_s.scan(ITEM_PATTERN).map do |checkbox, label| + # ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble. + TaskList::Item.new("- #{checkbox}", label.strip) + end + end + + def self.get_updated_tasks(old_content:, new_content:) + old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content) + + new_tasks.select.with_index do |new_task, i| + old_task = old_tasks[i] + next unless old_task + + new_task.source == new_task.source && new_task.complete? != old_task.complete? + end + end + # Called by `TaskList::Summary` def task_list_items return [] if description.blank? - @task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item| - # ItemPattern strips out the hyphen, but Item requires it. Rabble rabble. - TaskList::Item.new("- #{item}") - end + @task_list_items ||= Taskable.get_tasks(description) end def tasks diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 11d2b08bba7..19055fb67ff 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -27,6 +27,12 @@ class IssuableBaseService < BaseService old_branch, new_branch) end + def create_task_status_note(issuable) + issuable.updated_tasks.each do |task| + SystemNoteService.change_task_status(issuable, issuable.project, current_user, task) + end + end + def filter_params(issuable_ability_name = :issue) params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE @@ -55,6 +61,7 @@ class IssuableBaseService < BaseService old_labels - issuable.labels) end + handle_common_system_notes(issuable) handle_changes(issuable) issuable.create_new_cross_references!(current_user) execute_hooks(issuable, 'update') @@ -71,4 +78,14 @@ class IssuableBaseService < BaseService close_service.new(project, current_user, {}).execute(issuable) end end + + def handle_common_system_notes(issuable) + if issuable.previous_changes.include?('title') + create_title_change_note(issuable, issuable.previous_changes['title'].first) + end + + if issuable.previous_changes.include?('description') && issuable.tasks? + create_task_status_note(issuable) + end + end end diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb index 7c112f731a7..a55a04dd5e0 100644 --- a/app/services/issues/update_service.rb +++ b/app/services/issues/update_service.rb @@ -13,10 +13,6 @@ module Issues create_assignee_note(issue) notification_service.reassigned_issue(issue, current_user) end - - if issue.previous_changes.include?('title') - create_title_change_note(issue, issue.previous_changes['title'].first) - end end def reopen_service diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb index a5db3776eb6..5ff2cc03dda 100644 --- a/app/services/merge_requests/update_service.rb +++ b/app/services/merge_requests/update_service.rb @@ -30,10 +30,6 @@ module MergeRequests notification_service.reassigned_merge_request(merge_request, current_user) end - if merge_request.previous_changes.include?('title') - create_title_change_note(merge_request, merge_request.previous_changes['title'].first) - end - if merge_request.previous_changes.include?('target_branch') || merge_request.previous_changes.include?('source_branch') merge_request.mark_as_unchecked diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 708c2f00486..7c5d523ef39 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -341,4 +341,21 @@ class SystemNoteService "* #{commit_ids} - #{commits_text} from branch `#{branch}`\n" end + + # Called when the status of a Task has changed + # + # noteable - Noteable object. + # project - Project owning noteable + # author - User performing the change + # new_task - TaskList::Item object. + # + # Example Note text: + # + # "Soandso marked the task Whatever as completed." + # + # Returns the created Note object + def self.change_task_status(noteable, project, author, new_task) + body = "Marked the task **#{new_task.source}** as #{new_task.status_label}" + create_note(noteable: noteable, project: project, author: author, note: body) + end end diff --git a/config/initializers/task_list_ext.rb b/config/initializers/task_list_ext.rb new file mode 100644 index 00000000000..c05b683b5be --- /dev/null +++ b/config/initializers/task_list_ext.rb @@ -0,0 +1,12 @@ +require 'task_list' + +class TaskList + class Item + COMPLETED = 'completed'.freeze + INCOMPLETE = 'incomplete'.freeze + + def status_label + complete? ? COMPLETED : INCOMPLETE + end + end +end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index f55527ee9a3..adb3aa143ae 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -15,6 +15,17 @@ describe Issues::UpdateService do end describe 'execute' do + def find_note(starting_with) + @issue.notes.find do |note| + note && note.note.start_with?(starting_with) + end + end + + def update_issue(opts) + @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + @issue.reload + end + context "valid params" do before do opts = { @@ -44,12 +55,6 @@ describe Issues::UpdateService do expect(email.subject).to include(issue.title) end - def find_note(starting_with) - @issue.notes.find do |note| - note && note.note.start_with?(starting_with) - end - end - it 'should create system note about issue reassign' do note = find_note('Reassigned to') @@ -71,5 +76,52 @@ describe Issues::UpdateService do expect(note.note).to eq 'Title changed from **Old title** to **New title**' end end + + context 'when Issue has tasks' do + before { update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + + it { expect(@issue.tasks?).to eq(true) } + + context 'when tasks are marked as completed' do + before { update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) } + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as completed') + note2 = find_note('Marked the task **Task 2** as completed') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks are marked as incomplete' do + before do + update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as incomplete') + note2 = find_note('Marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks position has been modified' do + before do + update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2" }) + end + + it 'does not create a system note' do + note = find_note('Marked the task **Task 2** as incomplete') + + expect(note).to be_nil + end + end + end + end end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 2ed51d223b7..97f5c009aec 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -14,6 +14,17 @@ describe MergeRequests::UpdateService do end describe 'execute' do + def find_note(starting_with) + @merge_request.notes.find do |note| + note && note.note.start_with?(starting_with) + end + end + + def update_merge_request(opts) + @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request) + @merge_request.reload + end + context 'valid params' do let(:opts) do { @@ -56,12 +67,6 @@ describe MergeRequests::UpdateService do expect(email.subject).to include(merge_request.title) end - def find_note(starting_with) - @merge_request.notes.find do |note| - note && note.note.start_with?(starting_with) - end - end - it 'should create system note about merge_request reassign' do note = find_note('Reassigned to') @@ -90,5 +95,39 @@ describe MergeRequests::UpdateService do expect(note.note).to eq 'Target branch changed from `master` to `target`' end end + + context 'when MergeRequest has tasks' do + before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) } + + it { expect(@merge_request.tasks?).to eq(true) } + + context 'when tasks are marked as completed' do + before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) } + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as completed') + note2 = find_note('Marked the task **Task 2** as completed') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + + context 'when tasks are marked as incomplete' do + before do + update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) + update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) + end + + it 'creates system note about task status change' do + note1 = find_note('Marked the task **Task 1** as incomplete') + note2 = find_note('Marked the task **Task 2** as incomplete') + + expect(note1).not_to be_nil + expect(note2).not_to be_nil + end + end + end + end end -- cgit v1.2.1 From f31ee525070d335aba8a189b304e3c446aedf1fb Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 20 Nov 2015 11:13:43 +0200 Subject: Fix for Emoji --- app/assets/javascripts/awards_handler.coffee | 2 +- app/controllers/projects/notes_controller.rb | 2 +- app/helpers/issues_helper.rb | 2 ++ app/services/notes/create_service.rb | 4 ++-- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index ae42e390c43..635c9b4f8d2 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -80,7 +80,7 @@ class @AwardsHandler postEmoji: (emoji, callback) -> $.post @post_emoji_url, { note: { - note: emoji + note: ":" + emoji + ":" noteable_type: @noteable_type noteable_id: @noteable_id }},(data) -> diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 263b8b8d94e..1e3f1d8fd2f 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -65,7 +65,7 @@ class Projects::NotesController < Projects::ApplicationController data = { author: current_user, is_award: true, - note: note_params[:note] + note: note_params[:note].gsub(":", '') } note = noteable.notes.find_by(data) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 2c791aa5682..493f370d9a9 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -90,6 +90,8 @@ module IssuesHelper def url_to_emoji(name) emoji_path = ::AwardEmoji.path_to_emoji_image(name) url_to_image(emoji_path) + rescue StandardError + "" end def emoji_author_list(notes, current_user) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 25a985df4d8..dbff58dfb9c 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -35,11 +35,11 @@ module Notes end def contains_emoji_only?(note) - note =~ /\A:?[-_+[:alnum:]]*:?\s?\z/ + note =~ /\A:[-_+[:alnum:]]*:\s?\z/ end def emoji_name(note) - note.match(/\A:?([-_+[:alnum:]]*):?\s?/)[1] + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] end end end -- cgit v1.2.1 From 14d95b0529eacb8f42c5184fb71bff3f326d1670 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 20 Nov 2015 11:59:32 +0100 Subject: Part of tests done [ci skip] --- lib/gitlab/lfs/response.rb | 27 ++- spec/lib/gitlab/lfs/lfs_router_spec.rb | 415 +++++++++++++++++++++++++-------- 2 files changed, 338 insertions(+), 104 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index ddadc07ebba..0371e1a7107 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -42,7 +42,7 @@ module Gitlab when "upload" render_batch_upload(request_body) else - render_forbidden + render_not_found end end @@ -322,16 +322,21 @@ module Gitlab def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| # generate links only for existing objects - next unless existing_objects.include?(object['oid']) - - object['_links'] = { - 'download' => { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", - 'header' => { - 'Authorization' => @env['HTTP_AUTHORIZATION'] - }.compact + if existing_objects.include?(object['oid']) + object['actions'] = { + 'download' => { + 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}", + 'header' => { + 'Authorization' => @env['HTTP_AUTHORIZATION'] + }.compact + } } - } + else + object['error'] = { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + end end { 'objects' => all_objects } @@ -342,7 +347,7 @@ module Gitlab # generate links only for non-existing objects next if existing_objects.include?(object['oid']) - object['_links'] = { + object['actions'] = { 'upload' => { 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}", 'header' => { diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 5eafaad79c9..b0cf38e2253 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -238,144 +238,373 @@ describe Gitlab::Lfs::Router do end end - describe 'when initiating pushing of the lfs object' do + describe 'when handling lfs batch request' do before do enable_lfs env['REQUEST_METHOD'] = 'POST' - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" + env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" end - describe 'when user is authenticated' do - before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) - end - - describe 'when user has project push access' do + describe 'download' do + describe 'when user is authenticated' do before do - @auth = authorize(user) - env["HTTP_AUTHORIZATION"] = @auth - project.team << [user, :master] + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) end - context 'when pushing an lfs object that already exists' do + describe 'when user has download access' do before do - public_project.lfs_objects << lfs_object + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :reporter] + end + + context 'when downloading an lfs object that is assigned to our project' do + before do + project.lfs_objects << lfs_object + end + + it 'responds with status 200 and href to download' do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {'Authorization' => @auth} + } + } + }]) + end + end + + context 'when downloading an lfs object that is assigned to other project' do + before do + public_project.lfs_objects << lfs_object + end + + it 'responds with status 200 and error message' do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) + end end - it "responds with status 200 and links the object to the project" do - response_body = lfs_router_auth.try_call.last - response = ActiveSupport::JSON.decode(response_body.first) + context 'when downloading a lfs object that does not exist' do + before do + body = { + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it "responds with status 200 and error message" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) + end + end + + context 'when downloading one new and one existing lfs object' do + before do + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + project.lfs_objects << lfs_object + end + + it "responds with status 200 with upload hypermedia link for the new object" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + response_body = ActiveSupport::JSON.decode(response.last.first) + + expect(response_body).to eq( + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {'Authorization' => @auth} + } + } + }]) + end + end + end + + context 'when user does is not member of the project' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :guest] + end - expect(response['objects']).to be_kind_of(Array) - expect(response['objects'].first['oid']).to eq(sample_oid) - expect(response['objects'].first['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response['objects'].first).to have_key('_links') + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) end end - context 'when pushing a lfs object that does not exist' do + context 'when user does not have download access' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) - end - - it "responds with status 200 and upload hypermedia link" do - response = lfs_router_auth.try_call - expect(response.first).to eq(200) + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :guest] + end - response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body['objects']).to be_kind_of(Array) - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(response_body['objects'].first['size']).to eq(1575078) - expect(lfs_object.projects.pluck(:id)).not_to include(project.id) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) end end + end + + context 'when user is not authenticated' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'download' + }.to_json + env['rack.input'] = StringIO.new(body) + end - context 'when pushing one new and one existing lfs object' do + describe 'is accessing public project' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'upload' - }.to_json - env['rack.input'] = StringIO.new(body) public_project.lfs_objects << lfs_object end - it "responds with status 200 with upload hypermedia link for the new object" do - response = lfs_router_auth.try_call + it 'responds with status 200 and href to download' do + response = lfs_router_public_noauth.try_call expect(response.first).to eq(200) - response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body['objects']).to be_kind_of(Array) + expect(response_body).to eq( + 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } + } + }]) + end + end - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(response_body['objects'].first['size']).to eq(1575078) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + describe 'is accessing non-public project' do + before do + project.lfs_objects << lfs_object + end - expect(response_body['objects'].last['oid']).to eq(sample_oid) - expect(response_body['objects'].last['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response_body['objects'].last).to have_key('_links') + it 'responds with authorization required' do + expect(lfs_router_noauth.try_call.first).to eq(401) end end end + end - context 'when user does not have push access' do - it 'responds with 403' do - expect(lfs_router_auth.try_call.first).to eq(403) + describe 'upload' do + describe 'when user is authenticated' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) end - end - end - context 'when user is not authenticated' do - before do - env['rack.input'] = StringIO.new( - { 'objects' => [], 'operation' => 'upload' }.to_json - ) + describe 'when user has project push access' do + before do + @auth = authorize(user) + env["HTTP_AUTHORIZATION"] = @auth + project.team << [user, :master] + end + + context 'when pushing an lfs object that already exists' do + before do + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 and links the object to the project" do + response_body = lfs_router_auth.try_call.last + response = ActiveSupport::JSON.decode(response_body.first) + + expect(response['objects']).to be_kind_of(Array) + expect(response['objects'].first['oid']).to eq(sample_oid) + expect(response['objects'].first['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response['objects'].first).to have_key('_links') + end + end + + context 'when pushing a lfs object that does not exist' do + before do + body = { + 'objects' => [{ + 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it "responds with status 200 and upload hypermedia link" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(lfs_object.projects.pluck(:id)).not_to include(project.id) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + end + end + + context 'when pushing one new and one existing lfs object' do + before do + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + public_project.lfs_objects << lfs_object + end + + it "responds with status 200 with upload hypermedia link for the new object" do + response = lfs_router_auth.try_call + expect(response.first).to eq(200) + + response_body = ActiveSupport::JSON.decode(response.last.first) + expect(response_body['objects']).to be_kind_of(Array) + + + expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") + expect(response_body['objects'].first['size']).to eq(1575078) + expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + + expect(response_body['objects'].last['oid']).to eq(sample_oid) + expect(response_body['objects'].last['size']).to eq(sample_size) + expect(lfs_object.projects.pluck(:id)).to_not include(project.id) + expect(lfs_object.projects.pluck(:id)).to include(public_project.id) + expect(response_body['objects'].last).to have_key('_links') + end + end + end + + context 'when user does not have push access' do + it 'responds with 403' do + expect(lfs_router_auth.try_call.first).to eq(403) + end + end end - context 'when user has push access' do + context 'when user is not authenticated' do before do - project.team << [user, :master] + env['rack.input'] = StringIO.new( + { 'objects' => [], 'operation' => 'upload' }.to_json + ) end - it "responds with status 401" do - expect(lfs_router_public_noauth.try_call.first).to eq(401) + context 'when user has push access' do + before do + project.team << [user, :master] + end + + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end end - end - context 'when user does not have push access' do - it "responds with status 401" do - expect(lfs_router_public_noauth.try_call.first).to eq(401) + context 'when user does not have push access' do + it "responds with status 401" do + expect(lfs_router_public_noauth.try_call.first).to eq(401) + end end end end + + describe 'unsupported' do + before do + body = { 'objects' => [{ + 'oid' => sample_oid, + 'size' => sample_size + }], + 'operation' => 'other' + }.to_json + env['rack.input'] = StringIO.new(body) + end + + it 'responds with status 404' do + expect(lfs_router_public_noauth.try_call.first).to eq(404) + end + end end describe 'when pushing a lfs object' do -- cgit v1.2.1 From b8d8292bef996a5d074b21a82d4faec0edb2e2bf Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 20 Nov 2015 12:21:41 +0100 Subject: Fix upload tests, reformat code and make rubocop happy --- lib/gitlab/lfs/response.rb | 3 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 229 ++++++++++++++++----------------- 2 files changed, 110 insertions(+), 122 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index 0371e1a7107..ada518f9e04 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -321,7 +321,6 @@ module Gitlab def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - # generate links only for existing objects if existing_objects.include?(object['oid']) object['actions'] = { 'download' => { @@ -344,7 +343,7 @@ module Gitlab def upload_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| - # generate links only for non-existing objects + # generate actions only for non-existing objects next if existing_objects.include?(object['oid']) object['actions'] = { diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index b0cf38e2253..f898b76eb5f 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -248,11 +248,11 @@ describe Gitlab::Lfs::Router do describe 'download' do describe 'when user is authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] }.to_json env['rack.input'] = StringIO.new(body) end @@ -274,17 +274,16 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, 'size' => sample_size, 'actions' => { 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {'Authorization' => @auth} + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => @auth } + } } - } - }]) + }]) end end @@ -298,26 +297,24 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) end end context 'when downloading a lfs object that does not exist' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }] }.to_json env['rack.input'] = StringIO.new(body) end @@ -327,30 +324,28 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }]) end end context 'when downloading one new and one existing lfs object' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ] }.to_json env['rack.input'] = StringIO.new(body) project.lfs_objects << lfs_object @@ -361,25 +356,23 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078, - 'error' => { - 'code' => 404, - 'message' => "Object does not exist on the server or you don't have permissions to access it", - } - }, - { - 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {'Authorization' => @auth} - } - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078, + 'error' => { + 'code' => 404, + 'message' => "Object does not exist on the server or you don't have permissions to access it", + } + }, + { 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => { 'Authorization' => @auth } + } + } + }]) end end end @@ -411,11 +404,12 @@ describe Gitlab::Lfs::Router do context 'when user is not authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'download' + body = { 'operation' => 'download', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }], + }.to_json env['rack.input'] = StringIO.new(body) end @@ -430,17 +424,16 @@ describe Gitlab::Lfs::Router do expect(response.first).to eq(200) response_body = ActiveSupport::JSON.decode(response.last.first) - expect(response_body).to eq( - 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size, - 'actions' => { - 'download' => { - 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", - 'header' => {} - } - } - }]) + expect(response_body).to eq('objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size, + 'actions' => { + 'download' => { + 'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}", + 'header' => {} + } + } + }]) end end @@ -459,12 +452,12 @@ describe Gitlab::Lfs::Router do describe 'upload' do describe 'when user is authenticated' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'upload' - }.to_json + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] + }.to_json env['rack.input'] = StringIO.new(body) end @@ -472,7 +465,7 @@ describe Gitlab::Lfs::Router do before do @auth = authorize(user) env["HTTP_AUTHORIZATION"] = @auth - project.team << [user, :master] + project.team << [user, :developer] end context 'when pushing an lfs object that already exists' do @@ -489,19 +482,19 @@ describe Gitlab::Lfs::Router do expect(response['objects'].first['size']).to eq(sample_size) expect(lfs_object.projects.pluck(:id)).to_not include(project.id) expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response['objects'].first).to have_key('_links') + expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}") + expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) end end context 'when pushing a lfs object that does not exist' do before do - body = { - 'objects' => [{ - 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }], - 'operation' => 'upload' - }.to_json + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }] + }.to_json env['rack.input'] = StringIO.new(body) end @@ -514,26 +507,25 @@ describe Gitlab::Lfs::Router do expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(response_body['objects'].first['size']).to eq(1575078) expect(lfs_object.projects.pluck(:id)).not_to include(project.id) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth) end end context 'when pushing one new and one existing lfs object' do before do - body = { - 'objects' => [ - { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', - 'size' => 1575078 - }, - { 'oid' => sample_oid, - 'size' => sample_size - } - ], - 'operation' => 'upload' + body = { 'operation' => 'upload', + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ] }.to_json env['rack.input'] = StringIO.new(body) - public_project.lfs_objects << lfs_object + project.lfs_objects << lfs_object end it "responds with status 200 with upload hypermedia link for the new object" do @@ -543,17 +535,14 @@ describe Gitlab::Lfs::Router do response_body = ActiveSupport::JSON.decode(response.last.first) expect(response_body['objects']).to be_kind_of(Array) - expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") expect(response_body['objects'].first['size']).to eq(1575078) - expect(response_body['objects'].first['_links']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") - expect(response_body['objects'].first['_links']['upload']['header']).to eq("Authorization" => @auth) + expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078") + expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth) expect(response_body['objects'].last['oid']).to eq(sample_oid) expect(response_body['objects'].last['size']).to eq(sample_size) - expect(lfs_object.projects.pluck(:id)).to_not include(project.id) - expect(lfs_object.projects.pluck(:id)).to include(public_project.id) - expect(response_body['objects'].last).to have_key('_links') + expect(response_body['objects'].last).to_not have_key('actions') end end end @@ -592,11 +581,11 @@ describe Gitlab::Lfs::Router do describe 'unsupported' do before do - body = { 'objects' => [{ - 'oid' => sample_oid, - 'size' => sample_size - }], - 'operation' => 'other' + body = { 'operation' => 'other', + 'objects' => [ + { 'oid' => sample_oid, + 'size' => sample_size + }] }.to_json env['rack.input'] = StringIO.new(body) end -- cgit v1.2.1 From dbc0be1b275e92af5432cf7c5cf44de3dc8c9ca5 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 20 Nov 2015 12:45:30 +0100 Subject: Error 501 when client is using deprecated API. --- lib/gitlab/lfs/response.rb | 60 +++-------- lib/gitlab/lfs/router.rb | 4 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 180 +++++++-------------------------- 3 files changed, 49 insertions(+), 195 deletions(-) diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index ada518f9e04..aceef53ba60 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -10,20 +10,6 @@ module Gitlab @request = request end - # Return a response for a download request - # Can be a response to: - # Request from a user to get the file - # Request from gitlab-workhorse which file to serve to the user - def render_download_hypermedia_response(oid) - render_response_to_download do - if check_download_accept_header? - render_lfs_download_hypermedia(oid) - else - render_not_found - end - end - end - def render_download_object_response(oid) render_response_to_download do if check_download_sendfile_header? @@ -66,13 +52,24 @@ module Gitlab end end + def render_unsupported_deprecated_api + [ + 501, + { "Content-Type" => "application/json; charset=utf-8" }, + [JSON.dump({ + 'message' => 'Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.', + 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", + })] + ] + end + private def render_not_enabled [ 501, { - "Content-Type" => "application/vnd.git-lfs+json", + "Content-Type" => "application/json; charset=utf-8", }, [JSON.dump({ 'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.', @@ -169,21 +166,6 @@ module Gitlab end end - def render_lfs_download_hypermedia(oid) - return render_not_found unless oid.present? - - lfs_object = object_for_download(oid) - if lfs_object - [ - 200, - { "Content-Type" => "application/vnd.git-lfs+json" }, - [JSON.dump(download_hypermedia(oid))] - ] - else - render_not_found - end - end - def render_lfs_upload_ok(oid, size, tmp_file) if store_file(oid, size, tmp_file) [ @@ -226,10 +208,6 @@ module Gitlab @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile" end - def check_download_accept_header? - @env['HTTP_ACCEPT'].to_s == "application/vnd.git-lfs+json; charset=utf-8" - end - def user_can_fetch? # Check user access against the project they used to initiate the pull @user.can?(:download_code, @origin_project) @@ -305,20 +283,6 @@ module Gitlab download_hypermedia_links(objects, selected_objects) end - def download_hypermedia(oid) - { - '_links' => { - 'download' => - { - 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}", - 'header' => { - 'Authorization' => @env['HTTP_AUTHORIZATION'] - }.compact - } - } - } - end - def download_hypermedia_links(all_objects, existing_objects) all_objects.each do |object| if existing_objects.include?(object['oid']) diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb index 2b3f2e8e958..78d02891102 100644 --- a/lib/gitlab/lfs/router.rb +++ b/lib/gitlab/lfs/router.rb @@ -34,7 +34,7 @@ module Gitlab case path_match[1] when "info/lfs" - lfs.render_download_hypermedia_response(oid) + lfs.render_unsupported_deprecated_api when "gitlab-lfs" lfs.render_download_object_response(oid) else @@ -49,6 +49,8 @@ module Gitlab # Check for Batch API if post_path[0].ends_with?("/info/lfs/objects/batch") lfs.render_batch_operation_response + elsif post_path[0].ends_with?("/info/lfs/objects") + lfs.render_unsupported_deprecated_api else nil end diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index f898b76eb5f..92598559aea 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -26,113 +26,52 @@ describe Gitlab::Lfs::Router do let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" } let(:sample_size) { 499013 } + let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} + let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} describe 'when lfs is disabled' do before do allow(Gitlab.config.lfs).to receive(:enabled).and_return(false) - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" + env['REQUEST_METHOD'] = 'POST' + body = { + 'objects' => [ + { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', + 'size' => 1575078 + }, + { 'oid' => sample_oid, + 'size' => sample_size + } + ], + 'operation' => 'upload' + }.to_json + env['rack.input'] = StringIO.new(body) + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch" end it 'responds with 501' do - respond_with_disabled = [ 501, - { "Content-Type"=>"application/vnd.git-lfs+json" }, - ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"] - ] expect(lfs_router_auth.try_call).to match_array(respond_with_disabled) end end - describe 'when fetching lfs object' do + describe 'when fetching lfs object using deprecated API' do before do enable_lfs - env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" end - describe 'when user is authenticated' do - context 'and user has project download access' do - before do - @auth = authorize(user) - env["HTTP_AUTHORIZATION"] = @auth - project.lfs_objects << lfs_object - project.team << [user, :master] - end - - it "responds with status 200" do - expect(lfs_router_auth.try_call.first).to eq(200) - end - - it "responds with download hypermedia" do - json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) - end - end - - context 'and user does not have project access' do - it "responds with status 403" do - expect(lfs_router_auth.try_call.first).to eq(403) - end - end - end - - describe 'when user is unauthenticated' do - context 'and user does not have download access' do - it "responds with status 401" do - expect(lfs_router_noauth.try_call.first).to eq(401) - end - end - - context 'and user has download access' do - before do - project.team << [user, :master] - end - - it "responds with status 401" do - expect(lfs_router_noauth.try_call.first).to eq(401) - end - end + it 'responds with 501' do + expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) end + end - describe 'and project is public' do - context 'and project has access to the lfs object' do - before do - public_project.lfs_objects << lfs_object - end - - context 'and user is authenticated' do - it "responds with status 200 and sends download hypermedia" do - expect(lfs_router_public_auth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_public_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - - context 'and user is unauthenticated' do - it "responds with status 200 and sends download hypermedia" do - expect(lfs_router_public_noauth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_public_noauth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{public_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - end - - context 'and project does not have access to the lfs object' do - it "responds with status 404" do - expect(lfs_router_public_auth.try_call.first).to eq(404) - end - end + describe 'when fetching lfs object' do + before do + enable_lfs + env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" end describe 'and request comes from gitlab-workhorse' do - before do - env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}" - end context 'without user being authorized' do it "responds with status 401" do expect(lfs_router_noauth.try_call.first).to eq(401) @@ -173,68 +112,17 @@ describe Gitlab::Lfs::Router do end end end + end - describe 'from a forked public project' do - before do - env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8" - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}" - end - - context "when fetching a lfs object" do - context "and user has project download access" do - before do - public_project.lfs_objects << lfs_object - end - - it "can download the lfs object" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) - - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}") - expect(json_response['_links']['download']['header']).to eq({}) - end - end - - context "and user is not authenticated but project is public" do - before do - public_project.lfs_objects << lfs_object - end - - it "can download the lfs object" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - end - end - - context "and user has project download access" do - before do - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897" - @auth = ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password) - env["HTTP_AUTHORIZATION"] = @auth - lfs_object_two = create(:lfs_object, :with_file, oid: "91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", size: 1575078) - public_project.lfs_objects << lfs_object_two - end - - it "can get a lfs object that is not in the forked project" do - expect(lfs_router_forked_auth.try_call.first).to eq(200) - - json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first) - expect(json_response['_links']['download']['href']).to eq("#{Gitlab.config.gitlab.url}/#{forked_project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897") - expect(json_response['_links']['download']['header']).to eq("Authorization" => @auth) - end - end - - context "and user has project download access" do - before do - env["PATH_INFO"] = "#{forked_project.repository.path_with_namespace}.git/info/lfs/objects/267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b" - lfs_object_three = create(:lfs_object, :with_file, oid: "267c8b1d876743971e3a9978405818ff5ca731c4c870b06507619cd9b1847b6b", size: 127192524) - project.lfs_objects << lfs_object_three - end + describe 'when handling lfs request using deprecated API' do + before do + enable_lfs + env['REQUEST_METHOD'] = 'POST' + env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects" + end - it "cannot get a lfs object that is not in the project" do - expect(lfs_router_forked_auth.try_call.first).to eq(404) - end - end - end + it 'responds with 501' do + expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated) end end -- cgit v1.2.1 From 5a4c56c38dd7aef414582edb880b343bf67b65b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 19 Nov 2015 14:49:35 +0100 Subject: Reduce method complexity in AutocompleteController --- app/controllers/autocomplete_controller.rb | 49 +++++++++++------------- spec/controllers/autocomplete_controller_spec.rb | 9 +++-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 202e9da9eee..aa0268b8d62 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -1,34 +1,8 @@ class AutocompleteController < ApplicationController skip_before_action :authenticate_user!, only: [:users] + before_action :find_users, only: [:users] def users - begin - @users = - if params[:project_id].present? - project = Project.find(params[:project_id]) - - if can?(current_user, :read_project, project) - project.team.users - end - elsif params[:group_id] - group = Group.find(params[:group_id]) - - if can?(current_user, :read_group, group) - group.users - end - elsif current_user - User.all - end - rescue ActiveRecord::RecordNotFound - if current_user - return render json: {}, status: 404 - end - end - - if @users.nil? && current_user.nil? - authenticate_user! - end - @users ||= User.none @users = @users.search(params[:search]) if params[:search].present? @users = @users.active @@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController @user = User.find(params[:id]) render json: @user, only: [:name, :username, :id], methods: [:avatar_url] end + + private + + def find_users + @users = + if params[:project_id].present? + project = Project.find(params[:project_id]) + return render_404 unless can?(current_user, :read_project, project) + + project.team.users + elsif params[:group_id].present? + group = Group.find(params[:group_id]) + return render_404 unless can?(current_user, :read_group, group) + + group.users + elsif current_user + User.all + else + User.none + end + end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index aa8d6cb807f..85379a8e984 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -114,7 +114,7 @@ describe AutocompleteController do get(:users, project_id: project.id) end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with unknown project' do @@ -122,7 +122,7 @@ describe AutocompleteController do get(:users, project_id: 'unknown') end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with inaccessible group' do @@ -131,7 +131,7 @@ describe AutocompleteController do get(:users, group_id: user.namespace.id) end - it { expect(response.status).to eq(302) } + it { expect(response.status).to eq(404) } end describe 'GET #users with no project' do @@ -139,7 +139,8 @@ describe AutocompleteController do get(:users) end - it { expect(response.status).to eq(302) } + it { expect(body).to be_kind_of(Array) } + it { expect(body.size).to eq 0 } end end end -- cgit v1.2.1 From fed059a12ddde628a7d19d008c199da00990ce19 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 20 Nov 2015 15:48:05 +0100 Subject: Port GitLab EE ProjectsFinder changes These changes were added in GitLab EE commit d39de0ea91b26b8840195e5674b92c353cc16661. The tests were a bit bugged (they used a non existing group, thus not testing a crucial part) which I only noticed when porting CE changes to EE. --- app/finders/projects_finder.rb | 10 +++++----- spec/finders/projects_finder_spec.rb | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index dd35c215c50..3b4e0362e04 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -23,17 +23,17 @@ class ProjectsFinder group = options[:group] if group - base, extra = group_projects(current_user, group) + segments = group_projects(current_user, group) else - base, extra = all_projects(current_user) + segments = all_projects(current_user) end - if base and extra - union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) + if segments.length > 1 + union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) }) Project.where("projects.id IN (#{union.to_sql})") else - base + segments.first end end diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb index d1dede78f74..f32641ef0f6 100644 --- a/spec/finders/projects_finder_spec.rb +++ b/spec/finders/projects_finder_spec.rb @@ -3,10 +3,19 @@ require 'spec_helper' describe ProjectsFinder do describe '#execute' do let(:user) { create(:user) } + let(:group) { create(:group) } - let!(:private_project) { create(:project, :private) } - let!(:internal_project) { create(:project, :internal) } - let!(:public_project) { create(:project, :public) } + let!(:private_project) do + create(:project, :private, name: 'A', path: 'A') + end + + let!(:internal_project) do + create(:project, :internal, group: group, name: 'B', path: 'B') + end + + let!(:public_project) do + create(:project, :public, group: group, name: 'C', path: 'C') + end let(:finder) { described_class.new } @@ -38,8 +47,6 @@ describe ProjectsFinder do end describe 'with a group' do - let(:group) { public_project.group } - describe 'without a user' do subject { finder.execute(nil, group: group) } -- cgit v1.2.1 From 899807f604f7923cc40a64cdd0f61c4248b8870d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 20 Nov 2015 16:10:08 +0100 Subject: Update required version of lfs client and separate the docs for users and admins. --- doc/README.md | 1 + doc/workflow/README.md | 1 + doc/workflow/git_lfs.md | 135 --------------------- doc/workflow/lfs/lfs_administration.md | 41 +++++++ .../lfs/manage_large_binaries_with_git_lfs.md | 125 +++++++++++++++++++ lib/gitlab/lfs/response.rb | 2 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 2 +- 7 files changed, 170 insertions(+), 137 deletions(-) delete mode 100644 doc/workflow/git_lfs.md create mode 100644 doc/workflow/lfs/lfs_administration.md create mode 100644 doc/workflow/lfs/manage_large_binaries_with_git_lfs.md diff --git a/doc/README.md b/doc/README.md index 0f6866475f7..58ab5dd08e0 100644 --- a/doc/README.md +++ b/doc/README.md @@ -50,6 +50,7 @@ - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails. - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE. +- [Git LFS configuration](workflow/lfs/lfs_administration.md) ## Contributor documentation diff --git a/doc/workflow/README.md b/doc/workflow/README.md index c1a4f64981f..a6b4d951188 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -17,3 +17,4 @@ - [Milestones](milestones.md) - [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) +- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md) diff --git a/doc/workflow/git_lfs.md b/doc/workflow/git_lfs.md deleted file mode 100644 index 616a71522ae..00000000000 --- a/doc/workflow/git_lfs.md +++ /dev/null @@ -1,135 +0,0 @@ -# Git LFS - -Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. -The general recommendation is to not have Git repositories larger than 1GB to preserve performance. - -GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain -environments it is not always convenient to use different commands to differentiate between the large files and regular ones. - -Git LFS makes this simpler for the end user by removing the requirement to learn new commands. - - -## How it works - -Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. -Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. - -## Requirements - -* Git LFS is supported in GitLab starting with version 8.2 -* Git LFS [client](https://git-lfs.github.com) version 0.6.0 and up - -## GitLab and Git LFS - -### Configuration - -Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. - -There are two configuration options to help GitLab server administrators: - -* Enabling/disabling Git LFS support -* Changing the location of LFS object storage - -#### Omnibus packages - -In `/etc/gitlab/gitlab.rb`: - -```ruby -gitlab_rails['lfs_enabled'] = false -gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects" -``` - -#### Installations from source - -In `config/gitlab.yml`: - -```yaml - lfs: - enabled: false - storage_path: /mnt/storage/lfs-objects -``` - -## Known limitations - -* Git LFS v1 original API is not supported since it was deprecated early in LFS development, starting with Git LFS version 0.6.0 -* When SSH is set as a remote, Git LFS objects still go through HTTPS -* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended -* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported -* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting-tips) - -## Using Git LFS - -Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: -For example, if you want to upload a very large file and check it into your Git repository: - -```bash -git clone git@gitlab.example.com:group/project.git -git lfs init # initialize the Git LFS project project -git lfs track "*.iso" # select the file extensions that you want to treat as large files -``` - -Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: - -```bash -cp ~/tmp/debian.iso ./ # copy a large file into the current directory -git add . # add the large file to the project -git commit -am "Added Debian iso" # commit the file meta data -git push origin master # sync the git repo and large file to the GitLab server -``` - -Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. - -```bash -git clone git@gitlab.example.com:group/project.git -``` - - -## Troubleshooting - -### error: Repository or object not found - -There are a couple of reasons why this error can occur: - -* Wrong version of LFS client used: - -Check the version of Git LFS on the client machine with `git lfs version`. Only version 0.6.0 and newer are supported. - -* Project is using deprecated LFS API - -Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try using Git LFS client newer than 0.6.0. - -### Invalid status for : 501 - -When attempting to push a LFS object to a GitLab server that doesn't have Git LFS support enabled, server will return status `error 501`. Check with your GitLab administrator why Git LFS is not enabled on the server. See [Configuration section](#configuration) for instructions on how to enable LFS support. - -### getsockopt: connection refused - -If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, -the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. - -This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. - -To prevent this from happening, set the lfs url in project Git config: - -```bash - -git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch" -``` - -### Credentials are always required when pushing an object - -Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. - -By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). - -For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: - -```bash -git config --global credential.helper 'cache --timeout=3600' -``` - -This will remember the credentials for an hour after which Git operations will require re-authentication. - -If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. - -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage) \ No newline at end of file diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md new file mode 100644 index 00000000000..9fa307a9d5b --- /dev/null +++ b/doc/workflow/lfs/lfs_administration.md @@ -0,0 +1,41 @@ +## GitLab Git LFS Administration + +Documentation on how to use Git LFS are under [Managing large binary files with Git LFS doc](manage_large_binaries_with_git_lfs.md). + +## Requirements + +* Git LFS is supported in GitLab starting with version 8.2. +* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up + +## Configuration + +Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on. + +There are two configuration options to help GitLab server administrators: + +* Enabling/disabling Git LFS support +* Changing the location of LFS object storage + +### Omnibus packages + +In `/etc/gitlab/gitlab.rb`: + +```ruby +gitlab_rails['lfs_enabled'] = false +gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects" +``` + +### Installations from source + +In `config/gitlab.yml`: + +```yaml + lfs: + enabled: false + storage_path: /mnt/storage/lfs-objects +``` + +## Known limitations + +* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported +* Currently, removing LFS objects from GitLab Git LFS storage is not supported diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md new file mode 100644 index 00000000000..a93fd3dfb13 --- /dev/null +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -0,0 +1,125 @@ +# Git LFS + +Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git. +The general recommendation is to not have Git repositories larger than 1GB to preserve performance. + +GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain +environments it is not always convenient to use different commands to differentiate between the large files and regular ones. + +Git LFS makes this simpler for the end user by removing the requirement to learn new commands. + +## How it works + +Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests. +Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file. + +## GitLab server configuration + +Documentation for GitLab instance administrators is under [LFS administration doc](lfs_administration.md). + +## Requirements + +* Git LFS is supported in GitLab starting with version 8.2 +* [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up + +## Known limitations + +* Git LFS v1 original API is not supported since it was deprecated early in LFS development +* When SSH is set as a remote, Git LFS objects still go through HTTPS +* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended +* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting) + +## Using Git LFS + +Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS: +For example, if you want to upload a very large file and check it into your Git repository: + +```bash +git clone git@gitlab.example.com:group/project.git +git lfs init # initialize the Git LFS project project +git lfs track "*.iso" # select the file extensions that you want to treat as large files +``` + +Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension: + +```bash +cp ~/tmp/debian.iso ./ # copy a large file into the current directory +git add . # add the large file to the project +git commit -am "Added Debian iso" # commit the file meta data +git push origin master # sync the git repo and large file to the GitLab server +``` + +Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication. + +```bash +git clone git@gitlab.example.com:group/project.git +``` + +If you already cloned the repository and you want to get the latest LFS object that are on the remote repository, eg. from branch `master`: + +```bash +git lfs fetch master +``` + +## Troubleshooting + +### error: Repository or object not found + +There are a couple of reasons why this error can occur: + +* You don't have permissions to access certain LFS object + +Check if you have permissions to push to the project or fetch from the project. + +* Project is not allowed to access the LFS object + +Check if the LFS object you are trying to push to the project or fetch from the project is available to the project. + +* Project is using deprecated LFS API + +### Invalid status for : 501 + +Git LFS will log the failures into a log file. +To view this log file, while in project directory: + +```bash +git lfs logs last +``` + +If the status `error 501` is shown, it is because: + +* Git LFS support is not enabled on the GitLab server. Check with your GitLab administrator why Git LFS is not enabled on the server. See [LFS administration documentation](lfs_administration.md) for instructions on how to enable LFS support. + +* Git LFS client version is not supported by GitLab server. Check your Git LFS version with `git lfs version`. Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try to update your Git LFS client. Only version 1.0.1 and newer are supported. + +### getsockopt: connection refused + +If you push a LFS object to a project and you receive an error similar to: `Post /info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`, +the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP. + +This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config. + +To prevent this from happening, set the lfs url in project Git config: + +```bash + +git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch" +``` + +### Credentials are always required when pushing an object + +Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required. + +By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials). + +For example, you can tell Git to remember the password for a period of time in which you expect to push the objects: + +```bash +git config --global credential.helper 'cache --timeout=3600' +``` + +This will remember the credentials for an hour after which Git operations will require re-authentication. + +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. + +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index aceef53ba60..c18dfbd485d 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -57,7 +57,7 @@ module Gitlab 501, { "Content-Type" => "application/json; charset=utf-8" }, [JSON.dump({ - 'message' => 'Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.', + 'message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.', 'documentation_url' => "#{Gitlab.config.gitlab.url}/help", })] ] diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb index 92598559aea..5b13d65c7c0 100644 --- a/spec/lib/gitlab/lfs/lfs_router_spec.rb +++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::Lfs::Router do let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" } let(:sample_size) { 499013 } - let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 0.6.0 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} + let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]} describe 'when lfs is disabled' do -- cgit v1.2.1 From fc18e96db38f5d5ab5e102fe630682f6779203ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 10:36:12 -0500 Subject: Refactor creation of system notes for Issue/MR labels. #2296 --- app/services/issuable_base_service.rb | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 19055fb67ff..2556f06e2d3 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -53,15 +53,7 @@ class IssuableBaseService < BaseService if params.present? && issuable.update_attributes(params.merge(updated_by: current_user)) issuable.reset_events_cache - - if issuable.labels != old_labels - create_labels_note( - issuable, - issuable.labels - old_labels, - old_labels - issuable.labels) - end - - handle_common_system_notes(issuable) + handle_common_system_notes(issuable, old_labels: old_labels) handle_changes(issuable) issuable.create_new_cross_references!(current_user) execute_hooks(issuable, 'update') @@ -79,7 +71,7 @@ class IssuableBaseService < BaseService end end - def handle_common_system_notes(issuable) + def handle_common_system_notes(issuable, options = {}) if issuable.previous_changes.include?('title') create_title_change_note(issuable, issuable.previous_changes['title'].first) end @@ -87,5 +79,10 @@ class IssuableBaseService < BaseService if issuable.previous_changes.include?('description') && issuable.tasks? create_task_status_note(issuable) end + + old_labels = options[:old_labels] + if old_labels && (issuable.labels != old_labels) + create_labels_note(issuable, issuable.labels - old_labels, old_labels - issuable.labels) + end end end -- cgit v1.2.1 From fa9f2dec0e07ff3ae3a2acd6ee0586e317bdb7b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 10:49:12 -0500 Subject: Monkey patching TaskList::Item is no longer required. #2296 --- app/models/concerns/taskable.rb | 2 ++ app/services/system_note_service.rb | 3 ++- config/initializers/task_list_ext.rb | 12 ------------ 3 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 config/initializers/task_list_ext.rb diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 3daa4dbe24e..de7588fea86 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -7,6 +7,8 @@ require 'task_list/filter' # # Used by MergeRequest and Issue module Taskable + COMPLETED = 'completed'.freeze + INCOMPLETE = 'incomplete'.freeze ITEM_PATTERN = / ^ (?:\s*[-+*]|(?:\d+\.))? # optional list prefix diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7c5d523ef39..7e2bc834176 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -355,7 +355,8 @@ class SystemNoteService # # Returns the created Note object def self.change_task_status(noteable, project, author, new_task) - body = "Marked the task **#{new_task.source}** as #{new_task.status_label}" + status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE + body = "Marked the task **#{new_task.source}** as #{status_label}" create_note(noteable: noteable, project: project, author: author, note: body) end end diff --git a/config/initializers/task_list_ext.rb b/config/initializers/task_list_ext.rb deleted file mode 100644 index c05b683b5be..00000000000 --- a/config/initializers/task_list_ext.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'task_list' - -class TaskList - class Item - COMPLETED = 'completed'.freeze - INCOMPLETE = 'incomplete'.freeze - - def status_label - complete? ? COMPLETED : INCOMPLETE - end - end -end -- cgit v1.2.1 From ad1f451f249692f9ed27d3f27ee712178cb30742 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Nov 2015 08:13:25 -0800 Subject: Fix Drone web hook URL not being updated --- app/models/project_services/drone_ci_service.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index c240213200d..127684bd274 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -32,6 +32,8 @@ class DroneCiService < CiService def compose_service_hook hook = service_hook || build_service_hook + # If using a service template, project may not be available + hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project hook.enable_ssl_verification = enable_ssl_verification hook.save end -- cgit v1.2.1 From 3aabed3456506d1a917e6daba29cd46ce6a25dab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 13:58:45 -0500 Subject: Fix bug that happened when replacing the Task list. #2296 REF: https://gitlab.com/gitlab-org/gitlab-ce/issues/2296#note_2724697 --- app/models/concerns/taskable.rb | 2 +- spec/services/issues/update_service_spec.rb | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index de7588fea86..df2a9e3e84b 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -31,7 +31,7 @@ module Taskable old_task = old_tasks[i] next unless old_task - new_task.source == new_task.source && new_task.complete? != old_task.complete? + new_task.source == old_task.source && new_task.complete? != old_task.complete? end end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index adb3aa143ae..bc6a26416a2 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -121,6 +121,25 @@ describe Issues::UpdateService do expect(note).to be_nil end end + + context 'when a Task list with a completed item is totally replaced' do + before do + update_issue({ description: "- [ ] Task 1\n- [X] Task 2" }) + update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) + end + + it 'does not create a system note referencing the position the old item' do + note = find_note('Marked the task **Two** as incomplete') + + expect(note).to be_nil + end + + it 'should not generate a new note at all' do + expect { + update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) + }.not_to change { Note.count } + end + end end end -- cgit v1.2.1 From 976cebe45681766764509c3b4df630ced6cc6e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 14:27:31 -0500 Subject: Little fix for Rubocop's complaints. #2296 --- spec/services/issues/update_service_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index bc6a26416a2..cc3e6483261 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -135,9 +135,9 @@ describe Issues::UpdateService do end it 'should not generate a new note at all' do - expect { + expect do update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" }) - }.not_to change { Note.count } + end.not_to change { Note.count } end end end -- cgit v1.2.1 From d22435f3ffe005bddcd0ca24137d55cec97c84fb Mon Sep 17 00:00:00 2001 From: Greg Smethells Date: Fri, 20 Nov 2015 14:15:01 -0600 Subject: fix syntax error in common.scss --- app/assets/stylesheets/framework/common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 40f4beb1968..61689aff57e 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -64,7 +64,7 @@ pre { .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { background: $gl-primary; - color: #FFF + color: #FFF; } .str-truncated { -- cgit v1.2.1 From 2ed48df79af82313539fc0bf5dceac2785350262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 20 Nov 2015 16:50:48 -0500 Subject: Remove accidentally added line. #3598 It should exist in EE only. --- app/workers/repository_import_worker.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 1de49161997..d18c0706b30 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -51,8 +51,5 @@ class RepositoryImportWorker end project.import_finish - - # Explicitly update mirror so that upstream remote is created and fetched - project.update_mirror end end -- cgit v1.2.1 From d496a6b919abd32252f54e59d5607956005cfb15 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Fri, 20 Nov 2015 23:43:10 +0100 Subject: Handle removed source projects in MR CI commits When calling MergeRequest#ci_commit the code would previously raise an error if the source project no longer existed (e.g. because the user removed their fork). See #3599 for more information. --- app/models/merge_request.rb | 2 +- spec/models/merge_request_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1e8d9908f0a..1b3d6079d2c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -476,7 +476,7 @@ class MergeRequest < ActiveRecord::Base end def ci_commit - if last_commit + if last_commit and source_project source_project.ci_commit(last_commit.id) end end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 90af75ff0e3..567c911425c 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -193,4 +193,29 @@ describe MergeRequest do it_behaves_like 'a Taskable' do subject { create :merge_request, :simple } end + + describe '#ci_commit' do + describe 'when the source project exists' do + it 'returns the latest commit' do + commit = double(:commit, id: '123abc') + ci_commit = double(:ci_commit) + + allow(subject).to receive(:last_commit).and_return(commit) + + expect(subject.source_project).to receive(:ci_commit). + with('123abc'). + and_return(ci_commit) + + expect(subject.ci_commit).to eq(ci_commit) + end + end + + describe 'when the source project does not exist' do + it 'returns nil' do + allow(subject).to receive(:source_project).and_return(nil) + + expect(subject.ci_commit).to be_nil + end + end + end end -- cgit v1.2.1 From be045553ea4115853847cfcb8a0a40f2a3d7c4a2 Mon Sep 17 00:00:00 2001 From: Jose Corcuera Date: Fri, 20 Nov 2015 23:55:17 -0500 Subject: Fix Assignee selector when 'Unassigned' #2831 --- CHANGELOG | 1 + app/assets/javascripts/users_select.js.coffee | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 75e5b7585f9..fc7b6e75b1d 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.3.0 (unreleased) + - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) v 8.2.0 - Improved performance of finding projects and groups in various places diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 9157562a5c5..f5db74d84e7 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -58,11 +58,8 @@ class @UsersSelect query.callback(data) - initSelection: (element, callback) => - id = $(element).val() - if id != "" && id != "0" - @user(id, callback) - + initSelection: (args...) => + @initSelection(args...) formatResult: (args...) => @formatResult(args...) formatSelection: (args...) => @@ -71,6 +68,14 @@ class @UsersSelect escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results m + initSelection: (element, callback) -> + id = $(element).val() + if id == "0" + nullUser = { name: 'Unassigned' } + callback(nullUser) + else if id != "" + @user(id, callback) + formatResult: (user) -> if user.avatar_url avatar = user.avatar_url -- cgit v1.2.1 From ca735446efe46e9309fa59673ecea97fb11ddcd5 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 12:13:30 +0100 Subject: Add tags page to readme [ci skip] --- doc/api/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/api/README.md b/doc/api/README.md index 6b8528de50c..25a31b235cc 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -10,6 +10,7 @@ - [Repositories](repositories.md) - [Repository Files](repository_files.md) - [Commits](commits.md) +- [Tags](tags.md) - [Branches](branches.md) - [Merge Requests](merge_requests.md) - [Issues](issues.md) -- cgit v1.2.1 From c113e23489fe4ee0a0d0be837090597595800553 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 16:50:37 +0100 Subject: Use award emoticons in GitLab workflow [ci skip] --- doc/workflow/gitlab_flow.md | 9 ++++----- doc/workflow/voting_slider.png | Bin 5329 -> 0 bytes 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 doc/workflow/voting_slider.png diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md index 31495bce76e..8965e5b3654 100644 --- a/doc/workflow/gitlab_flow.md +++ b/doc/workflow/gitlab_flow.md @@ -244,13 +244,12 @@ Developing software happen in small messy steps and it is OK to have your histor You can use tools to view the network graphs of commits and understand the messy history that created your code. If you rebase code the history is incorrect, and there is no way for tools to remedy this because they can't deal with changing commit identifiers. -## Voting on merge requests +## Award emojis on issues and merge requests -![Voting slider in GitLab](voting_slider.png) +![Emoji bar in GitLab](award_emoji.png) -It is common to voice approval or disapproval by using +1 or -1 emoticons. -In GitLab the +1 and -1 are aggregated and shown at the top of the merge request. -As a rule of thumb anything that doesn't have two times more +1's than -1's is suspect and should not be merged yet. +It is common to voice approval or disapproval by using +1 or -1. In GitLab you +can use emojis to give a virtual high five on issues and merge requests. ## Pushing and removing branches diff --git a/doc/workflow/voting_slider.png b/doc/workflow/voting_slider.png deleted file mode 100644 index 4c660ef9593..00000000000 Binary files a/doc/workflow/voting_slider.png and /dev/null differ -- cgit v1.2.1 From 2cba93a0d2d12ee36bf98569e5c6c14ac7ea40e0 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 17:29:26 +0100 Subject: Make tag API consistent for release feature --- doc/api/tags.md | 10 +++++----- lib/api/entities.rb | 3 ++- lib/api/tags.rb | 8 ++++---- spec/requests/api/tags_spec.rb | 6 +++--- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index b5b90cf6b82..cf95f87dc3e 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -29,7 +29,7 @@ Parameters: ] }, "release": { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" }, "name": "v1.0.0", @@ -70,7 +70,7 @@ Parameters: ] }, "release": { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" }, "name": "v1.0.0", @@ -89,18 +89,18 @@ It returns 200 if the operation succeed. In case of an error, Add release notes to the existing git tag ``` -PUT /projects/:id/repository/:tag/release +PUT /projects/:id/repository/tags/:tag_name/release ``` Parameters: - `id` (required) - The ID of a project -- `tag` (required) - The name of a tag +- `tag_name` (required) - The name of a tag - `description` (required) - Release notes with markdown support ```json { - "tag": "1.0.0", + "tag_name": "1.0.0", "description": "Amazing release. Wow" } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3da6bc415d6..5dea74db295 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -322,7 +322,8 @@ module API end class Release < Grape::Entity - expose :tag, :description + expose :tag, as: :tag_name + expose :description end class RepoTag < Grape::Entity diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 673342dd447..2c6c73da08e 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -44,14 +44,14 @@ module API # # Parameters: # id (required) - The ID of a project - # tag (required) - The name of the tag + # tag_name (required) - The name of the tag # description (required) - Release notes with markdown support # Example Request: - # PUT /projects/:id/repository/tags - put ':id/repository/:tag/release', requirements: { tag: /.*/ } do + # PUT /projects/:id/repository/tags/:tag_name/release + put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] - release = user_project.releases.find_or_initialize_by(tag: params[:tag]) + release = user_project.releases.find_or_initialize_by(tag: params[:tag_name]) release.update_attributes(description: params[:description]) present release, with: Entities::Release diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index cc9a5f47582..e3fd2d547c4 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -119,16 +119,16 @@ describe API::API, api: true do end end - describe 'PUT /projects/:id/repository/:tag/release' do + describe 'PUT /projects/:id/repository/tags/:tag_name/release' do let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } it 'should create description for existing git tag' do - put api("/projects/#{project.id}/repository/#{tag_name}/release", user), + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description expect(response.status).to eq(200) - expect(json_response['tag']).to eq(tag_name) + expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end end -- cgit v1.2.1 From faef95af1a07bdcfd02eead36d144f332b428f1f Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 18:08:45 +0100 Subject: API: Return 404 if the tag for a release does not exist --- app/services/create_release_service.rb | 25 +++++++++++++++++++++++++ app/services/create_tag_service.rb | 10 +++++----- doc/api/tags.md | 3 ++- lib/api/tags.rb | 10 +++++++--- spec/requests/api/tags_spec.rb | 8 ++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 app/services/create_release_service.rb diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb new file mode 100644 index 00000000000..355374ce252 --- /dev/null +++ b/app/services/create_release_service.rb @@ -0,0 +1,25 @@ +require_relative 'base_service' + +class CreateReleaseService < BaseService + def execute(tag_name, release_description) + + repository = project.repository + existing_tag = repository.find_tag(tag_name) + + # Only create a release if the tag exists + if existing_tag + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: release_description) + + success(release) + else + error('Tag does not exist') + end + end + + def success(release) + out = super() + out[:release] = release + out + end +end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 9917119fce2..2452999382a 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -19,16 +19,16 @@ class CreateTagService < BaseService new_tag = repository.find_tag(tag_name) if new_tag - if release_description - release = project.releases.find_or_initialize_by(tag: tag_name) - release.update_attributes(description: release_description) - end - push_data = create_push_data(project, current_user, new_tag) EventCreateService.new.push(project, current_user, push_data) project.execute_hooks(push_data.dup, :tag_push_hooks) project.execute_services(push_data.dup, :tag_push_hooks) + if release_description + CreateReleaseService.new(@project, @current_user). + execute(tag_name, release_description) + end + success(new_tag) else error('Invalid reference name') diff --git a/doc/api/tags.md b/doc/api/tags.md index cf95f87dc3e..ca9e2cef651 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -86,7 +86,8 @@ It returns 200 if the operation succeed. In case of an error, ## New release -Add release notes to the existing git tag +Add release notes to the existing git tag. It returns 200 if the release is +created successfully. If the tag does not exist, 404 is returned. ``` PUT /projects/:id/repository/tags/:tag_name/release diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 2c6c73da08e..0721b9cc844 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -51,10 +51,14 @@ module API put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] - release = user_project.releases.find_or_initialize_by(tag: params[:tag_name]) - release.update_attributes(description: params[:description]) + result = CreateReleaseService.new(user_project, current_user). + execute(params[:tag_name], params[:description]) - present release, with: Entities::Release + if result[:status] == :success + present result[:release], with: Entities::Release + else + render_api_error!(result[:message], 404) + end end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index e3fd2d547c4..4dac3eec84e 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -131,5 +131,13 @@ describe API::API, api: true do expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end + + it 'should return 404 if the tag does not exist' do + put api("/projects/#{project.id}/repository/tags/foobar/release", user), + description: description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Tag does not exist') + end end end -- cgit v1.2.1 From fbac9e106dd6acf35ba679b106b438a3bbfd191f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sat, 21 Nov 2015 18:32:59 +0200 Subject: Award: merge request fix --- app/controllers/projects/notes_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 1e3f1d8fd2f..ead940aea6c 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -59,8 +59,11 @@ class Projects::NotesController < Projects::ApplicationController end def award_toggle - noteable = note_params[:noteable_type] == "issue" ? Issue : MergeRequest - noteable = noteable.find_by!(id: note_params[:noteable_id], project: project) + noteable = if note_params[:noteable_type] == "issue" + project.issues.find(note_params[:noteable_id]) + else + project.merge_requests.find(note_params[:noteable_id]) + end data = { author: current_user, -- cgit v1.2.1 From 6f7e90f6dba300591281aba08ffbe30ce3cc5c90 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 18:51:41 +0100 Subject: Use POST to create a new release instead of PUT --- doc/api/tags.md | 6 +++--- lib/api/tags.rb | 2 +- spec/requests/api/tags_spec.rb | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/api/tags.md b/doc/api/tags.md index ca9e2cef651..ab135117250 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -84,13 +84,13 @@ It returns 200 if the operation succeed. In case of an error, 405 with an explaining error message is returned. -## New release +## Create a new release -Add release notes to the existing git tag. It returns 200 if the release is +Add release notes to the existing git tag. It returns 201 if the release is created successfully. If the tag does not exist, 404 is returned. ``` -PUT /projects/:id/repository/tags/:tag_name/release +POST /projects/:id/repository/tags/:tag_name/release ``` Parameters: diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 0721b9cc844..48f630d58e5 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -48,7 +48,7 @@ module API # description (required) - Release notes with markdown support # Example Request: # PUT /projects/:id/repository/tags/:tag_name/release - put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do + post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] result = CreateReleaseService.new(user_project, current_user). diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 4dac3eec84e..0ee819ae445 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -119,21 +119,21 @@ describe API::API, api: true do end end - describe 'PUT /projects/:id/repository/tags/:tag_name/release' do + describe 'POST /projects/:id/repository/tags/:tag_name/release' do let(:tag_name) { project.repository.tag_names.first } let(:description) { 'Awesome release!' } it 'should create description for existing git tag' do - put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), description: description - expect(response.status).to eq(200) + expect(response.status).to eq(201) expect(json_response['tag_name']).to eq(tag_name) expect(json_response['description']).to eq(description) end it 'should return 404 if the tag does not exist' do - put api("/projects/#{project.id}/repository/tags/foobar/release", user), + post api("/projects/#{project.id}/repository/tags/foobar/release", user), description: description expect(response.status).to eq(404) -- cgit v1.2.1 From c502695fdb9b01b4ff8721845e1d39f3d3369008 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 21 Nov 2015 13:15:51 -0500 Subject: Add award_emoji.png doc image [ci skip] --- doc/workflow/award_emoji.png | Bin 0 -> 6620 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/workflow/award_emoji.png diff --git a/doc/workflow/award_emoji.png b/doc/workflow/award_emoji.png new file mode 100644 index 00000000000..fb26ee04393 Binary files /dev/null and b/doc/workflow/award_emoji.png differ -- cgit v1.2.1 From f2318dfc9e80555bae99f543d4e07ec089ab3935 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sat, 21 Nov 2015 20:25:07 +0100 Subject: Fix CI yaml syntax documentation [ci skip] --- doc/ci/yaml/README.md | 90 +++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3e6071a2ee7..3dbf1afc7a9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -278,26 +278,23 @@ The above script will: `artifacts` is used to specify list of files and directories which should be attached to build after success. 1. Send all files in `binaries` and `.config`: -``` -artifacts: - paths: - - binaries/ - - .config -``` + + artifacts: + paths: + - binaries/ + - .config 2. Send all git untracked files: -``` -artifacts: - untracked: true -``` + + artifacts: + untracked: true 3. Send all git untracked files and files in `binaries`: -``` -artifacts: - untracked: true - paths: - - binaries/ -``` + + artifacts: + untracked: true + paths: + - binaries/ The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download. @@ -307,46 +304,41 @@ This feature requires GitLab Runner v0.7.0 or higher. `cache` is used to specify list of files and directories which should be cached between builds. 1. Cache all files in `binaries` and `.config`: -``` -rspec: - script: test - cache: - paths: - - binaries/ - - .config -``` + + rspec: + script: test + cache: + paths: + - binaries/ + - .config 2. Cache all git untracked files: -``` -rspec: - script: test - cache: - untracked: true -``` + rspec: + script: test + cache: + untracked: true + 3. Cache all git untracked files and files in `binaries`: -``` -rspec: - script: test - cache: - untracked: true - paths: - - binaries/ -``` -4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`: + rspec: + script: test + cache: + untracked: true + paths: + - binaries/ -``` -cache: - paths: - - my/files +4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`: -rspec: - script: test - cache: - paths: - - binaries/ -``` + cache: + paths: + - my/files + + rspec: + script: test + cache: + paths: + - binaries/ The cache is provided on best effort basis, so don't expect that cache will be present. For implementation details please check GitLab Runner. -- cgit v1.2.1 From 26b12e2c374c8f07abda06a8b19bd116448325f4 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 21:36:31 +0100 Subject: Add upvote/downvote fields to merge request and note API to preserve compatibility --- app/models/concerns/issuable.rb | 10 ++++++++++ app/models/note.rb | 10 ++++++++++ doc/api/merge_requests.md | 32 +++++++++++++++++++++++++------- doc/api/notes.md | 11 ++++++++--- lib/api/entities.rb | 5 +++++ 5 files changed, 58 insertions(+), 10 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 2dafb5e752f..98ad5e76da7 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -92,6 +92,16 @@ module Issuable opened? || reopened? end + # Deprecated. Still exists to preserve API compatibility. + def downvotes + 0 + end + + # Deprecated. Still exists to preserve API compatibility. + def upvotes + 0 + end + def subscribed?(user) subscription = subscriptions.find_by_user_id(user.id) diff --git a/app/models/note.rb b/app/models/note.rb index e30be444eb5..1c6345e735c 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -335,6 +335,16 @@ class Note < ActiveRecord::Base read_attribute(:system) end + # Deprecated. Still exists to preserve API compatibility. + def downvote? + false + end + + # Deprecated. Still exists to preserve API compatibility. + def upvote? + false + end + def editable? !system? end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2f17d4ae06b..0cef09d5b27 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -3,8 +3,9 @@ ## List merge requests Get all merge requests for this project. -The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). -The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. +The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). +The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. With GitLab 8.2 the return fields `upvotes` and +`downvotes` are deprecated and always return `0`. ``` GET /projects/:id/merge_requests @@ -31,6 +32,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -55,7 +58,7 @@ Parameters: ## Get single MR -Shows information about a single merge request. +Shows information about a single merge request. With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and always return `0`. ``` GET /projects/:id/merge_request/:merge_request_id @@ -75,6 +78,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -98,7 +103,9 @@ Parameters: ## Get single MR changes -Shows information about the merge request including its files and changes +Shows information about the merge request including its files and changes. +With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and +always return `0`. ``` GET /projects/:id/merge_request/:merge_request_id/changes @@ -122,6 +129,8 @@ Parameters: "updated_at": "2015-02-02T20:08:49.959Z", "target_branch": "secret_token", "source_branch": "version-1-9", + "upvotes": 0, + "downvotes": 0, "author": { "name": "Chad Hamill", "username": "jarrett", @@ -167,7 +176,8 @@ Parameters: ## Create MR -Creates a new merge request. +Creates a new merge request. With GitLab 8.2 the return fields `upvotes` and ` +downvotes` are deprecated and always return `0`. ``` POST /projects/:id/merge_requests @@ -192,6 +202,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -217,7 +229,8 @@ If an error occurs, an error number and a message explaining the reason is retur ## Update MR -Updates an existing merge request. You can change the target branch, title, or even close the MR. +Updates an existing merge request. You can change the target branch, title, or even close the MR. With GitLab 8.2 the return fields `upvotes` and `downvotes` +are deprecated and always return `0`. ``` PUT /projects/:id/merge_request/:merge_request_id @@ -242,6 +255,8 @@ Parameters: "title": "test1", "description": "description1", "state": "opened", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", @@ -266,7 +281,8 @@ If an error occurs, an error number and a message explaining the reason is retur ## Accept MR -Merge changes submitted with MR using this API. +Merge changes submitted with MR using this API. With GitLab 8.2 the return +fields `upvotes` and `downvotes` are deprecated and always return `0`. If merge success you get `200 OK`. @@ -294,6 +310,8 @@ Parameters: "project_id": 3, "title": "test1", "state": "merged", + "upvotes": 0, + "downvotes": 0, "author": { "id": 1, "username": "admin", diff --git a/doc/api/notes.md b/doc/api/notes.md index bcba1f62151..e7f299c0994 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -6,7 +6,8 @@ Notes are comments on snippets, issues or merge requests. ### List project issue notes -Gets a list of all notes for a single issue. +Gets a list of all notes for a single issue. With GitLab 8.2 the return fields +`upvote` and `downvote` are deprecated and always return `false`. ``` GET /projects/:id/issues/:issue_id/notes @@ -32,7 +33,9 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:22:45Z", - "system": true + "system": true, + "upvote": false, + "downvote": false }, { "id": 305, @@ -47,7 +50,9 @@ Parameters: "created_at": "2013-09-30T13:46:01Z" }, "created_at": "2013-10-02T09:56:03Z", - "system": false + "system": true, + "upvote": false, + "downvote": false } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 3da6bc415d6..7f9dba4b6a5 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -163,6 +163,8 @@ module API class MergeRequest < ProjectEntity expose :target_branch, :source_branch + # deprecated, always returns 0 + expose :upvotes, :downvotes expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id expose :label_names, as: :labels @@ -192,6 +194,9 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system + # upvote? and downvote? are deprecated, always return false + expose :upvote?, as: :upvote + expose :downvote?, as: :downvote end class MRNote < Grape::Entity -- cgit v1.2.1 From 3ea05c5b5b253de33d8bf8d615c66e2935b940ef Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 22:24:34 +0100 Subject: Only allow to create a release if it does not exist yet --- app/services/create_release_service.rb | 14 ++++++++++---- doc/api/tags.md | 3 ++- lib/api/tags.rb | 4 ++-- spec/requests/api/tags_spec.rb | 17 ++++++++++++++++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb index 355374ce252..54e87478e64 100644 --- a/app/services/create_release_service.rb +++ b/app/services/create_release_service.rb @@ -8,12 +8,18 @@ class CreateReleaseService < BaseService # Only create a release if the tag exists if existing_tag - release = project.releases.find_or_initialize_by(tag: tag_name) - release.update_attributes(description: release_description) + release = project.releases.find_by(tag: tag_name) - success(release) + if(release) + error('Release already exists', 409) + else + release = project.releases.new({ tag: tag_name, description: release_description }) + release.save + + success(release) + end else - error('Tag does not exist') + error('Tag does not exist', 404) end end diff --git a/doc/api/tags.md b/doc/api/tags.md index ab135117250..bc61aa1118f 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -87,7 +87,8 @@ It returns 200 if the operation succeed. In case of an error, ## Create a new release Add release notes to the existing git tag. It returns 201 if the release is -created successfully. If the tag does not exist, 404 is returned. +created successfully. If the tag does not exist, 404 is returned. If there +already exists a release for the given tag, 409 is returned. ``` POST /projects/:id/repository/tags/:tag_name/release diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 48f630d58e5..e46d9bb96f0 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -47,7 +47,7 @@ module API # tag_name (required) - The name of the tag # description (required) - Release notes with markdown support # Example Request: - # PUT /projects/:id/repository/tags/:tag_name/release + # POST /projects/:id/repository/tags/:tag_name/release post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do authorize_push_project required_attributes! [:description] @@ -57,7 +57,7 @@ module API if result[:status] == :success present result[:release], with: Entities::Release else - render_api_error!(result[:message], 404) + render_api_error!(result[:message], result[:http_status]) end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 0ee819ae445..0a456fc3b5d 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -28,10 +28,10 @@ describe API::API, api: true do before do release = project.releases.find_or_initialize_by(tag: tag_name) release.update_attributes(description: description) - get api("/projects/#{project.id}/repository/tags", user) end it "should return an array of project tags with release info" do + get api("/projects/#{project.id}/repository/tags", user) expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) @@ -139,5 +139,20 @@ describe API::API, api: true do expect(response.status).to eq(404) expect(json_response['message']).to eq('Tag does not exist') end + + context 'on tag with existing release' do + before do + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: description) + end + + it 'should return 409 if there is already a release' do + post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: description + + expect(response.status).to eq(409) + expect(json_response['message']).to eq('Release already exists') + end + end end end -- cgit v1.2.1 From 04a3d27eaba0312d99e8d88a3a9ee4b5c83ecce1 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 21 Nov 2015 22:34:53 +0100 Subject: Allow editing a release in API via PUT method --- app/services/create_release_service.rb | 2 +- app/services/update_release_service.rb | 29 ++++++++++++++++++++++++++ doc/api/tags.md | 23 ++++++++++++++++++++ lib/api/tags.rb | 21 +++++++++++++++++++ spec/requests/api/tags_spec.rb | 38 ++++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/services/update_release_service.rb diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb index 54e87478e64..e06a6f2f47a 100644 --- a/app/services/create_release_service.rb +++ b/app/services/create_release_service.rb @@ -10,7 +10,7 @@ class CreateReleaseService < BaseService if existing_tag release = project.releases.find_by(tag: tag_name) - if(release) + if release error('Release already exists', 409) else release = project.releases.new({ tag: tag_name, description: release_description }) diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb new file mode 100644 index 00000000000..25eb13ef09a --- /dev/null +++ b/app/services/update_release_service.rb @@ -0,0 +1,29 @@ +require_relative 'base_service' + +class UpdateReleaseService < BaseService + def execute(tag_name, release_description) + + repository = project.repository + existing_tag = repository.find_tag(tag_name) + + if existing_tag + release = project.releases.find_by(tag: tag_name) + + if release + release.update_attributes(description: release_description) + + success(release) + else + error('Release does not exist', 404) + end + else + error('Tag does not exist', 404) + end + end + + def success(release) + out = super() + out[:release] = release + out + end +end diff --git a/doc/api/tags.md b/doc/api/tags.md index bc61aa1118f..085d387e824 100644 --- a/doc/api/tags.md +++ b/doc/api/tags.md @@ -106,3 +106,26 @@ Parameters: "description": "Amazing release. Wow" } ``` + +## Update a release + +Updates the release notes of a given release. It returns 200 if the release is +successfully updated. If the tag or the release does not exist, it returns 404 +with a proper error message. + +``` +PUT /projects/:id/repository/tags/:tag_name/release +``` + +Parameters: + +- `id` (required) - The ID of a project +- `tag_name` (required) - The name of a tag +- `description` (required) - Release notes with markdown support + +```json +{ + "tag_name": "1.0.0", + "description": "Amazing release. Wow" +} +``` \ No newline at end of file diff --git a/lib/api/tags.rb b/lib/api/tags.rb index e46d9bb96f0..47621f443e6 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -60,6 +60,27 @@ module API render_api_error!(result[:message], result[:http_status]) end end + + # Updates a release notes of a tag + # + # Parameters: + # id (required) - The ID of a project + # tag_name (required) - The name of the tag + # description (required) - Release notes with markdown support + # Example Request: + # PUT /projects/:id/repository/tags/:tag_name/release + put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do + authorize_push_project + required_attributes! [:description] + result = UpdateReleaseService.new(user_project, current_user). + execute(params[:tag_name], params[:description]) + + if result[:status] == :success + present result[:release], with: Entities::Release + else + render_api_error!(result[:message], result[:http_status]) + end + end end end end diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 0a456fc3b5d..17f2643fd45 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -155,4 +155,42 @@ describe API::API, api: true do end end end + + describe 'PUT id/repository/tags/:tag_name/release' do + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:new_description) { 'The best release!' } + + context 'on tag with existing release' do + before do + release = project.releases.find_or_initialize_by(tag: tag_name) + release.update_attributes(description: description) + end + + it 'should update the release description' do + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: new_description + + expect(response.status).to eq(200) + expect(json_response['tag_name']).to eq(tag_name) + expect(json_response['description']).to eq(new_description) + end + end + + it 'should return 404 if the tag does not exist' do + put api("/projects/#{project.id}/repository/tags/foobar/release", user), + description: new_description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Tag does not exist') + end + + it 'should return 404 if the release does not exist' do + put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user), + description: new_description + + expect(response.status).to eq(404) + expect(json_response['message']).to eq('Release does not exist') + end + end end -- cgit v1.2.1 From 3fc10d46f180d5b1904496e4f4e528d215057dbd Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Sun, 22 Nov 2015 01:51:00 +0200 Subject: Emoji bug: Invalid url to image --- app/assets/javascripts/awards_handler.coffee | 2 +- app/controllers/projects/notes_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 635c9b4f8d2..09b48fe5572 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -73,7 +73,7 @@ class @AwardsHandler getImage: (emoji, custom_path) -> if custom_path - $(".awards-menu li").first().html().replace(/emoji\/.*\.png/, custom_path) + $("").attr({src: custom_path, width: 20, height: 20}).wrap("
    ").parent().html() else $("li[data-emoji='" + emoji + "']").html() diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index ead940aea6c..5ac18446aa7 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -136,7 +136,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_id: note.discussion_id, html: note_to_html(note), award: note.is_award, - emoji_path: note.is_award ? ::AwardEmoji.path_to_emoji_image(note.note) : "", + emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "", note: note.note, discussion_html: note_to_discussion_html(note), discussion_with_diff_html: note_to_discussion_with_diff_html(note) -- cgit v1.2.1 From 075e3661c534a06753065e9e3323168b786cdbe5 Mon Sep 17 00:00:00 2001 From: Felipe Orlando Date: Sun, 22 Nov 2015 06:04:20 -0200 Subject: Update autocomplete_controller to be more readable --- app/controllers/autocomplete_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index aa0268b8d62..77c8dafc012 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -9,7 +9,7 @@ class AutocompleteController < ApplicationController @users = @users.reorder(:name) @users = @users.page(params[:page]).per(PER_PAGE) - unless params[:search].present? + if params[:search].blank? # Include current user if available to filter by "Me" if params[:current_user] && current_user @users = [*@users, current_user].uniq -- cgit v1.2.1 From ab238a767713aeb2ae019e22831fc0049873870c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 22 Nov 2015 07:23:56 -0800 Subject: Fix CSS styling for clipboard icon in new MR page Closes #3602 --- app/assets/stylesheets/framework/tw_bootstrap.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 50c0cf61f4e..94f0ed761df 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -190,6 +190,10 @@ .btn { min-width: 124px; } + + .btn-clipboard { + min-width: 0px; + } } &.panel-small { -- cgit v1.2.1 From 732c2d15d235ca112db4b1d15c888da52660a1d5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 18:24:53 -0500 Subject: Update doc/release/patch.md with more current patch procedures [ci skip] --- doc/release/patch.md | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/doc/release/patch.md b/doc/release/patch.md index 6aa11b283df..3022e375aca 100644 --- a/doc/release/patch.md +++ b/doc/release/patch.md @@ -1,21 +1,46 @@ # Things to do when doing a patch release -NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). +NOTE: This is a guide for GitLab developers. If you are trying to install GitLab +see the latest stable [installation guide](install/installation.md) and if you +are trying to upgrade, see the [upgrade guides](update). ## When to do a patch release -Do a patch release when there is a critical regression that needs to be addresses before the next monthly release. - -Otherwise include it in the monthly release and note there was a regression fix in the release announcement. +Patch releases are done as-needed in order to fix regressions in the current +major release that cannot or should not wait until the next major release. +What's included and when to release is at the discretion of the release manager. ## Release Procedure +### Create a patch issue + +Create an issue in the GitLab CE project. Name it "Release x.y.z", tag it with +the `release` label, and assign it to the milestone of the corresponding major +release. + +Use the following template: + +``` +- Picked into respective `stable` branches: +- [ ] Merge `x-y-stable` into `x-y-stable-ee` +- [ ] release-tools: `x.y.z` +- gitlab-omnibus + - [ ] `x.y.z+ee.0` + - [ ] `x.y.z+ce.0` +- [ ] Deploy +- [ ] Add patch notice to [x.y regressions]() +- [ ] [Blog post]() +- [ ] [Tweet]() +- [ ] Add entry to version.gitlab.com +``` + +Update the issue with links to merge requests that need to be/have been picked +into the `stable` branches. + ### Preparation 1. Verify that the issue can be reproduced 1. Note in the 'GitLab X.X regressions' that you will create a patch -1. Create an issue on private GitLab development server -1. Name the issue "Release X.X.X CE and X.X.X EE", this will make searching easier 1. Fix the issue on a feature branch, do this on the private GitLab development server 1. If it is a security issue, then assign it to the release manager and apply a 'security' label 1. Consider creating and testing workarounds @@ -25,7 +50,6 @@ Otherwise include it in the monthly release and note there was a regression fix 1. For EE, update the CHANGELOG-EE if it is EE specific fix. Otherwise, merge the stable CE branch and add to CHANGELOG-EE "Merge community edition changes for version X.X.X" 1. Merge CE stable branch into EE stable branch - ### Bump version Get release tools @@ -54,4 +78,4 @@ bundle exec rake release["x.x.x"] 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only) 1. Create the 'x.y.0' version on version.gitlab.com 1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md) -1. Create a new patch release issue for the next potential release \ No newline at end of file +1. Create a new patch release issue for the next potential release -- cgit v1.2.1 From 8608c6325e19f529f7b43ff881c562d3a0114e1c Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 23 Nov 2015 09:42:20 +0100 Subject: Refactor MergeWhenBuildSucceedsService and incorporate feedback --- CHANGELOG | 5 +-- .../projects/merge_requests_controller.rb | 11 ++----- app/models/merge_request.rb | 11 +++++-- .../merge_when_build_succeeds_service.rb | 17 +++++++++- app/services/system_note_service.rb | 4 +-- .../merge_requests/widget/open/_accept.html.haml | 2 +- .../open/_merge_when_build_succeeds.html.haml | 9 ++---- config/routes.rb | 1 + db/schema.rb | 2 -- doc/api/merge_requests.md | 8 ++--- lib/api/merge_requests.rb | 36 ++++++++-------------- 11 files changed, 52 insertions(+), 54 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 08188324d74..18f5f270ec1 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.3.0 (unreleased) +- Merge when build succeeds (Zeger-Jan van de Weg) v 8.2.0 - Fix grouping of contributors by email in graph. @@ -28,10 +29,6 @@ v 8.2.0 - Allow to define cache in `.gitlab-ci.yml` - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - - Use issue editor as cross reference comment author when issue is edited with a new mention. - - Merge when build succeeds (Zeger-Jan van de Weg) - -v 8.1.1 - [API] Add ability to fetch the commit ID of the last commit that actually touched a file - Fix omniauth documentation setting for omnibus configuration (Jon Cairns) - Add "New file" link to dropdown on project page diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 9db6ed5022d..f2e9a34dd2e 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -151,14 +151,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def cancel_merge_when_build_succeeds - unless @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user - return access_denied! - end + return access_denied! unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user) - if @merge_request.merge_when_build_succeeds? - @merge_request.reset_merge_when_build_succeeds - SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user) - end + MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user).cancel(@merge_request) end def merge @@ -171,7 +166,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request.update(merge_error: nil) - if params[:merge_when_build_succeeds] && @merge_request.ci_commit.active? + if params[:merge_when_build_succeeds] && @merge_request.ci_commit && @merge_request.ci_commit.active? MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params) .execute(@merge_request) @status = :merge_when_build_succeeds diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 05c3bc074bb..89f9e8fa6a8 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -254,8 +254,15 @@ class MergeRequest < ActiveRecord::Base end end - def mergeable_by_or_author(user) - self.can_be_merged_by?(user) || self.author == user + def can_cancel_merge_when_build_succeeds?(user) + can_be_merged_by?(user) || self.author == user + end + + def can_remove_source_branch? + for_fork? && + !project.protected_branch(source_branch) && + !project.repository.root_ref(source_branch) && + can?(current_user, :push_code, project) end def mr_and_commit_notes diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb index d5cae2f98f7..15dcace5dfb 100644 --- a/app/services/merge_requests/merge_when_build_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb @@ -1,5 +1,7 @@ module MergeRequests class MergeWhenBuildSucceedsService < MergeRequests::BaseService + # Marks the passed `merge_request` to be marked when the build succeeds or + # updates the params for the automatic merge def execute(merge_request) merge_request.merge_params.merge!(params) @@ -14,10 +16,11 @@ module MergeRequests merge_request.save unless already_approved - SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user) + SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.ci_commit) end end + # Triggers the automatic merge of merge_request once the build succeeds def trigger(build) merge_requests = merge_request_from(build) @@ -31,6 +34,18 @@ module MergeRequests end end + # Cancels the automatic merge + def cancel(merge_request) + if merge_request.merge_when_build_succeeds? && merge_request.open? && !merge_request.merged? + merge_request.reset_merge_when_build_succeeds + SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user) + + success + else + error("Can't cancel the automatic merge", 406) + end + end + private def merge_request_from(build) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 5e8281a3fd0..7de4221c4c4 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -131,8 +131,8 @@ class SystemNoteService end # Called when 'merge when build succeeds' is executed - def self.merge_when_build_succeeds(noteable, project, author) - body = "This merge request will be automatically merged when the build for #{noteable.ci_commit.short_sha} succeeds" + def self.merge_when_build_succeeds(noteable, project, author, ci_commit) + body = "Approved an automatic merge when the build for #{ci_commit.sha} succeeds" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index d2189787d47..5ec623b472c 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -13,7 +13,7 @@ - else = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do Accept Merge Request - - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork? + - if @merge_request.can_remove_source_branch? .accept-control.checkbox = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do = check_box_tag :should_remove_source_branch diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index ddd1a7bd63d..51e18f84424 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -8,20 +8,17 @@ The changes will be merged into %span.label-branch= @merge_request.target_branch The source branch will be removed. - - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) - - remove_source_branch_button = true %p = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch - The source branch won't be removed. + The source branch will not be removed. -- if remove_source_branch_button || @merge_request.can_be_merged_by?(current_user) .clearfix.prepend-top-10 - - if remove_source_branch_button + - if @merge_request.can_remove_source_branch? && !@merge_request.merge_params["should_remove_source_branch"].present? = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do = icon('times') Remove Source Branch When Merged - - if @merge_request.can_be_merged_by?(current_user) || @merge_request.author == current_user + - if @merge_request.merge_when_build_succeeds = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do Cancel Automatic Merge diff --git a/config/routes.rb b/config/routes.rb index 49543786686..81f568cfa74 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -570,6 +570,7 @@ Gitlab::Application.routes.draw do member do get :diffs get :commits + get :merge_check post :merge post :cancel_merge_when_build_succeeds get :ci_status diff --git a/db/schema.rb b/db/schema.rb index fa32617cb99..7d5ed559754 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -421,7 +421,6 @@ ActiveRecord::Schema.define(version: 20151116144118) do end add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree - add_index "labels", ["title"], name: "index_labels_on_title", using: :btree create_table "lfs_objects", force: true do |t| t.string "oid", null: false @@ -525,7 +524,6 @@ ActiveRecord::Schema.define(version: 20151116144118) do add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree - add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: true do |t| t.string "name", null: false diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 1b2ad1caf49..19e6fbb7887 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -296,7 +296,7 @@ Parameters: - `merge_request_id` (required) - ID of MR - `merge_commit_message` (optional) - Custom merge commit message - `should_remove_source_branch` (optional) - if `true` removes the source branch -- `merge_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds +- `merged_when_build_succeeds` (optional) - if `true` the MR is merge when the build succeeds ```json { @@ -329,15 +329,13 @@ Parameters: ## Cancel Merge When Build Succeeds -Cancels the merge when build succeeds and reset the merge parameters - -If successfull you'll get `200 OK`. +If successful you'll get `200 OK`. If you don't have permissions to accept this merge request - you'll get a 401 If the merge request is already merged or closed - you get 405 and error message 'Method Not Allowed' -In case the merge request is not set to be merged when the build succeeds, you'll also get a 405 with the error message 'Method Not Allowed' +In case the merge request is not set to be merged when the build succeeds, you'll also get a 406 error. ``` PUT /projects/:id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds ``` diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index f981432db36..b72f816932b 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -182,39 +182,35 @@ module API # id (required) - The ID of a project # merge_request_id (required) - ID of MR # merge_commit_message (optional) - Custom merge commit message - # merge_when_build_succeeds (optional) - truethy when this MR should be merged when the build is succesfull + # should_remove_source_branch - When true, the source branch will be deleted if possible + # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds # Example: # PUT /projects/:id/merge_request/:merge_request_id/merge # put ":id/merge_request/:merge_request_id/merge" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - allowed = ::Gitlab::GitAccess.new(current_user, user_project). - can_push_to_branch?(merge_request.target_branch) - # Merge request can not be merged # because user dont have permissions to push into target branch - unauthorized! unless allowed + unauthorized! unless merge_request.can_be_merged_by?(current_user) - not_allowed! unless merge_request.open? + not_allowed! unless merge_request.open? && !merge_request.work_in_progress? merge_request.check_if_can_be_merged if merge_request.unchecked? + render_api_error!('Branch cannot be merged', 406) if merge_request.can_be_merged? + merge_params = { commit_message: params[:merge_commit_message], should_remove_source_branch: params[:should_remove_source_branch] } - if !merge_request.work_in_progress? - if parse_boolean(params[:merge_when_build_succeeds]) - ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). - execute(merge_request) - else - ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params). - execute(merge_request, merge_params) - end + if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active? + ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). + execute(merge_request) else - render_api_error!('Branch cannot be merged', 405) + ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params). + execute(merge_request) end present merge_request, with: Entities::MergeRequest @@ -233,15 +229,9 @@ module API # Merge request can not be merged # because user dont have permissions to push into target branch - unauthorized! unless allowed + unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user) - if merge_request.merged? || !merge_request.open? || !merge_request.merge_when_build_succeeds? - not_allowed! - end - - merge_request.reset_merge_when_build_succeeds - - present merge_request, with: Entities::MergeRequest + ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request) end # Get a merge request's comments -- cgit v1.2.1 From 11728b50f96a296c760d9e69273d304f92e51f8f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Sun, 22 Nov 2015 14:27:30 +0100 Subject: Expose artifacts path --- CHANGELOG | 1 + app/models/application_setting.rb | 2 +- app/uploaders/artifact_uploader.rb | 6 +++--- config/gitlab.yml.example | 6 ++++++ config/initializers/1_settings.rb | 9 ++++++++- lib/ci/api/builds.rb | 2 ++ lib/gitlab/current_settings.rb | 2 +- 7 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 75e5b7585f9..4b7218b594d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ v 8.3.0 (unreleased) v 8.2.0 - Improved performance of finding projects and groups in various places - Improved performance of rendering user profile pages and Atom feeds + - Expose build artifacts path as config option - Fix grouping of contributors by email in graph. - Improved performance of finding issues with/without labels - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 9e70247ef51..b2d5fe1558f 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -99,7 +99,7 @@ class ApplicationSetting < ActiveRecord::Base restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], - max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'], + max_artifacts_size: Settings.artifacts['max_size'], ) end diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb index b4e0fc5772d..1dccc39e7e2 100644 --- a/app/uploaders/artifact_uploader.rb +++ b/app/uploaders/artifact_uploader.rb @@ -5,15 +5,15 @@ class ArtifactUploader < CarrierWave::Uploader::Base attr_accessor :build, :field def self.artifacts_path - File.expand_path('shared/artifacts/', Rails.root) + Gitlab.config.artifacts.path end def self.artifacts_upload_path - File.expand_path('shared/artifacts/tmp/uploads/', Rails.root) + File.join(self.artifacts_path, 'tmp/uploads/') end def self.artifacts_cache_path - File.expand_path('shared/artifacts/tmp/cache/', Rails.root) + File.join(self.artifacts_path, 'tmp/cache/') end def initialize(build, field) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1788d4c2c7c..1da42ab38f3 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -124,6 +124,12 @@ production: &base # The mailbox where incoming mail will end up. Usually "inbox". mailbox: "inbox" + ## Build Artifacts + artifacts: + enabled: true + # The location where build artifacts are stored (default: shared/artifacts). + # path: shared/artifacts + ## Git LFS lfs: enabled: true diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b498fb1e1da..b162b8a83fc 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -187,7 +187,6 @@ Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_br Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url) Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) -Settings.gitlab_ci['max_artifacts_size'] ||= 100 # in megabytes # # Reply by email @@ -199,6 +198,14 @@ Settings.incoming_email['ssl'] = false if Settings.incoming_email['ssl']. Settings.incoming_email['start_tls'] = false if Settings.incoming_email['start_tls'].nil? Settings.incoming_email['mailbox'] = "inbox" if Settings.incoming_email['mailbox'].nil? +# +# Build Artifacts +# +Settings['artifacts'] ||= Settingslogic.new({}) +Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil? +Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root) +Settings.artifacts['max_size'] ||= 100 # in megabytes + # # Git LFS # diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb index 0a586672807..15faa6edd84 100644 --- a/lib/ci/api/builds.rb +++ b/lib/ci/api/builds.rb @@ -58,6 +58,7 @@ module Ci # POST /builds/:id/artifacts/authorize post ":id/artifacts/authorize" do require_gitlab_workhorse! + not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) not_found! unless build authenticate_build_token!(build) @@ -91,6 +92,7 @@ module Ci # POST /builds/:id/artifacts post ":id/artifacts" do require_gitlab_workhorse! + not_allowed! unless Gitlab.config.artifacts.enabled build = Ci::Build.find_by_id(params[:id]) not_found! unless build authenticate_build_token!(build) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 2d3e32d9539..46a4ef0e31f 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -25,7 +25,7 @@ module Gitlab session_expire_delay: Settings.gitlab['session_expire_delay'], import_sources: Settings.gitlab['import_sources'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], - max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'], + max_artifacts_size: Settings.artifacts['max_size'], ) end -- cgit v1.2.1 From 57e974c03b693c37b6ca7e57ce86477e32b770f1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 23 Nov 2015 13:49:40 +0100 Subject: Fix 500 when using CI - Fix for Ci::Build state machine, allowing to process builds without the project - Forcefully update builds that didn't want to update with state machine - Fix saving GitLabCiService as Admin Template --- CHANGELOG | 2 ++ app/models/ci/build.rb | 2 ++ app/models/project_services/gitlab_ci_service.rb | 1 + app/workers/stuck_ci_builds_worker.rb | 3 +++ 4 files changed, 8 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index fc7b6e75b1d..1e39a9da59b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + - Forcefully update builds that didn't want to update with state machine + - Fix: saving GitLabCiService as Admin Template v 8.2.0 - Improved performance of finding projects and groups in various places diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e78b154084b..52ce1b920fa 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -97,6 +97,8 @@ module Ci state_machine :status, initial: :pending do after_transition any => [:success, :failed, :canceled] do |build, transition| + return unless build.gl_project + project = build.project if project.web_hooks? diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 095d04e0df4..c5657b5070e 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -30,6 +30,7 @@ class GitlabCiService < CiService end def ensure_gitlab_ci_project + return unless project project.ensure_gitlab_ci_project end diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb index ad02a3b16d9..4e5eddbaba1 100644 --- a/app/workers/stuck_ci_builds_worker.rb +++ b/app/workers/stuck_ci_builds_worker.rb @@ -14,5 +14,8 @@ class StuckCiBuildsWorker Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}" build.drop end + + # Update builds that failed to drop + builds.update_all(status: 'failed') end end -- cgit v1.2.1 From 93f32b25d23525138ec910f725ebd545ce7ef125 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 14:57:41 +0100 Subject: Add bundler-audit to CI Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 8 ++++++++ Gemfile | 1 + Gemfile.lock | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94753093540..acba37039aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -87,3 +87,11 @@ flay: tags: - ruby - mysql + +bundler:audit + script: + - bundle exec bundle-audit update + - bundle exec bundle-audit check + tags: + - ruby + - mysql diff --git a/Gemfile b/Gemfile index 8a19885bcb1..56f341aac30 100644 --- a/Gemfile +++ b/Gemfile @@ -261,6 +261,7 @@ group :development, :test do gem 'simplecov', '~> 0.10.0', require: false gem 'flog', require: false gem 'flay', require: false + gem 'bundler-audit', require: false gem 'benchmark-ips', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 99cdc2a50ae..83d4e6927db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -90,6 +90,9 @@ GEM bullet (4.14.9) activesupport (>= 3.0.0) uniform_notifier (~> 1.9.0) + bundler-audit (0.4.0) + bundler (~> 1.2) + thor (~> 0.18) byebug (6.0.2) cal-heatmap-rails (0.0.1) capybara (2.4.4) @@ -802,6 +805,7 @@ DEPENDENCIES brakeman (= 3.0.1) browser (~> 1.0.0) bullet + bundler-audit byebug cal-heatmap-rails (~> 0.0.1) capybara (~> 2.4.0) -- cgit v1.2.1 From 7ac112d8e814a8e3bb08e24f5af0d8828e38d4e9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:18:59 +0100 Subject: Add few fixes to documentation based on comments from review Signed-off-by: Dmitriy Zaporozhets --- doc/workflow/lfs/lfs_administration.md | 4 ++-- doc/workflow/lfs/manage_large_binaries_with_git_lfs.md | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md index 9fa307a9d5b..5076b2697a3 100644 --- a/doc/workflow/lfs/lfs_administration.md +++ b/doc/workflow/lfs/lfs_administration.md @@ -1,11 +1,11 @@ -## GitLab Git LFS Administration +# GitLab Git LFS Administration Documentation on how to use Git LFS are under [Managing large binary files with Git LFS doc](manage_large_binaries_with_git_lfs.md). ## Requirements * Git LFS is supported in GitLab starting with version 8.2. -* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up +* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up. ## Configuration diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index a93fd3dfb13..210a8f71c3b 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -73,9 +73,10 @@ Check if you have permissions to push to the project or fetch from the project. * Project is not allowed to access the LFS object -Check if the LFS object you are trying to push to the project or fetch from the project is available to the project. +LFS object you are trying to push to the project or fetch from the project is not available to the project anymore. +Probably the object was removed from the server. -* Project is using deprecated LFS API +* Local git repository is using deprecated LFS API ### Invalid status for : 501 -- cgit v1.2.1 From 200c82adade27225caf2035721161a99296050f3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:21:38 +0100 Subject: Fix gitlab-ci.yml syntax Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index acba37039aa..38fd7b9ac1f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -88,10 +88,10 @@ flay: - ruby - mysql -bundler:audit +bundler:audit: script: - - bundle exec bundle-audit update - - bundle exec bundle-audit check + - "bundle exec bundle-audit update" + - "bundle exec bundle-audit check" tags: - ruby - mysql -- cgit v1.2.1 From b6ed935dcddef1b458b3431dae7a1e8e250e081f Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 23 Nov 2015 15:45:16 +0100 Subject: Allow bundler:audit to fail Signed-off-by: Dmitriy Zaporozhets --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38fd7b9ac1f..e8290fb36b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -95,3 +95,4 @@ bundler:audit: tags: - ruby - mysql + allow_failure: true -- cgit v1.2.1 From e4f3d05c74292ae2dbc3563db38325d982cd6f99 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 23 Nov 2015 18:35:16 +0100 Subject: Add tests for (Create|Update)ReleaseService --- spec/services/create_release_service_spec.rb | 34 ++++++++++++++++++++++++++++ spec/services/update_release_service_spec.rb | 34 ++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 spec/services/create_release_service_spec.rb create mode 100644 spec/services/update_release_service_spec.rb diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb new file mode 100644 index 00000000000..26d7f365bbb --- /dev/null +++ b/spec/services/create_release_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe CreateReleaseService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:service) { CreateReleaseService.new(project, user) } + + it 'creates a new release' do + result = service.execute(tag_name, description) + expect(result[:status]).to eq(:success) + release = project.releases.find_by(tag: tag_name) + expect(release).not_to be_nil + expect(release.description).to eq(description) + end + + it 'raises an error if the tag does not exist' do + result = service.execute("foobar", description) + expect(result[:status]).to eq(:error) + end + + context 'there already exists a release on a tag' do + before do + service.execute(tag_name, description) + end + + it 'raises an error and does not update the release' do + result = service.execute(tag_name, 'The best release!') + expect(result[:status]).to eq(:error) + expect(project.releases.find_by(tag: tag_name).description).to eq(description) + end + end +end diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb new file mode 100644 index 00000000000..93368c45b88 --- /dev/null +++ b/spec/services/update_release_service_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe UpdateReleaseService do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:tag_name) { project.repository.tag_names.first } + let(:description) { 'Awesome release!' } + let(:new_description) { 'The best release!' } + let(:service) { UpdateReleaseService.new(project, user) } + + context 'with an existing release' do + let(:create_service) { CreateReleaseService.new(project, user) } + + before do + create_service.execute(tag_name, description) + end + + it 'successfully updates an existing release' do + result = service.execute(tag_name, new_description) + expect(result[:status]).to eq(:success) + expect(project.releases.find_by(tag: tag_name).description).to eq(new_description) + end + end + + it 'raises an error if the tag does not exist' do + result = service.execute("foobar", description) + expect(result[:status]).to eq(:error) + end + + it 'raises an error if the release does not exist' do + result = service.execute(tag_name, description) + expect(result[:status]).to eq(:error) + end +end -- cgit v1.2.1 From 62303bfad1268dbb466f0bfa68fef40958c6d287 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 23 Nov 2015 16:51:16 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 1e39a9da59b..eb5badf6a96 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,8 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + +v 8.2.1 - Forcefully update builds that didn't want to update with state machine - Fix: saving GitLabCiService as Admin Template -- cgit v1.2.1 From d6e0a1883b9b0eb0faabc77f6c439d981e64a84a Mon Sep 17 00:00:00 2001 From: Anton Davydov Date: Tue, 24 Nov 2015 02:19:17 +0300 Subject: Fix typos in all docs [skip ci] --- doc/api/settings.md | 2 +- doc/development/db_dump.md | 2 +- doc/raketasks/backup_restore.md | 4 ++-- doc/release/monthly.md | 2 +- doc/web_hooks/web_hooks.md | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/settings.md b/doc/api/settings.md index d1b93a09c02..96867c67915 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -57,7 +57,7 @@ Parameters: - `default_project_visibility` - what visibility level new project receives - `default_snippet_visibility` - what visibility level new snippet receives - `restricted_signup_domains` - force people to use only corporate emails for signup -- `user_oauth_applications` - allow users to create oauth applicaitons +- `user_oauth_applications` - allow users to create oauth applications - `after_sign_out_path` - where redirect user after logout All parameters are optional. You can send only one that you want to change. diff --git a/doc/development/db_dump.md b/doc/development/db_dump.md index 21f1b3edecd..e4ff72aa349 100644 --- a/doc/development/db_dump.md +++ b/doc/development/db_dump.md @@ -1,4 +1,4 @@ -# Importing a database dump into a staging enviroment +# Importing a database dump into a staging environment Sometimes it is useful to import the database from a production environment into a staging environment for testing. The procedure below assumes you have diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 4e645b21a85..014f6a12dfc 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -360,8 +360,8 @@ If you are using backup restore procedures you might encounter the following war ``` psql:/var/opt/gitlab/backups/db/database.sql:22: ERROR: must be owner of extension plpgsql -psql:/var/opt/gitlab/backups/db/database.sql:2931: WARNING: no privileges could be revoked for "public" (two occurences) -psql:/var/opt/gitlab/backups/db/database.sql:2933: WARNING: no privileges were granted for "public" (two occurences) +psql:/var/opt/gitlab/backups/db/database.sql:2931: WARNING: no privileges could be revoked for "public" (two occurrences) +psql:/var/opt/gitlab/backups/db/database.sql:2933: WARNING: no privileges were granted for "public" (two occurrences) ``` diff --git a/doc/release/monthly.md b/doc/release/monthly.md index aff3f066b24..907c19e65a0 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -176,7 +176,7 @@ Tweet about the RC release: 1. Also check the CI changelog 1. Add a proposed tweet text to the blog post WIP MR description. 1. Create a WIP MR for the blog post -1. Make sure merge request title starts with `WIP` so it can not be accidently merged until ready. +1. Make sure merge request title starts with `WIP` so it can not be accidentally merged until ready. 1. Ask Dmitriy (or a team member with OS X) to add screenshots to the WIP MR. 1. Decide with core team who will be the MVP user. 1. Create WIP MR for adding MVP to MVP page on website diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 7d838187a26..d24aa0293cf 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -184,7 +184,7 @@ X-Gitlab-Event: Note Hook { "object_kind": "note", "user": { - "name": "Adminstrator", + "name": "Administrator", "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, @@ -337,7 +337,7 @@ X-Gitlab-Event: Note Hook { "object_kind": "note", "user": { - "name": "Adminstrator", + "name": "Administrator", "username": "root", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon" }, -- cgit v1.2.1 From 97f8c6279fc39c4bad87bb880eba04802f6d351d Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 19 Nov 2015 12:33:58 +0100 Subject: Added total query time to Sherlock This makes it easier to see if a problem is caused by slow queries or slow Ruby code (unrelated to any SQL queries that might be used). --- app/views/sherlock/transactions/_general.html.haml | 6 ++++++ config/locales/sherlock.en.yml | 1 + lib/gitlab/sherlock/transaction.rb | 5 +++++ spec/lib/gitlab/sherlock/transaction_spec.rb | 13 +++++++++++++ 4 files changed, 25 insertions(+) diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml index 4287a0c3203..8533b130da6 100644 --- a/app/views/sherlock/transactions/_general.html.haml +++ b/app/views/sherlock/transactions/_general.html.haml @@ -25,6 +25,12 @@ %strong = @transaction.duration.round(2) = t('sherlock.seconds') + %li + %span.light + #{t('sherlock.query_time')} + %strong + = @transaction.query_duration.round(2) + = t('sherlock.seconds') %li %span.light #{t('sherlock.finished_at')}: diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml index 683b09dc329..f24b825f585 100644 --- a/config/locales/sherlock.en.yml +++ b/config/locales/sherlock.en.yml @@ -35,3 +35,4 @@ en: events: Events percent: '%' count: Count + query_time: Query Time diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb index d87a4c9bb4a..3489fb251b6 100644 --- a/lib/gitlab/sherlock/transaction.rb +++ b/lib/gitlab/sherlock/transaction.rb @@ -36,6 +36,11 @@ module Gitlab @duration ||= started_at && finished_at ? finished_at - started_at : 0 end + # Returns the total query duration in seconds. + def query_duration + @query_duration ||= @queries.map { |q| q.duration }.inject(:+) / 1000.0 + end + def to_param @id end diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb index bb49fb65cf8..fb80c62c794 100644 --- a/spec/lib/gitlab/sherlock/transaction_spec.rb +++ b/spec/lib/gitlab/sherlock/transaction_spec.rb @@ -84,6 +84,19 @@ describe Gitlab::Sherlock::Transaction do end end + describe '#query_duration' do + it 'returns the total query duration in seconds' do + time = Time.now + query1 = Gitlab::Sherlock::Query.new('SELECT 1', time, time + 5) + query2 = Gitlab::Sherlock::Query.new('SELECT 2', time, time + 2) + + transaction.queries << query1 + transaction.queries << query2 + + expect(transaction.query_duration).to be_within(0.1).of(7.0) + end + end + describe '#to_param' do it 'returns the transaction ID' do expect(transaction.to_param).to eq(transaction.id) -- cgit v1.2.1 From d1a73563985a2fdaf7a99fc469c38e957d238fde Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 11:36:53 +0100 Subject: Fix ultra-light color for small text Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/typography.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index ba0312ba0db..2c4a58c8db1 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -256,3 +256,9 @@ textarea.js-gfm-input { .strikethrough { text-decoration: line-through; } + +h1, h2, h3, h4 { + small { + color: $gl-gray; + } +} -- cgit v1.2.1 From 9595ec036a0f6f0d2e18e47094431b5a1b6d427f Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 12:11:31 +0100 Subject: Maybe rescue session_expire_delay by setting a default value. --- config/initializers/session_store.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index d7c5432da76..eb534625e33 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -7,6 +7,7 @@ include Gitlab::CurrentSettings begin Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay rescue + Settings.gitlab['session_expire_delay'] ||= 10080 end unless Rails.env.test? -- cgit v1.2.1 From 01bd7baf4fbf23507f6adcdaebf3946b892eb001 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 12:21:11 +0100 Subject: Spell out commands for omnibus-gitlab packages. --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 007e410b677..446eb1189ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,10 +47,10 @@ Please send a merge request with a tested solution or a merge request with a fai 1. **Observed behavior** 1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise. 1. **Output of checks** - * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing + * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing * Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md) * Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page) - * Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) + * Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) 1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem ## Merge requests -- cgit v1.2.1 From 0985032132a2dfa02f2968a3e959df8629b77b12 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 24 Nov 2015 15:57:28 +0100 Subject: Also fallback to a default value if none is set. --- config/initializers/session_store.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index eb534625e33..f30178ff711 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -3,9 +3,9 @@ require 'gitlab/current_settings' include Gitlab::CurrentSettings -# allow it to fail: it may to do so when create_from_defaults is executed before migrations are actually done +# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done begin - Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay + Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay || 10080 rescue Settings.gitlab['session_expire_delay'] ||= 10080 end -- cgit v1.2.1 From 4caef63d82143518e6f23ec6183021657d7ed1ae Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:13:21 +0100 Subject: Fix 500 error when update group member permission Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + .../groups/group_members/_group_member.html.haml | 2 +- app/views/groups/group_members/update.js.haml | 2 +- features/groups.feature | 7 +++ features/steps/groups.rb | 51 +++++++++++++++++----- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 90ade156caf..8916b9f0403 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.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + - Fix 500 error when update group member permission v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index be94b1abc11..bae67552a10 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -30,7 +30,7 @@ - if should_user_see_group_roles?(current_user, @group) %span.pull-right - %strong= member.human_access + %strong.member-access-level= member.human_access - if show_controls - if can?(current_user, :update_group_member, member) = button_tag class: "btn-xs btn js-toggle-button", diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml index 5bad48abafd..df726e2b2b9 100644 --- a/app/views/groups/group_members/update.js.haml +++ b/app/views/groups/group_members/update.js.haml @@ -1,2 +1,2 @@ :plain - $("##{dom_id(@member)}").replaceWith('#{escape_javascript(render(@member, member: @member, show_controls: true))}'); + $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member, show_controls: true))}'); diff --git a/features/groups.feature b/features/groups.feature index abf3769a844..938e658f2a9 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -74,6 +74,13 @@ Feature: Groups When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" + @javascript + Scenario: Edit group member permissions + Given "Mary Jane" is guest of group "Owned" + And I visit group "Owned" members page + When I change the "Mary Jane" role to "Developer" + Then I should see "Mary Jane" as "Developer" + # Leave @javascript diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 9c0313537b1..1d530a25283 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -26,7 +26,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'Group "Owned" has a public project "Public-project"' do - group = Group.find_by(name: "Owned") + group = owned_group @project = create :empty_project, :public, group: group, @@ -91,7 +91,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see group "Owned" projects list' do - Group.find_by(name: "Owned").projects.each do |project| + owned_group.projects.each do |project| expect(page).to have_link project.name end end @@ -172,12 +172,12 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I change group "Owned" avatar' do attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')) click_button "Save group" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I should see new group "Owned" avatar' do - expect(Group.find_by(name: "Owned").avatar).to be_instance_of AvatarUploader - expect(Group.find_by(name: "Owned").avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif" + expect(owned_group.avatar).to be_instance_of AvatarUploader + expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif" end step 'I should see the "Remove avatar" button' do @@ -187,16 +187,16 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I have group "Owned" avatar' do attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')) click_button "Save group" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I remove group "Owned" avatar' do click_link "Remove avatar" - Group.find_by(name: "Owned").reload + owned_group.reload end step 'I should not see group "Owned" avatar' do - expect(Group.find_by(name: "Owned").avatar?).to eq false + expect(owned_group.avatar?).to eq false end step 'I should not see the "Remove avatar" button' do @@ -295,18 +295,49 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end end + step 'I change the "Mary Jane" role to "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + find(".js-toggle-button").click + page.within "#edit_group_member_#{member.id}" do + select 'Developer', from: 'group_member_access_level' + click_on 'Save' + end + end + end + + step 'I should see "Mary Jane" as "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + page.within '.member-access-level' do + expect(page).to have_content "Developer" + end + end + end + protected + def owned_group + @owned_group ||= Group.find_by(name: "Owned") + end + + def mary_jane_member + user = User.find_by(name: "Mary Jane") + member = owned_group.members.where(user_id: user.id).first + end + def assigned_to_me(key) project.send(key).where(assignee_id: current_user.id) end def project - Group.find_by(name: "Owned").projects.first + owned_group.projects.first end def group_milestone - group = Group.find_by(name: "Owned") + group = owned_group @project1 = create :project, group: group -- cgit v1.2.1 From cb9f573818098b2fdc556db867bd9a32f469d471 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:17:11 +0100 Subject: Fix rubocop complain Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 1d530a25283..5086a9e0b87 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -325,7 +325,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def mary_jane_member user = User.find_by(name: "Mary Jane") - member = owned_group.members.where(user_id: user.id).first + owned_group.members.where(user_id: user.id).first end def assigned_to_me(key) -- cgit v1.2.1 From cbaa33db8a77efaf6446ed294db2e334ff7de8e7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 17:40:41 +0100 Subject: Small code improvement Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 5086a9e0b87..7c991af4c2b 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -325,7 +325,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def mary_jane_member user = User.find_by(name: "Mary Jane") - owned_group.members.where(user_id: user.id).first + owned_group.members.find_by(user_id: user.id) end def assigned_to_me(key) -- cgit v1.2.1 From 776027e4c7a4e7ff5df18e92cd79461a7d5de73e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:36:36 -0500 Subject: Bump gon to ~> 6.0.1 Closes #2856 --- Gemfile | 2 +- Gemfile.lock | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..3bb9448e405 100644 --- a/Gemfile +++ b/Gemfile @@ -193,7 +193,7 @@ gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' gem 'font-awesome-rails', '~> 4.2' gem 'gitlab_emoji', '~> 0.1' -gem 'gon', '~> 5.0.0' +gem 'gon', '~> 6.0.1' gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-rails', '~> 3.1.3' gem 'jquery-scrollto-rails', '~> 1.4.3' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..70c532f05de 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -312,9 +312,11 @@ GEM rouge (~> 1.10.1) sanitize (~> 2.1.0) stringex (~> 2.5.1) - gon (5.0.4) - actionpack (>= 2.3.0) + gon (6.0.1) + actionpack (>= 3.0) json + multi_json + request_store (>= 1.0) grape (0.13.0) activesupport builder @@ -846,7 +848,7 @@ DEPENDENCIES gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.0.2) - gon (~> 5.0.0) + gon (~> 6.0.1) grape (~> 0.13.0) grape-entity (~> 0.4.2) haml-rails (~> 0.9.0) -- cgit v1.2.1 From 26f9dbf8a95d79eccf6a3dd3b779b7a7bc51c03b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:42:42 -0500 Subject: Bump creole to ~> 0.5.0 Closes #2815 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..bcbdabb5d95 100644 --- a/Gemfile +++ b/Gemfile @@ -95,7 +95,7 @@ gem 'redcarpet', '~> 3.3.3' gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' -gem 'creole', '~>0.3.6' +gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..4d60d8fadc1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -134,7 +134,7 @@ GEM thor (~> 0.19.1) crack (0.4.2) safe_yaml (~> 1.0.0) - creole (0.3.8) + creole (0.5.0) d3_rails (3.5.6) railties (>= 3.1.0) daemons (1.2.3) @@ -816,7 +816,7 @@ DEPENDENCIES colored (~> 1.2) colorize (~> 0.5.8) coveralls (~> 0.8.2) - creole (~> 0.3.6) + creole (~> 0.5.0) d3_rails (~> 3.5.5) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) -- cgit v1.2.1 From 446a95d3079e8927dd40c8d41a58d66192721cfc Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 15:48:49 -0500 Subject: Bump rack-oauth2 to ~> 1.2.1 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..0436e930653 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' -gem 'rack-oauth2', '~> 1.0.5' +gem 'rack-oauth2', '~> 1.2.1' # Two-factor authentication gem 'devise-two-factor', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..ecd83b6979a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -357,7 +357,7 @@ GEM httparty (0.13.5) json (~> 1.8) multi_xml (>= 0.5.2) - httpclient (2.6.0.1) + httpclient (2.7.0.1) i18n (0.7.0) ice_cube (0.11.1) ice_nine (0.11.1) @@ -502,7 +502,7 @@ GEM rack-cors (0.4.0) rack-mount (0.8.3) rack (>= 1.0.0) - rack-oauth2 (1.0.10) + rack-oauth2 (1.2.1) activesupport (>= 2.3) attr_required (>= 0.0.5) httpclient (>= 2.4) @@ -889,7 +889,7 @@ DEPENDENCIES quiet_assets (~> 1.0.2) rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) - rack-oauth2 (~> 1.0.5) + rack-oauth2 (~> 1.2.1) rails (= 4.1.14) raphael-rails (~> 2.1.2) rblineprof -- cgit v1.2.1 From b9828278127724ff5eac60eb71e36f675b9985cd Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Tue, 24 Nov 2015 20:55:18 +0000 Subject: Add reference to Microsoft's Git Credential Manager for Windows. --- doc/workflow/lfs/manage_large_binaries_with_git_lfs.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md index 210a8f71c3b..b59e92cb317 100644 --- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md +++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md @@ -121,6 +121,6 @@ git config --global credential.helper 'cache --timeout=3600' This will remember the credentials for an hour after which Git operations will require re-authentication. -If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, `wincred` is available. +If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases). -More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). +More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage). \ No newline at end of file -- cgit v1.2.1 From a2915aeb662e30537f3f226e3db6d85fd252905f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:12:25 -0500 Subject: Rescue StandardError, not Exception --- app/services/merge_requests/merge_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 7963af127e1..d619b72e3c2 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -38,7 +38,7 @@ module MergeRequests } repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options) - rescue Exception => e + rescue StandardError => e merge_request.update(merge_error: "Something went wrong during merge") Rails.logger.error(e.message) return false -- cgit v1.2.1 From 28de6770fae44f65aa81538ff660fc7050072070 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:29:21 -0500 Subject: Bump colorize to ~> 0.7.0 Also removes `colored` which came in during the CI merge and is redundant. Closes #2822 --- Gemfile | 3 +-- Gemfile.lock | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index bd8e9bd8fa1..b7ec16198c9 100644 --- a/Gemfile +++ b/Gemfile @@ -125,8 +125,7 @@ gem 'sidetiq', '~> 0.6.3' gem "httparty", '~> 0.13.3' # Colored output to console -gem "colored", '~> 1.2' -gem "colorize", '~> 0.5.8' +gem "colorize", '~> 0.7.0' # GitLab settings gem 'settingslogic', '~> 2.0.9' diff --git a/Gemfile.lock b/Gemfile.lock index 448941cfc8d..20fc885481a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -123,8 +123,7 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) - colored (1.2) - colorize (0.5.8) + colorize (0.7.7) connection_pool (2.2.0) coveralls (0.8.2) json (~> 1.8) @@ -813,8 +812,7 @@ DEPENDENCIES carrierwave (~> 0.9.0) charlock_holmes (~> 0.7.3) coffee-rails (~> 4.1.0) - colored (~> 1.2) - colorize (~> 0.5.8) + colorize (~> 0.7.0) coveralls (~> 0.8.2) creole (~> 0.3.6) d3_rails (~> 3.5.5) -- cgit v1.2.1 From af65046c5fd9f214124d39a5582b06ee2fe39933 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 23:52:02 -0500 Subject: Move HTTP/SSH clone button logic to helpers --- app/helpers/button_helper.rb | 35 +++++++++++++++++++++++++++++++++ app/views/shared/_clone_panel.html.haml | 18 ++--------------- 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 app/helpers/button_helper.rb diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb new file mode 100644 index 00000000000..8963e96ea1d --- /dev/null +++ b/app/helpers/button_helper.rb @@ -0,0 +1,35 @@ +module ButtonHelper + def http_clone_button(project) + klass = 'btn' + klass << ' active' if default_clone_protocol == 'http' + klass << ' has_tooltip' if current_user.try(:require_password?) + + protocol = gitlab_config.protocol.upcase + + content_tag :button, protocol, + class: klass, + data: { + clone: project.http_url_to_repo, + container: 'body', + html: 'true', + title: "Set a password on your account
    to pull or push via #{protocol}" + }, + type: :button + end + + def ssh_clone_button(project) + klass = 'btn' + klass << ' active' if default_clone_protocol == 'ssh' + klass << ' has_tooltip' if current_user.try(:require_ssh_key?) + + content_tag :button, 'SSH', + class: klass, + data: { + clone: project.ssh_url_to_repo, + container: 'body', + html: 'true', + title: 'Add an SSH key to your profile
    to pull or push via SSH.' + }, + type: :button + end +end diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 8bcb24ae9df..a82971157d3 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -2,23 +2,9 @@ .git-clone-holder.input-group .input-group-addon.git-protocols .input-group-btn - %button{ | - type: 'button', | - class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | - :"data-clone" => project.ssh_url_to_repo, | - :"data-title" => "Add an SSH key to your profile
    to pull or push via SSH.", - :"data-html" => "true", - :"data-container" => "body"} - SSH + = ssh_clone_button(project) .input-group-btn - %button{ | - type: 'button', | - class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | - :"data-clone" => project.http_url_to_repo, | - :"data-title" => "Set a password on your account
    to pull or push via #{gitlab_config.protocol.upcase}.", - :"data-html" => "true", - :"data-container" => "body"} - = gitlab_config.protocol.upcase + = http_clone_button(project) = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true - if project.kind_of?(Project) .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } -- cgit v1.2.1 From acc0f162c864d2a061461467473fca8761b6611f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 22 Nov 2015 23:53:55 -0500 Subject: Move clipboard_button helper to ButtonHelper module --- app/helpers/button_helper.rb | 7 +++++++ app/helpers/clipboard_helper.rb | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 app/helpers/clipboard_helper.rb diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index 8963e96ea1d..bbb35afca89 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -1,4 +1,11 @@ module ButtonHelper + def clipboard_button + content_tag :button, + icon('clipboard'), + class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + type: :button + end + def http_clone_button(project) klass = 'btn' klass << ' active' if default_clone_protocol == 'http' diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb deleted file mode 100644 index 3c1d7569fac..00000000000 --- a/app/helpers/clipboard_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ -module ClipboardHelper - def clipboard_button - content_tag :button, - icon('clipboard'), - class: 'btn btn-xs btn-clipboard js-clipboard-trigger', - type: :button - end -end -- cgit v1.2.1 From 7dab8ed739359bc579d8bc4d3de61816993ca57d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 18:35:24 -0500 Subject: Rework the copy_to_clipboard logic It needed to be more flexible in how we set the target text or element. --- app/assets/javascripts/copy_to_clipboard.js.coffee | 57 ++++++++++++---------- app/helpers/button_helper.rb | 20 +++++++- app/views/projects/commits/_commit.html.haml | 4 +- app/views/projects/issues/_discussion.html.haml | 4 +- .../projects/merge_requests/_discussion.html.haml | 4 +- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee index 9c68c5cc1bc..24301e01b10 100644 --- a/app/assets/javascripts/copy_to_clipboard.js.coffee +++ b/app/assets/javascripts/copy_to_clipboard.js.coffee @@ -1,32 +1,37 @@ #= require clipboard -$ -> - clipboard = new Clipboard '.js-clipboard-trigger', - text: (trigger) -> - $target = $(trigger.nextElementSibling || trigger.previousElementSibling) - $target.data('clipboard-text') || $target.text().trim() +genericSuccess = (e) -> + showTooltip(e.trigger, 'Copied!') + + # Clear the selection and blur the trigger so it loses its border + e.clearSelection() + $(e.trigger).blur() - clipboard.on 'success', (e) -> - $(e.trigger). - tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!'). - tooltip('show'). - one('mouseleave', -> $(this).tooltip('hide')) +# Safari doesn't support `execCommand`, so instead we inform the user to +# copy manually. +# +# See http://clipboardjs.com/#browser-support +genericError = (e) -> + if /Mac/i.test(navigator.userAgent) + key = '⌘' # Command + else + key = 'Ctrl' - # Clear the selection and blur the trigger so it loses its border - e.clearSelection() - $(e.trigger).blur() + showTooltip(e.trigger, "Press #{key}-C to copy") - # Safari doesn't support `execCommand`, so instead we inform the user to - # copy manually. - # - # See http://clipboardjs.com/#browser-support - clipboard.on 'error', (e) -> - if /Mac/i.test(navigator.userAgent) - title = "Press ⌘-C to copy" - else - title = "Press Ctrl-C to copy" +showTooltip = (target, title) -> + $(target). + tooltip( + container: 'body' + html: 'true' + placement: 'auto bottom' + title: title + trigger: 'manual' + ). + tooltip('show'). + one('mouseleave', -> $(this).tooltip('hide')) - $(e.trigger). - tooltip(trigger: 'manual', placement: 'auto bottom', html: true, title: title). - tooltip('show'). - one('mouseleave', -> $(this).tooltip('hide')) +$ -> + clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]' + clipboard.on 'success', genericSuccess + clipboard.on 'error', genericError diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index bbb35afca89..b9bb1ac8d88 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -1,8 +1,24 @@ module ButtonHelper - def clipboard_button + # Output a "Copy to Clipboard" button + # + # data - Data attributes passed to `content_tag` + # + # Examples: + # + # # Define the clipboard's text + # clipboard_button(clipboard_text: "Foo") + # # => "" + # + # # Define the target element + # clipboard_button(clipboard_target: "#foo") + # # => "" + # + # See http://clipboardjs.com/#usage + def clipboard_button(data = {}) content_tag :button, icon('clipboard'), - class: 'btn btn-xs btn-clipboard js-clipboard-trigger', + class: 'btn btn-xs btn-clipboard', + data: data, type: :button end diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 805be332e64..2e489a0a4d5 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -20,8 +20,8 @@ - if ci_commit = render_ci_status(ci_commit)   - = clipboard_button - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id} + = clipboard_button(clipboard_text: commit.id) + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" .notes_count - if note_count > 0 diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 020952dd001..8f0a1ed9be2 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -18,9 +18,9 @@ = link_to_member(@project, participant, name: false, size: 24) .col-md-3 .input-group.cross-project-reference - %span.slead.has_tooltip{title: 'Cross-project reference'} + %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @issue) - = clipboard_button + = clipboard_button(clipboard_target: '#cross-project-reference') .row %section.col-md-9 diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index cb75bd8c5ba..2b3c3eff5e4 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -15,9 +15,9 @@ = render "projects/merge_requests/show/participants" .col-md-3 .input-group.cross-project-reference - %span.slead.has_tooltip{title: 'Cross-project reference'} + %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @merge_request) - = clipboard_button + = clipboard_button(clipboard_target: '#cross-project-reference') .row %section.col-md-9 -- cgit v1.2.1 From ec002e802677a691d758ebc6f94f23020b2b063a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 19:20:23 -0500 Subject: Remove usage of Colored --- lib/tasks/gitlab/task_helpers.rake | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index c95b6540ebc..efb863a8764 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -2,16 +2,6 @@ module Gitlab class TaskAbortedByUserError < StandardError; end end -unless STDOUT.isatty - module Colored - extend self - - def colorize(string, options={}) - string - end - end -end - namespace :gitlab do # Ask if the user wants to continue @@ -103,7 +93,7 @@ namespace :gitlab do gitlab_user = Gitlab.config.gitlab.user current_user = run(%W(whoami)).chomp unless current_user == gitlab_user - puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}" + puts " Warning ".colorize(:black).on_yellow puts " You are running as user #{current_user.magenta}, we hope you know what you are doing." puts " Things may work\/fail for the wrong reasons." puts " For correct results you should run this as user #{gitlab_user.magenta}." -- cgit v1.2.1 From 1d80cd315e403e02f85a99c35eb9e83f4d829f8d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 19:32:06 -0500 Subject: Add clipboard button to project clone panel Closes #3585 --- app/assets/javascripts/project.js.coffee | 10 ++++------ app/assets/stylesheets/pages/projects.scss | 7 ++++++- app/helpers/button_helper.rb | 4 ++-- app/helpers/projects_helper.rb | 3 +-- app/views/shared/_clone_panel.html.haml | 2 ++ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 0ea8fffce07..d37a00bdedc 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -3,11 +3,9 @@ class @Project # Git clone panel switcher cloneHolder = $('.git-clone-holder') if cloneHolder.length - $('a, button', cloneHolder).click -> - $('a, button', cloneHolder).removeClass 'active' - $(@).addClass 'active' - $('#project_clone', cloneHolder).val $(@).data 'clone' - $(".clone").text("").append $(@).data 'clone' + $('.js-protocol-switch', cloneHolder).click -> + $('.js-protocol-switch', cloneHolder).toggleClass('active') + $('#project_clone').val($(@).data('clone')) # Ref switcher $('.project-refs-select').on 'change', -> @@ -39,4 +37,4 @@ class @Project when 4 then label = ' On Mention ' $('#notifications-button').empty().append("" + label + "") $(@).parents('ul').find('li.active').removeClass 'active' - $(@).parent().addClass 'active' \ No newline at end of file + $(@).parent().addClass 'active' diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index d3b10040022..ad607ead2bf 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -178,6 +178,11 @@ &:active { outline: none; } + + &.btn-clipboard { + padding-left: 15px; + padding-right: 15px; + } } .active { @@ -552,4 +557,4 @@ pre.light-well { z-index: 100; position: relative; } -} \ No newline at end of file +} diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index b9bb1ac8d88..313b6dde910 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -23,7 +23,7 @@ module ButtonHelper end def http_clone_button(project) - klass = 'btn' + klass = 'btn js-protocol-switch' klass << ' active' if default_clone_protocol == 'http' klass << ' has_tooltip' if current_user.try(:require_password?) @@ -41,7 +41,7 @@ module ButtonHelper end def ssh_clone_button(project) - klass = 'btn' + klass = 'btn js-protocol-switch' klass << ' active' if default_clone_protocol == 'ssh' klass << ' has_tooltip' if current_user.try(:require_ssh_key?) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index c9cd4a0d54c..c0c51aae039 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -173,8 +173,7 @@ module ProjectsHelper 'unknown' end - def default_url_to_repo(project = nil) - project = project || @project + def default_url_to_repo(project = @project) current_user ? project.url_to_repo : project.http_url_to_repo end diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index a82971157d3..408a46aaafc 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -6,6 +6,8 @@ .input-group-btn = http_clone_button(project) = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true + .input-group-btn + = clipboard_button(clipboard_target: '#project_clone') - if project.kind_of?(Project) .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } .visibility-level-label -- cgit v1.2.1 From 637e64c24472a06b32091cb45f1b81260c5e6ec0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 20:38:36 -0500 Subject: Clean up the Git protocol switcher JS Also re-adds the `.clone` updating that was removed accidentally. Thanks, tests! --- app/assets/javascripts/project.js.coffee | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index d37a00bdedc..ec919f0cd67 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -1,11 +1,19 @@ class @Project constructor: -> - # Git clone panel switcher - cloneHolder = $('.git-clone-holder') - if cloneHolder.length - $('.js-protocol-switch', cloneHolder).click -> - $('.js-protocol-switch', cloneHolder).toggleClass('active') - $('#project_clone').val($(@).data('clone')) + # Git protocol switcher + $('.js-protocol-switch').click -> + return if $(@).hasClass('active') + + # Toggle 'active' for both buttons + $('.js-protocol-switch').toggleClass('active') + + url = $(@).data('clone') + + # Update the input field + $('#project_clone').val(url) + + # Update the command line instructions + $('.clone').text(url) # Ref switcher $('.project-refs-select').on 'change', -> -- cgit v1.2.1 From 767bd6f8825661c2cd170172f2b0d5ce34c67a93 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 24 Nov 2015 16:18:14 -0500 Subject: Enable the Lint/RescueException cop --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index 11e4502849a..d59edbc8b17 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -888,7 +888,7 @@ Lint/RequireParentheses: Lint/RescueException: Description: 'Avoid rescuing the Exception class.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' - Enabled: false + Enabled: true Lint/ShadowingOuterLocalVariable: Description: >- -- cgit v1.2.1 From d9749c8d36750136cbd8989918b1fec8ff2c4b49 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 16:51:01 +0100 Subject: Improve UI for group members page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 5 ++ app/assets/stylesheets/framework/pagination.scss | 4 ++ app/assets/stylesheets/pages/groups.scss | 4 +- .../groups/group_members/_group_member.html.haml | 4 +- app/views/groups/group_members/index.html.haml | 58 +++++++++++----------- 5 files changed, 43 insertions(+), 32 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 45f3b5849bf..a798ae812e3 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -127,3 +127,8 @@ ul.content-list { } } +.panel > .content-list { + li { + margin: 0; + } +} diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss index 6677f94dafd..2cd30491bf5 100644 --- a/app/assets/stylesheets/framework/pagination.scss +++ b/app/assets/stylesheets/framework/pagination.scss @@ -32,3 +32,7 @@ } } } + +.panel > .gl-pagination { + margin: 0; +} diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index 07a38a19fad..bcb73a9acd3 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -1,5 +1,5 @@ .new-group-member-holder { - margin-top: 50px; + margin-top: 10px; padding-top: 20px; } @@ -15,4 +15,4 @@ .form-control { height: 42px; } -} \ No newline at end of file +} diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index bae67552a10..a79a0fcdc8e 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -4,7 +4,7 @@ %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %span{class: ("list-item-name" if show_controls)} - if member.user - = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 24), class: "avatar s24", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username @@ -14,7 +14,7 @@ %label.label.label-danger %strong Blocked - else - = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: '' %strong = member.invite_email %span.cgray diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index d4ad33a8bf1..b3a811b2669 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,38 +1,40 @@ +- @blank_container = true - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) -- if should_user_see_group_roles?(current_user, @group) - %p.light - Members of group have access to all group projects. - Read more about permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" -.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', spellcheck: false } - = button_tag 'Search', class: 'btn' - - if current_user && current_user.can?(:admin_group_member, @group) - .pull-right - = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do - Add members - %i.fa.fa-chevron-down - - .js-toggle-content.hide.new-group-member-holder - = render "new_group_member" -.panel.panel-default.prepend-top-20 - .panel-heading - %strong #{@group.name} - group members - %small - (#{@members.total_count}) - %ul.well-list - - @members.each do |member| - = render 'groups/group_members/group_member', member: member, show_controls: true +.group-members-page + - if current_user && current_user.can?(:admin_group_member, @group) + .panel.panel-default + .panel-heading + Add new user to group + .panel-body + - if should_user_see_group_roles?(current_user, @group) + %p.light + Members of group have access to all group projects. + Read more about permissions + %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" + .new-group-member-holder + = render "new_group_member" -= paginate @members, theme: 'gitlab' + .panel.panel-default + .panel-heading + %strong #{@group.name} + group members + %small + (#{@members.total_count}) + .pull-right + = 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', spellcheck: false } + = button_tag class: 'btn' do + = icon("search") + %ul.content-list + - @members.each do |member| + = render 'groups/group_members/group_member', member: member, show_controls: true + = paginate @members, theme: 'gitlab' :javascript $('form.member-search-form').on('submit', function(event) { -- cgit v1.2.1 From 34cc8f4a60f25bfa2503f0ad006b047fd2c2f81c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 17:11:45 +0100 Subject: Improve project members page UI Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/groups.scss | 5 --- app/views/groups/group_members/index.html.haml | 7 +--- .../project_members/_group_members.html.haml | 8 ++-- .../project_members/_project_member.html.haml | 4 +- app/views/projects/project_members/_team.html.haml | 16 +++++++- app/views/projects/project_members/index.html.haml | 45 ++++++++-------------- 6 files changed, 36 insertions(+), 49 deletions(-) diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index bcb73a9acd3..263993f59a5 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -1,8 +1,3 @@ -.new-group-member-holder { - margin-top: 10px; - padding-top: 20px; -} - .member-search-form { float: left; } diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index b3a811b2669..c83766e729a 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,9 +1,6 @@ -- @blank_container = true - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) - - - +- @blank_container = true .group-members-page - if current_user && current_user.can?(:admin_group_member, @group) @@ -14,8 +11,6 @@ - if should_user_see_group_roles?(current_user, @group) %p.light Members of group have access to all group projects. - Read more about permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" .new-group-member-holder = render "new_group_member" diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml index 43e92437cf5..0c73d7e34ac 100644 --- a/app/views/projects/project_members/_group_members.html.haml +++ b/app/views/projects/project_members/_group_members.html.haml @@ -4,11 +4,11 @@ group members %small (#{members.count}) - .panel-head-actions - = link_to group_group_members_path(@group), class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o + .pull-right + = link_to group_group_members_path(@group), class: 'btn' do + = icon('pencil-square-o') Edit group members - %ul.well-list + %ul.content-list - members.each do |member| = render 'groups/group_members/group_member', member: member, show_controls: false - if members.count > 20 diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml index f07cd97e63d..05bf3a7ef6a 100644 --- a/app/views/projects/project_members/_project_member.html.haml +++ b/app/views/projects/project_members/_project_member.html.haml @@ -4,7 +4,7 @@ %li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)} %span.list-item-name - if member.user - = image_tag avatar_icon(user, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(user, 24), class: "avatar s24", alt: '' %strong = link_to user.name, user_path(user) %span.cgray= user.username @@ -14,7 +14,7 @@ %label.label.label-danger %strong Blocked - else - = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: '' + = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: '' %strong = member.invite_email %span.cgray diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index b807fb2cc9d..8f4c6134261 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -1,9 +1,21 @@ -.panel.panel-default.prepend-top-20 +.panel.panel-default .panel-heading %strong #{@project.name} project members %small (#{members.count}) - %ul.well-list + .pull-right + = 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', spellcheck: false } + = button_tag class: 'btn' do + = icon("search") + %ul.content-list - members.each do |project_member| = render 'project_member', member: project_member + +:javascript + $('form.member-search-form').on('submit', function (event) { + event.preventDefault(); + Turbolinks.visit(this.action + '?' + $(this).serialize()); + }); diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 9fc4be583cc..29225a36364 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,36 +1,21 @@ - page_title "Members" = render "header_title" +- @blank_container = true -.gray-content-block.top-block - .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', spellcheck: false } - = button_tag 'Search', class: 'btn' - - - if can?(current_user, :admin_project_member, @project) - %span.pull-right - = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do - Add members - %i.fa.fa-chevron-down - = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do - Import members - - .js-toggle-content.hide.new-group-member-holder +.project-members-page + - if can?(current_user, :admin_project_member, @project) + .panel.panel-default + .panel-heading + Add new user to project + .pull-right + = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do + Import members + .panel-body + %p.light + Users with access to this project are listed below. = render "new_project_member" -%p.prepend-top-default.light - Users with access to this project are listed below. - Read more about project permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" - -= render "team", members: @project_members - -- if @group - = render "group_members", members: @group_members + = render "team", members: @project_members -:javascript - $('form.member-search-form').on('submit', function (event) { - event.preventDefault(); - Turbolinks.visit(this.action + '?' + $(this).serialize()); - }); + - if @group + = render "group_members", members: @group_members -- cgit v1.2.1 From ea7467d2be0e367ed1ec6df656cab059a9db6da0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 24 Nov 2015 22:27:01 +0100 Subject: Refactor group members tests a bit Signed-off-by: Dmitriy Zaporozhets --- features/groups.feature | 3 --- features/project/team_management.feature | 6 ++---- features/steps/groups.rb | 29 +++++++++++++---------------- features/steps/project/team_management.rb | 4 ---- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/features/groups.feature b/features/groups.feature index 938e658f2a9..65ced5c529d 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -55,7 +55,6 @@ Feature: Groups Scenario: Add user to group Given gitlab user "Mike" When I visit group "Owned" members page - And I click link "Add members" When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" @@ -63,14 +62,12 @@ Feature: Groups Scenario: Ignore add user to group when is already Owner Given gitlab user "Mike" When I visit group "Owned" members page - And I click link "Add members" When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Owner" @javascript Scenario: Invite user to group When I visit group "Owned" members page - And I click link "Add members" When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" diff --git a/features/project/team_management.feature b/features/project/team_management.feature index 09a7df59df6..06fb45c8bde 100644 --- a/features/project/team_management.feature +++ b/features/project/team_management.feature @@ -13,14 +13,12 @@ Feature: Project Team Management @javascript Scenario: Add user to project - Given I click link "Add members" - And I select "Mike" as "Reporter" + When I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" @javascript Scenario: Invite user to project - Given I click link "Add members" - And I select "sjobs@apple.com" as "Reporter" + When I select "sjobs@apple.com" as "Reporter" Then I should see "sjobs@apple.com" in team list as invited "Reporter" @javascript diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 7c991af4c2b..5de54d9b1ee 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -13,10 +13,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps create(:user, name: "Mike") end - step 'I click link "Add members"' do - find(:css, 'button.btn-new').click - end - step 'I should see group "Owned"' do expect(page).to have_content '@owned' end @@ -60,14 +56,14 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see "Mike" in team list as "Reporter"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('Mike') expect(page).to have_content('Reporter') end end step 'I should see "Mike" in team list as "Owner"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('Mike') expect(page).to have_content('Owner') end @@ -83,7 +79,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do - page.within '.well-list' do + page.within '.content-list' do expect(page).to have_content('sjobs@apple.com') expect(page).to have_content('invited') expect(page).to have_content('Reporter') @@ -114,32 +110,29 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I select user "Mary Jane" from list with role "Reporter"' do user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") - click_button 'Add members' + page.within ".users-group-form" do select2(user.id, from: "#user_ids", multiple: true) select "Reporter", from: "access_level" end + click_button "Add users to group" end step 'I should see user "John Doe" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).to have_content("John Doe") + expect(group_members_list).to have_content("John Doe") end step 'I should not see user "John Doe" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).not_to have_content("John Doe") + expect(group_members_list).not_to have_content("John Doe") end step 'I should see user "Mary Jane" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).to have_content("Mary Jane") + expect(group_members_list).to have_content("Mary Jane") end step 'I should not see user "Mary Jane" in team list' do - projects_with_access = find(".panel .well-list") - expect(projects_with_access).not_to have_content("Mary Jane") + expect(group_members_list).not_to have_content("Mary Jane") end step 'project from group "Owned" has issues assigned to me' do @@ -401,4 +394,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps author: current_user, milestone: milestone2_project3 end + + def group_members_list + find(".panel .content-list") + end end diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb index 97d63016458..caad52def79 100644 --- a/features/steps/project/team_management.rb +++ b/features/steps/project/team_management.rb @@ -15,10 +15,6 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps expect(page).to have_content(user.username) end - step 'I click link "Add members"' do - find(:css, 'button.btn-new').click - end - step 'I select "Mike" as "Reporter"' do user = User.find_by(name: "Mike") -- cgit v1.2.1 From 561634c78f3ab7967709196e8cca39a256d27196 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 10:24:08 +0100 Subject: Refactor group steps Signed-off-by: Dmitriy Zaporozhets --- features/steps/groups.rb | 77 ++++++++++-------------------------------------- 1 file changed, 16 insertions(+), 61 deletions(-) diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 5de54d9b1ee..d99417d178e 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -332,67 +332,22 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def group_milestone group = owned_group - @project1 = create :project, - group: group - project2 = create :project, - path: 'gitlab-ci', - group: group - @project3 = create :project, - path: 'cookbook-gitlab', - group: group - milestone1_project1 = create :milestone, - title: "Version 7.2", - project: @project1 - milestone1_project2 = create :milestone, - title: "Version 7.2", - project: project2 - create :milestone, - title: "Version 7.2", - project: @project3 - milestone2_project1 = create :milestone, - title: "GL-113", - project: @project1 - milestone2_project2 = create :milestone, - title: "GL-113", - project: project2 - milestone2_project3 = create :milestone, - title: "GL-113", - project: @project3, - due_date: '2114-08-20', - description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry' - @issue1 = create :issue, - project: @project1, - assignee: current_user, - author: current_user, - milestone: milestone2_project1 - create :issue, - project: project2, - assignee: current_user, - author: current_user, - milestone: milestone1_project2 - create :issue, - project: @project3, - assignee: current_user, - author: current_user, - milestone: milestone1_project1 - create :merge_request, - source_project: @project1, - target_project: @project1, - assignee: current_user, - author: current_user, - milestone: milestone2_project1 - create :merge_request, - source_project: project2, - target_project: project2, - assignee: current_user, - author: current_user, - milestone: milestone2_project2 - @mr3 = create :merge_request, - source_project: @project3, - target_project: @project3, - assignee: current_user, - author: current_user, - milestone: milestone2_project3 + %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| + project = create :project, path: path, group: group + milestone = create :milestone, title: "Version 7.2", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + + milestone = create :milestone, title: "GL-113", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + end end def group_members_list -- cgit v1.2.1 From d262daa4f0f425ddda189ae9e2eb21bbf287f21a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 11:44:39 +0100 Subject: Split group feature tests Signed-off-by: Dmitriy Zaporozhets --- app/views/groups/group_members/index.html.haml | 2 +- app/views/projects/project_members/_team.html.haml | 2 +- features/group/members.feature | 105 ++++++++++ features/group/milestones.feature | 30 +++ features/groups.feature | 129 ------------ features/steps/group/members.rb | 147 ++++++++++++++ features/steps/group/milestones.rb | 87 ++++++++ features/steps/groups.rb | 226 +-------------------- features/steps/shared/group.rb | 4 + features/steps/shared/user.rb | 4 + 10 files changed, 380 insertions(+), 356 deletions(-) create mode 100644 features/group/members.feature create mode 100644 features/group/milestones.feature create mode 100644 features/steps/group/members.rb create mode 100644 features/steps/group/milestones.rb diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index c83766e729a..335bf036074 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -24,7 +24,7 @@ = 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', spellcheck: false } - = button_tag class: 'btn' do + = button_tag class: 'btn', title: 'Search' do = icon("search") %ul.content-list - @members.each do |member| diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index 8f4c6134261..ccddab13aaf 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -8,7 +8,7 @@ = 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', spellcheck: false } - = button_tag class: 'btn' do + = button_tag class: 'btn', title: 'Search' do = icon("search") %ul.content-list - members.each do |project_member| diff --git a/features/group/members.feature b/features/group/members.feature new file mode 100644 index 00000000000..1f9514bac39 --- /dev/null +++ b/features/group/members.feature @@ -0,0 +1,105 @@ +Feature: Group Members + Background: + Given I sign in as "John Doe" + And "John Doe" is owner of group "Owned" + And "John Doe" is guest of group "Guest" + + @javascript + Scenario: I should add user to group "Owned" + Given User "Mary Jane" exists + When I visit group "Owned" members page + And I select user "Mary Jane" from list with role "Reporter" + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Add user to group + Given gitlab user "Mike" + When I visit group "Owned" members page + When I select "Mike" as "Reporter" + Then I should see "Mike" in team list as "Reporter" + + @javascript + Scenario: Ignore add user to group when is already Owner + Given gitlab user "Mike" + When I visit group "Owned" members page + When I select "Mike" as "Reporter" + Then I should see "Mike" in team list as "Owner" + + @javascript + Scenario: Invite user to group + When I visit group "Owned" members page + When I select "sjobs@apple.com" as "Reporter" + Then I should see "sjobs@apple.com" in team list as invited "Reporter" + + @javascript + Scenario: Edit group member permissions + Given "Mary Jane" is guest of group "Owned" + And I visit group "Owned" members page + When I change the "Mary Jane" role to "Developer" + Then I should see "Mary Jane" as "Developer" + + # Leave + + @javascript + Scenario: Owner should be able to remove himself from group if he is not the last owner + Given "Mary Jane" is owner of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "John Doe" + And I visit group "Owned" members page + Then I should not see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Owner should not be able to remove himself from group if he is the last owner + Given "Mary Jane" is guest of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + Then I should not see the "Remove User From Group" button for "John Doe" + + @javascript + Scenario: Guest should be able to remove himself from group + Given "Mary Jane" is guest of group "Guest" + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "John Doe" + When I visit group "Guest" members page + Then I should not see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + + @javascript + Scenario: Guest should be able to remove himself from group even if he is the only user in the group + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + When I click on the "Remove User From Group" button for "John Doe" + When I visit group "Guest" members page + Then I should not see user "John Doe" in team list + + # Remove others + + Scenario: Owner should be able to remove other users from group + Given "Mary Jane" is owner of group "Owned" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + When I click on the "Remove User From Group" button for "Mary Jane" + When I visit group "Owned" members page + Then I should see user "John Doe" in team list + Then I should not see user "Mary Jane" in team list + + Scenario: Guest should not be able to remove other users from group + Given "Mary Jane" is guest of group "Guest" + When I visit group "Guest" members page + Then I should see user "John Doe" in team list + Then I should see user "Mary Jane" in team list + Then I should not see the "Remove User From Group" button for "Mary Jane" + + Scenario: Search member by name + Given "Mary Jane" is guest of group "Guest" + And I visit group "Guest" members page + When I search for 'Mary' member + Then I should see user "Mary Jane" in team list + Then I should not see user "John Doe" in team list diff --git a/features/group/milestones.feature b/features/group/milestones.feature new file mode 100644 index 00000000000..62ea66a783c --- /dev/null +++ b/features/group/milestones.feature @@ -0,0 +1,30 @@ +Feature: Group Milestones + Background: + Given I sign in as "John Doe" + And "John Doe" is owner of group "Owned" + + Scenario: I should see group "Owned" milestone index page with no milestones + When I visit group "Owned" page + And I click on group milestones + Then I should see group milestones index page has no milestones + + Scenario: I should see group "Owned" milestone index page with milestones + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + Then I should see group milestones index page with milestones + + Scenario: I should see group "Owned" milestone show page + Given Group has projects with milestones + When I visit group "Owned" page + And I click on group milestones + And I click on one group milestone + Then I should see group milestone with descriptions and expiry date + And I should see group milestone with all issues and MRs assigned to that milestone + + Scenario: Create multiple milestones with one form + Given I visit group "Owned" milestones page + And I click new milestone button + And I fill milestone name + When I press create mileston button + Then milestone in each project should be created diff --git a/features/groups.feature b/features/groups.feature index 65ced5c529d..c803e952980 100644 --- a/features/groups.feature +++ b/features/groups.feature @@ -2,7 +2,6 @@ Feature: Groups Background: Given I sign in as "John Doe" And "John Doe" is owner of group "Owned" - And "John Doe" is guest of group "Guest" Scenario: I should have back to group button When I visit group "Owned" page @@ -24,13 +23,6 @@ Feature: Groups When I visit group "Owned" merge requests page Then I should see merge requests from group "Owned" assigned to me - @javascript - Scenario: I should add user to projects in group "Owned" - Given User "Mary Jane" exists - When I visit group "Owned" members page - And I select user "Mary Jane" from list with role "Reporter" - Then I should see user "Mary Jane" in team list - Scenario: I should see edit group "Owned" page When I visit group "Owned" settings page And I change group "Owned" name to "new-name" @@ -51,127 +43,6 @@ Feature: Groups Then I should not see group "Owned" avatar And I should not see the "Remove avatar" button - @javascript - Scenario: Add user to group - Given gitlab user "Mike" - When I visit group "Owned" members page - When I select "Mike" as "Reporter" - Then I should see "Mike" in team list as "Reporter" - - @javascript - Scenario: Ignore add user to group when is already Owner - Given gitlab user "Mike" - When I visit group "Owned" members page - When I select "Mike" as "Reporter" - Then I should see "Mike" in team list as "Owner" - - @javascript - Scenario: Invite user to group - When I visit group "Owned" members page - When I select "sjobs@apple.com" as "Reporter" - Then I should see "sjobs@apple.com" in team list as invited "Reporter" - - @javascript - Scenario: Edit group member permissions - Given "Mary Jane" is guest of group "Owned" - And I visit group "Owned" members page - When I change the "Mary Jane" role to "Developer" - Then I should see "Mary Jane" as "Developer" - - # Leave - - @javascript - Scenario: Owner should be able to remove himself from group if he is not the last owner - Given "Mary Jane" is owner of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "John Doe" - And I visit group "Owned" members page - Then I should not see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - - @javascript - Scenario: Owner should not be able to remove himself from group if he is the last owner - Given "Mary Jane" is guest of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - Then I should not see the "Remove User From Group" button for "John Doe" - - @javascript - Scenario: Guest should be able to remove himself from group - Given "Mary Jane" is guest of group "Guest" - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "John Doe" - When I visit group "Guest" members page - Then I should not see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - - @javascript - Scenario: Guest should be able to remove himself from group even if he is the only user in the group - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - When I click on the "Remove User From Group" button for "John Doe" - When I visit group "Guest" members page - Then I should not see user "John Doe" in team list - - # Remove others - - Scenario: Owner should be able to remove other users from group - Given "Mary Jane" is owner of group "Owned" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - When I click on the "Remove User From Group" button for "Mary Jane" - When I visit group "Owned" members page - Then I should see user "John Doe" in team list - Then I should not see user "Mary Jane" in team list - - Scenario: Guest should not be able to remove other users from group - Given "Mary Jane" is guest of group "Guest" - When I visit group "Guest" members page - Then I should see user "John Doe" in team list - Then I should see user "Mary Jane" in team list - Then I should not see the "Remove User From Group" button for "Mary Jane" - - Scenario: Search member by name - Given "Mary Jane" is guest of group "Guest" - And I visit group "Guest" members page - When I search for 'Mary' member - Then I should see user "Mary Jane" in team list - Then I should not see user "John Doe" in team list - - # Group milestones - - Scenario: I should see group "Owned" milestone index page with no milestones - When I visit group "Owned" page - And I click on group milestones - Then I should see group milestones index page has no milestones - - Scenario: I should see group "Owned" milestone index page with milestones - Given Group has projects with milestones - When I visit group "Owned" page - And I click on group milestones - Then I should see group milestones index page with milestones - - Scenario: I should see group "Owned" milestone show page - Given Group has projects with milestones - When I visit group "Owned" page - And I click on group milestones - And I click on one group milestone - Then I should see group milestone with descriptions and expiry date - And I should see group milestone with all issues and MRs assigned to that milestone - - Scenario: Create multiple milestones with one form - Given I visit group "Owned" milestones page - And I click new milestone button - And I fill milestone name - When I press create mileston button - Then milestone in each project should be created - # Group projects in settings Scenario: I should see all projects in the project list in settings Given Group "Owned" has archived project diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb new file mode 100644 index 00000000000..0706df3aec5 --- /dev/null +++ b/features/steps/group/members.rb @@ -0,0 +1,147 @@ +class Spinach::Features::GroupMembers < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedGroup + include SharedUser + include Select2Helper + + step 'I select "Mike" as "Reporter"' do + user = User.find_by(name: "Mike") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I select "Mike" as "Master"' do + user = User.find_by(name: "Mike") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Master", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see "Mike" in team list as "Reporter"' do + page.within '.content-list' do + expect(page).to have_content('Mike') + expect(page).to have_content('Reporter') + end + end + + step 'I should see "Mike" in team list as "Owner"' do + page.within '.content-list' do + expect(page).to have_content('Mike') + expect(page).to have_content('Owner') + end + end + + step 'I select "sjobs@apple.com" as "Reporter"' do + page.within ".users-group-form" do + select2("sjobs@apple.com", from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do + page.within '.content-list' do + expect(page).to have_content('sjobs@apple.com') + expect(page).to have_content('invited') + expect(page).to have_content('Reporter') + end + end + + step 'I select user "Mary Jane" from list with role "Reporter"' do + user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") + + page.within ".users-group-form" do + select2(user.id, from: "#user_ids", multiple: true) + select "Reporter", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see user "John Doe" in team list' do + expect(group_members_list).to have_content("John Doe") + end + + step 'I should not see user "John Doe" in team list' do + expect(group_members_list).not_to have_content("John Doe") + end + + step 'I should see user "Mary Jane" in team list' do + expect(group_members_list).to have_content("Mary Jane") + end + + step 'I should not see user "Mary Jane" in team list' do + expect(group_members_list).not_to have_content("Mary Jane") + end + + step 'I click on the "Remove User From Group" button for "John Doe"' do + find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I click on the "Remove User From Group" button for "Mary Jane"' do + find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I should not see the "Remove User From Group" button for "John Doe"' do + expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove') + # poltergeist always confirms popups. + end + + step 'I should not see the "Remove User From Group" button for "Mary Jane"' do + expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove') + # poltergeist always confirms popups. + end + + step 'I search for \'Mary\' member' do + page.within '.member-search-form' do + fill_in 'search', with: 'Mary' + click_button 'Search' + end + end + + step 'I change the "Mary Jane" role to "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + find(".js-toggle-button").click + page.within "#edit_group_member_#{member.id}" do + select 'Developer', from: 'group_member_access_level' + click_on 'Save' + end + end + end + + step 'I should see "Mary Jane" as "Developer"' do + member = mary_jane_member + + page.within "#group_member_#{member.id}" do + page.within '.member-access-level' do + expect(page).to have_content "Developer" + end + end + end + + private + + def mary_jane_member + user = User.find_by(name: "Mary Jane") + owned_group.members.find_by(user_id: user.id) + end + + def group_members_list + find(".panel .content-list") + end +end diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb new file mode 100644 index 00000000000..4c4038d44cc --- /dev/null +++ b/features/steps/group/milestones.rb @@ -0,0 +1,87 @@ +class Spinach::Features::GroupMilestones < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedGroup + include SharedUser + + step 'I click on group milestones' do + click_link 'Milestones' + end + + step 'I should see group milestones index page has no milestones' do + expect(page).to have_content('No milestones to show') + end + + step 'Group has projects with milestones' do + group_milestone + end + + step 'I should see group milestones index page with milestones' do + expect(page).to have_content('Version 7.2') + expect(page).to have_content('GL-113') + expect(page).to have_link('3 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2")) + expect(page).to have_link('0 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113")) + end + + step 'I click on one group milestone' do + click_link 'GL-113' + end + + step 'I should see group milestone with descriptions and expiry date' do + expect(page).to have_content('expires at Aug 20, 2114') + end + + step 'I should see group milestone with all issues and MRs assigned to that milestone' do + expect(page).to have_content('Milestone GL-113') + expect(page).to have_content('Progress: 0 closed – 3 open') + issue = Milestone.find_by(name: 'GL-113').issues.first + expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue)) + end + + step 'I fill milestone name' do + fill_in 'milestone_title', with: 'v2.9.0' + end + + step 'I click new milestone button' do + click_link "New Milestone" + end + + step 'I press create mileston button' do + click_button "Create Milestone" + end + + step 'milestone in each project should be created' do + group = Group.find_by(name: 'Owned') + expect(page).to have_content "Milestone v2.9.0" + expect(group.projects).to be_present + + group.projects.each do |project| + expect(page).to have_content project.name + end + end + + private + + def group_milestone + group = owned_group + + %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| + project = create :project, path: path, group: group + milestone = create :milestone, title: "Version 7.2", project: project + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + + milestone = create :milestone, title: "GL-113", project: project, + due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' + + create :issue, + project: project, + assignee: current_user, + author: current_user, + milestone: milestone + end + end +end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index d99417d178e..f5e3fee61c0 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -3,16 +3,11 @@ class Spinach::Features::Groups < Spinach::FeatureSteps include SharedPaths include SharedGroup include SharedUser - include Select2Helper step 'I should see back to dashboard button' do expect(page).to have_content 'Go to dashboard' end - step 'gitlab user "Mike"' do - create(:user, name: "Mike") - end - step 'I should see group "Owned"' do expect(page).to have_content '@owned' end @@ -33,59 +28,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_content 'Public-project' end - step 'I select "Mike" as "Reporter"' do - user = User.find_by(name: "Mike") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I select "Mike" as "Master"' do - user = User.find_by(name: "Mike") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Master", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see "Mike" in team list as "Reporter"' do - page.within '.content-list' do - expect(page).to have_content('Mike') - expect(page).to have_content('Reporter') - end - end - - step 'I should see "Mike" in team list as "Owner"' do - page.within '.content-list' do - expect(page).to have_content('Mike') - expect(page).to have_content('Owner') - end - end - - step 'I select "sjobs@apple.com" as "Reporter"' do - page.within ".users-group-form" do - select2("sjobs@apple.com", from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do - page.within '.content-list' do - expect(page).to have_content('sjobs@apple.com') - expect(page).to have_content('invited') - expect(page).to have_content('Reporter') - end - end - step 'I should see group "Owned" projects list' do owned_group.projects.each do |project| expect(page).to have_link project.name @@ -108,33 +50,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end end - step 'I select user "Mary Jane" from list with role "Reporter"' do - user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane") - - page.within ".users-group-form" do - select2(user.id, from: "#user_ids", multiple: true) - select "Reporter", from: "access_level" - end - - click_button "Add users to group" - end - - step 'I should see user "John Doe" in team list' do - expect(group_members_list).to have_content("John Doe") - end - - step 'I should not see user "John Doe" in team list' do - expect(group_members_list).not_to have_content("John Doe") - end - - step 'I should see user "Mary Jane" in team list' do - expect(group_members_list).to have_content("Mary Jane") - end - - step 'I should not see user "Mary Jane" in team list' do - expect(group_members_list).not_to have_content("Mary Jane") - end - step 'project from group "Owned" has issues assigned to me' do create :issue, project: project, @@ -196,67 +111,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).not_to have_link("Remove avatar") end - step 'I click on the "Remove User From Group" button for "John Doe"' do - find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I click on the "Remove User From Group" button for "Mary Jane"' do - find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "John Doe"' do - expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I should not see the "Remove User From Group" button for "Mary Jane"' do - expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove') - # poltergeist always confirms popups. - end - - step 'I search for \'Mary\' member' do - page.within '.member-search-form' do - fill_in 'search', with: 'Mary' - click_button 'Search' - end - end - - step 'I click on group milestones' do - click_link 'Milestones' - end - - step 'I should see group milestones index page has no milestones' do - expect(page).to have_content('No milestones to show') - end - - step 'Group has projects with milestones' do - group_milestone - end - - step 'I should see group milestones index page with milestones' do - expect(page).to have_content('Version 7.2') - expect(page).to have_content('GL-113') - expect(page).to have_link('2 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2")) - expect(page).to have_link('3 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113")) - end - - step 'I click on one group milestone' do - click_link 'GL-113' - end - - step 'I should see group milestone with descriptions and expiry date' do - expect(page).to have_content('expires at Aug 20, 2114') - end - - step 'I should see group milestone with all issues and MRs assigned to that milestone' do - expect(page).to have_content('Milestone GL-113') - expect(page).to have_content('Progress: 0 closed – 4 open') - expect(page).to have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1)) - expect(page).to have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3)) - end - step 'Group "Owned" has archived project' do group = Group.find_by(name: 'Owned') create(:project, namespace: group, archived: true, path: "archived-project") @@ -266,60 +120,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived') end - step 'I fill milestone name' do - fill_in 'milestone_title', with: 'v2.9.0' - end - - step 'I click new milestone button' do - click_link "New Milestone" - end - - step 'I press create mileston button' do - click_button "Create Milestone" - end - - step 'milestone in each project should be created' do - group = Group.find_by(name: 'Owned') - expect(page).to have_content "Milestone v2.9.0" - expect(group.projects).to be_present - - group.projects.each do |project| - expect(page).to have_content project.name - end - end - - step 'I change the "Mary Jane" role to "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - find(".js-toggle-button").click - page.within "#edit_group_member_#{member.id}" do - select 'Developer', from: 'group_member_access_level' - click_on 'Save' - end - end - end - - step 'I should see "Mary Jane" as "Developer"' do - member = mary_jane_member - - page.within "#group_member_#{member.id}" do - page.within '.member-access-level' do - expect(page).to have_content "Developer" - end - end - end - - protected - - def owned_group - @owned_group ||= Group.find_by(name: "Owned") - end - - def mary_jane_member - user = User.find_by(name: "Mary Jane") - owned_group.members.find_by(user_id: user.id) - end + private def assigned_to_me(key) project.send(key).where(assignee_id: current_user.id) @@ -328,29 +129,4 @@ class Spinach::Features::Groups < Spinach::FeatureSteps def project owned_group.projects.first end - - def group_milestone - group = owned_group - - %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path| - project = create :project, path: path, group: group - milestone = create :milestone, title: "Version 7.2", project: project - create :issue, - project: project, - assignee: current_user, - author: current_user, - milestone: milestone - - milestone = create :milestone, title: "GL-113", project: project - create :issue, - project: project, - assignee: current_user, - author: current_user, - milestone: milestone - end - end - - def group_members_list - find(".panel .content-list") - end end diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index 83a04576973..58581653f28 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -41,4 +41,8 @@ module SharedGroup project.team << [user, :master] @project_count += 1 end + + def owned_group + @owned_group ||= Group.find_by(name: "Owned") + end end diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb index fc1e8d6e889..250cc5b94f3 100644 --- a/features/steps/shared/user.rb +++ b/features/steps/shared/user.rb @@ -9,6 +9,10 @@ module SharedUser user_exists("Mary Jane", { username: "mary_jane" }) end + step 'gitlab user "Mike"' do + create(:user, name: "Mike") + end + protected def user_exists(name, options = {}) -- cgit v1.2.1 From acad9d67c7af881c1e4cce1e20ffd9c8f65f3029 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 12:27:34 +0100 Subject: Fix rubocop complain Signed-off-by: Dmitriy Zaporozhets --- features/steps/group/milestones.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb index 4c4038d44cc..6e57b16ccb6 100644 --- a/features/steps/group/milestones.rb +++ b/features/steps/group/milestones.rb @@ -74,8 +74,11 @@ class Spinach::Features::GroupMilestones < Spinach::FeatureSteps author: current_user, milestone: milestone - milestone = create :milestone, title: "GL-113", project: project, - due_date: '2114-08-20', description: 'Lorem Ipsum is simply dummy text' + milestone = create :milestone, + title: "GL-113", + project: project, + due_date: '2114-08-20', + description: 'Lorem Ipsum is simply dummy text' create :issue, project: project, -- cgit v1.2.1 From 18a5f91ed63322af7f2fed29a6cc9a4c1e96978d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 25 Nov 2015 13:08:47 +0100 Subject: Bump gitlab-shell to 2.6.8 Signed-off-by: Dmitriy Zaporozhets --- GITLAB_SHELL_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index e261122d5c4..743af5e1251 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.7 +2.6.8 -- cgit v1.2.1 From 82ff9e65939d2da279b684cf378ca33e60eed66b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurens=20St=C3=B6tzel?= Date: Wed, 25 Nov 2015 12:27:07 +0000 Subject: Correction of markdown in SSH docs --- doc/ssh/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 9753504ac8b..fe5b45dd432 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -15,7 +15,8 @@ Note: It is a best practice to use a password for an SSH key, but it is not required and you can skip creating a password by pressing enter. Note that the password you choose here can't be altered or retrieved. -To generate a new SSH key, use the following commandGitLab```bash +To generate a new SSH key, use the following command: +```bash ssh-keygen -t rsa -C "$your_email" ``` This command will prompt you for a location and filename to store the key @@ -108,4 +109,4 @@ Note in the gitlab.com example above a username was specified to override the de Due to the wide variety of SSH clients and their very large number of configuration options, further explanation of these topics is beyond the scope of this document. Public SSH keys need to be unique, as they will bind to your account. Your SSH key is the only identifier you'll -have when pushing code via SSH. That's why it needs to uniquely map to a single user. +have when pushing code via SSH. That's why it needs to uniquely map to a single user. \ No newline at end of file -- cgit v1.2.1 From 7da750cc30308173e6f21f570ecd5902dfd3d89d Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:27:31 +0100 Subject: Specs for links in email notifications for Gmail Actions. --- spec/mailers/notify_spec.rb | 94 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 47863d54579..52bad36ae1d 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -77,6 +77,14 @@ describe Notify do end end + shared_examples 'it should have Gmail Actions links' do + it { is_expected.to have_body_text /ViewAction/ } + end + + shared_examples 'it should not have Gmail Actions links' do + it { is_expected.to_not have_body_text /ViewAction/ } + end + describe 'for new users, the email' do let(:example_site_path) { root_path } let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } @@ -87,6 +95,7 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it_behaves_like 'a new user email', new_user_address + it_behaves_like 'it should not have Gmail Actions links' it 'contains the password text' do is_expected.to have_body_text /Click here to set your password/ @@ -115,6 +124,7 @@ describe Notify do it_behaves_like 'an email sent from GitLab' it_behaves_like 'a new user email', new_user_address + it_behaves_like 'it should not have Gmail Actions links' it 'should not contain the new user\'s password' do is_expected.not_to have_body_text /password/ @@ -127,6 +137,7 @@ describe Notify do subject { Notify.new_ssh_key_email(key.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'is sent to the new user' do is_expected.to deliver_to key.user.email @@ -150,6 +161,8 @@ describe Notify do subject { Notify.new_email_email(email.id) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent to the new user' do is_expected.to deliver_to email.user.email end @@ -194,6 +207,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ @@ -202,6 +216,10 @@ describe Notify do it 'contains a link to the new issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end describe 'that are new with a description' do @@ -217,6 +235,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -239,6 +258,10 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end describe 'status changed' do @@ -246,6 +269,7 @@ describe Notify do subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -268,8 +292,11 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - end + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end + end end context 'for merge requests' do @@ -282,6 +309,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -302,14 +330,24 @@ describe Notify do it 'has the correct message-id set' do is_expected.to have_header 'Message-ID', "" end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are new with a description' do subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } + it_behaves_like 'it should have Gmail Actions links' + it 'contains the description' do is_expected.to have_body_text /#{merge_request_with_description.description}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are reassigned' do @@ -317,6 +355,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -339,6 +378,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'status changed' do @@ -346,6 +389,8 @@ describe Notify do subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -368,6 +413,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'that are merged' do @@ -375,6 +424,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] @@ -393,6 +443,10 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end end end @@ -403,6 +457,7 @@ describe Notify do subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Project was moved/ @@ -424,13 +479,16 @@ describe Notify do subject { Notify.project_access_granted_email(project_member.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Access to project was granted/ end + it 'contains name of project' do is_expected.to have_body_text /#{project.name}/ end + it 'contains new user role' do is_expected.to have_body_text /#{project_member.human_access}/ end @@ -445,6 +503,8 @@ describe Notify do end shared_examples 'a note email' do + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(note_author.name) @@ -469,6 +529,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'commit' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ @@ -477,6 +538,10 @@ describe Notify do it 'contains a link to the commit' do is_expected.to have_body_text commit.short_id end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Commit/ + end end describe 'on a merge request' do @@ -488,6 +553,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'merge_request' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -496,6 +562,10 @@ describe Notify do it 'contains a link to the merge request note' do is_expected.to have_body_text /#{note_on_merge_request_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Merge request/ + end end describe 'on an issue' do @@ -507,6 +577,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'issue' + it_behaves_like 'it should have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ @@ -515,6 +586,10 @@ describe Notify do it 'contains a link to the issue note' do is_expected.to have_body_text /#{note_on_issue_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Issue/ + end end end end @@ -527,6 +602,7 @@ describe Notify do subject { Notify.group_access_granted_email(membership.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'it should not have Gmail Actions links' it 'has the correct subject' do is_expected.to have_subject /Access to group was granted/ @@ -574,6 +650,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -600,6 +678,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -625,6 +705,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -646,6 +728,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -671,6 +755,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) } + it_behaves_like 'it should not have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -774,6 +860,8 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) } + it_behaves_like 'it should have Gmail Actions links' + it 'is sent as the author' do sender = subject.header[:from].addrs[0] expect(sender.display_name).to eq(user.name) @@ -799,5 +887,9 @@ describe Notify do it 'contains a link to the diff' do is_expected.to have_body_text /#{diff_path}/ end + + it 'Gmail Actions contain correct action name' do + is_expected.to have_body_text /View Commit/ + end end end -- cgit v1.2.1 From 56135927273cfd722872e893abde3728f3b21d38 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:53:02 +0100 Subject: If action link is not found, return nil. --- app/helpers/emails_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index 45788ba95ac..41b5bd7be90 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -28,6 +28,8 @@ module EmailsHelper return "View #{action.humanize.singularize}" end end + + nil end def color_email_diff(diffcontent) -- cgit v1.2.1 From 5c0be319cc6d568cefd339be8bc5f80e157836b4 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Wed, 25 Nov 2015 13:59:03 +0100 Subject: Remove some repetition in notify spec. --- spec/mailers/notify_spec.rb | 93 ++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 61 deletions(-) diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 52bad36ae1d..d6796b07a5b 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -85,6 +85,24 @@ describe Notify do it { is_expected.to_not have_body_text /ViewAction/ } end + shared_examples 'it should show Gmail Actions View Issue link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Issue/ } + end + + shared_examples 'it should show Gmail Actions View Merge request link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Merge request/ } + end + + shared_examples 'it should show Gmail Actions View Commit link' do + it_behaves_like 'it should have Gmail Actions links' + + it { is_expected.to have_body_text /View Commit/ } + end + describe 'for new users, the email' do let(:example_site_path) { root_path } let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) } @@ -207,7 +225,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'has the correct subject' do is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/ @@ -216,15 +234,13 @@ describe Notify do it 'contains a link to the new issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end describe 'that are new with a description' do subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) } + it_behaves_like 'it should show Gmail Actions View Issue link' + it 'contains the description' do is_expected.to have_body_text /#{issue_with_description.description}/ end @@ -235,7 +251,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -258,10 +274,6 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end describe 'status changed' do @@ -269,7 +281,7 @@ describe Notify do subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -292,10 +304,6 @@ describe Notify do it 'contains a link to the issue' do is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end end @@ -309,7 +317,7 @@ describe Notify do it_behaves_like 'an assignee email' it_behaves_like 'an email starting a new thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -330,24 +338,16 @@ describe Notify do it 'has the correct message-id set' do is_expected.to have_header 'Message-ID', "" end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are new with a description' do subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) } - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'contains the description' do is_expected.to have_body_text /#{merge_request_with_description.description}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are reassigned' do @@ -355,7 +355,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -378,10 +378,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'status changed' do @@ -389,8 +385,7 @@ describe Notify do subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' - + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -413,10 +408,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'that are merged' do @@ -424,7 +415,7 @@ describe Notify do it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'is sent as the merge author' do sender = subject.header[:from].addrs[0] @@ -443,10 +434,6 @@ describe Notify do it 'contains a link to the merge request' do is_expected.to have_body_text /#{namespace_project_merge_request_path project.namespace, project, merge_request}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end end end @@ -529,7 +516,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'commit' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Commit link' it 'has the correct subject' do is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/ @@ -538,10 +525,6 @@ describe Notify do it 'contains a link to the commit' do is_expected.to have_body_text commit.short_id end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Commit/ - end end describe 'on a merge request' do @@ -553,7 +536,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'merge_request' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Merge request link' it 'has the correct subject' do is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ @@ -562,10 +545,6 @@ describe Notify do it 'contains a link to the merge request note' do is_expected.to have_body_text /#{note_on_merge_request_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Merge request/ - end end describe 'on an issue' do @@ -577,7 +556,7 @@ describe Notify do it_behaves_like 'a note email' it_behaves_like 'an answer to an existing thread', 'issue' - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Issue link' it 'has the correct subject' do is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/ @@ -586,10 +565,6 @@ describe Notify do it 'contains a link to the issue note' do is_expected.to have_body_text /#{note_on_issue_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Issue/ - end end end end @@ -860,7 +835,7 @@ describe Notify do subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) } - it_behaves_like 'it should have Gmail Actions links' + it_behaves_like 'it should show Gmail Actions View Commit link' it 'is sent as the author' do sender = subject.header[:from].addrs[0] @@ -887,9 +862,5 @@ describe Notify do it 'contains a link to the diff' do is_expected.to have_body_text /#{diff_path}/ end - - it 'Gmail Actions contain correct action name' do - is_expected.to have_body_text /View Commit/ - end end end -- cgit v1.2.1 From 40ff1318d29884e4d17e7e450d8a7633e5ac36a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 25 Nov 2015 18:18:44 +0200 Subject: Rails update to 4.2.4 --- Gemfile | 7 +- Gemfile.lock | 246 ++++++++++++++++------------ app/models/sent_notification.rb | 5 +- app/workers/emails_on_push_worker.rb | 2 +- bin/ci/upgrade.rb | 0 bin/rails | 6 +- bin/rake | 9 +- bin/setup | 29 ++++ bin/upgrade.rb | 0 config/environment.rb | 2 +- config/environments/development.rb | 2 +- config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/cookies_serializer.rb | 2 +- config/initializers/default_url_options.rb | 2 +- config/initializers/rack_attack.rb.example | 14 +- config/initializers/rack_lineprof.rb | 2 +- config/initializers/secret_token.rb | 8 +- config/initializers/session_store.rb | 4 +- config/initializers/sherlock.rb | 2 +- config/initializers/smtp_settings.rb.sample | 2 +- config/initializers/static_files.rb | 2 +- config/routes.rb | 2 +- db/schema.rb | 106 ++++++------ 24 files changed, 264 insertions(+), 194 deletions(-) mode change 100644 => 100755 bin/ci/upgrade.rb create mode 100755 bin/setup mode change 100644 => 100755 bin/upgrade.rb diff --git a/Gemfile b/Gemfile index 4762e2e223f..4a9f70a1297 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,9 @@ source "https://rubygems.org" -gem 'rails', '4.1.14' +gem 'rails', '4.2.4' + +# Responders respond_to and respond_with +gem 'responders', '~> 2.0' # Specify a sprockets version due to security issue # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY @@ -98,6 +101,7 @@ gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' +gem 'net-ssh', '~> 3.0.1' # Diffs gem 'diffy', '~> 3.0.3' @@ -213,6 +217,7 @@ group :development do gem 'rerun', '~> 0.10.0' gem 'bullet', require: false gem 'rblineprof', platform: :mri, require: false + gem 'web-console', '~> 2.0' # Better errors handler gem 'better_errors', '~> 1.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..e9460515051 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,31 +1,40 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (2.3.1) + CFPropertyList (2.3.2) RedCloth (4.2.9) ace-rails-ap (2.0.1) - actionmailer (4.1.14) - actionpack (= 4.1.14) - actionview (= 4.1.14) + actionmailer (4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) mail (~> 2.5, >= 2.5.4) - actionpack (4.1.14) - actionview (= 4.1.14) - activesupport (= 4.1.14) - rack (~> 1.5.2) + rails-dom-testing (~> 1.0, >= 1.0.5) + actionpack (4.2.4) + actionview (= 4.2.4) + activesupport (= 4.2.4) + rack (~> 1.6) rack-test (~> 0.6.2) - actionview (4.1.14) - activesupport (= 4.1.14) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) erubis (~> 2.7.0) - activemodel (4.1.14) - activesupport (= 4.1.14) + rails-dom-testing (~> 1.0, >= 1.0.5) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activejob (4.2.4) + activesupport (= 4.2.4) + globalid (>= 0.3.0) + activemodel (4.2.4) + activesupport (= 4.2.4) builder (~> 3.1) - activerecord (4.1.14) - activemodel (= 4.1.14) - activesupport (= 4.1.14) - arel (~> 5.0.0) + activerecord (4.2.4) + activemodel (= 4.2.4) + activesupport (= 4.2.4) + arel (~> 6.0) activerecord-deprecated_finders (1.0.4) - activerecord-session_store (0.1.1) + activerecord-session_store (0.1.2) actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) @@ -33,31 +42,31 @@ GEM activemodel (~> 4.0) activesupport (~> 4.0) rails-observers (~> 0.1.1) - activesupport (4.1.14) - i18n (~> 0.6, >= 0.6.9) + activesupport (4.2.4) + i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) - thread_safe (~> 0.1) + thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) acts-as-taggable-on (3.5.0) activerecord (>= 3.2, < 5) addressable (2.3.8) - after_commit_queue (1.1.0) - rails (>= 3.0) + after_commit_queue (1.3.0) + activerecord (>= 3.0) annotate (2.6.10) activerecord (>= 3.2, <= 4.3) rake (~> 10.4) - arel (5.0.1.20140414130214) + arel (6.0.3) asana (0.0.6) activeresource (>= 3.2.3) - asciidoctor (1.5.2) + asciidoctor (1.5.3) ast (2.1.0) astrolabe (1.3.1) parser (~> 2.2) attr_encrypted (1.3.4) encryptor (>= 1.3.0) attr_required (1.0.0) - autoprefixer-rails (5.2.1.2) + autoprefixer-rails (6.1.1) execjs json awesome_print (1.2.0) @@ -85,15 +94,15 @@ GEM ruby_parser (~> 3.5.0) sass (~> 3.0) terminal-table (~> 1.4) - browser (1.0.0) + browser (1.0.1) builder (3.2.2) - bullet (4.14.9) + bullet (4.14.10) activesupport (>= 3.0.0) uniform_notifier (~> 1.9.0) bundler-audit (0.4.0) bundler (~> 1.2) thor (~> 0.18) - byebug (6.0.2) + byebug (8.2.0) cal-heatmap-rails (0.0.1) capybara (2.4.4) mime-types (>= 1.16) @@ -108,10 +117,25 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.16.0) - timers (~> 4.0.0) + celluloid (0.17.2) + celluloid-essentials + celluloid-extras + celluloid-fsm + celluloid-pool + celluloid-supervision + timers (>= 4.1.1) + celluloid-essentials (0.20.5) + timers (>= 4.1.1) + celluloid-extras (0.20.5) + timers (>= 4.1.1) + celluloid-fsm (0.20.5) + timers (>= 4.1.1) + celluloid-pool (0.20.5) + timers (>= 4.1.1) + celluloid-supervision (0.20.5) + timers (>= 4.1.1) charlock_holmes (0.7.3) - chunky_png (1.3.4) + chunky_png (1.3.5) cliver (0.3.2) coderay (1.1.0) coercible (1.0.0) @@ -122,15 +146,16 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) + coffee-script-source (1.10.0) colorize (0.7.7) connection_pool (2.2.0) - coveralls (0.8.2) + coveralls (0.8.9) json (~> 1.8) rest-client (>= 1.6.8, < 2) simplecov (~> 0.10.0) term-ansicolor (~> 1.3) thor (~> 0.19.1) + tins (~> 1.6.0) crack (0.4.2) safe_yaml (~> 1.0.0) creole (0.5.0) @@ -153,7 +178,7 @@ GEM warden (~> 1.2.3) devise-async (0.9.0) devise (~> 3.2) - devise-two-factor (2.0.0) + devise-two-factor (2.0.1) activesupport attr_encrypted (~> 1.3.2) devise (~> 3.5.0) @@ -162,11 +187,11 @@ GEM diff-lcs (1.2.5) diffy (3.0.7) docile (1.1.5) - domain_name (0.5.24) + domain_name (0.5.25) unf (>= 0.0.5, < 1.0.0) doorkeeper (2.1.4) railties (>= 3.2) - dropzonejs-rails (0.7.1) + dropzonejs-rails (0.7.2) rails (> 3.1) email_reply_parser (0.5.8) email_spec (1.6.0) @@ -202,7 +227,7 @@ GEM flog (4.3.2) ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.4) - flowdock (0.7.0) + flowdock (0.7.1) httparty (~> 0.7) multi_json fog (1.25.0) @@ -224,13 +249,10 @@ GEM fog-core (~> 1.22) fog-json inflecto (~> 0.0.2) - fog-core (1.32.1) + fog-core (1.35.0) builder excon (~> 0.45) formatador (~> 0.2) - mime-types - net-scp (~> 1.1) - net-ssh (>= 2.1.3) fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) @@ -242,10 +264,10 @@ GEM fog-core (>= 1.21.0) fog-json fog-xml (>= 0.0.1) - fog-sakuracloud (1.0.1) + fog-sakuracloud (1.4.0) fog-core fog-json - fog-softlayer (0.4.7) + fog-softlayer (1.0.2) fog-core fog-json fog-terremark (0.1.0) @@ -260,7 +282,7 @@ GEM fog-xml (0.1.2) fog-core nokogiri (~> 1.5, >= 1.5.11) - font-awesome-rails (4.4.0.0) + font-awesome-rails (4.5.0.0) railties (>= 3.2, < 5.0) foreman (0.78.0) thor (~> 0.19.1) @@ -270,11 +292,11 @@ GEM ruby-progressbar (~> 1.4) gemnasium-gitlab-service (0.2.6) rugged (~> 0.21) - gemojione (2.0.1) + gemojione (2.1.0) json get_process_mem (0.2.0) gherkin-ruby (0.3.2) - github-linguist (4.7.0) + github-linguist (4.7.2) charlock_holmes (~> 0.7.3) escape_utils (~> 1.1.0) mime-types (>= 1.19) @@ -289,8 +311,8 @@ GEM diff-lcs (~> 1.1) mime-types (~> 1.15) posix-spawn (~> 0.3) - gitlab_emoji (0.1.1) - gemojione (~> 2.0) + gitlab_emoji (0.2.0) + gemojione (~> 2.1) gitlab_git (7.2.20) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) @@ -302,13 +324,15 @@ GEM omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.3) + globalid (0.3.6) + activesupport (>= 4.1.0) gollum-grit_adapter (1.0.0) gitlab-grit (~> 2.7, >= 2.7.1) gollum-lib (4.0.3) github-markup (~> 1.3.3) gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.10.1) + rouge (~> 1.7.4) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (6.0.1) @@ -337,7 +361,7 @@ GEM haml (>= 4.0.6, < 5.0) html2haml (>= 1.0.1) railties (>= 4.0.1) - hashie (3.4.2) + hashie (3.4.3) highline (1.6.21) hike (1.2.3) hipchat (1.5.2) @@ -355,7 +379,7 @@ GEM http-cookie (1.0.2) domain_name (~> 0.5) http_parser.rb (0.5.3) - httparty (0.13.5) + httparty (0.13.7) json (~> 1.8) multi_xml (>= 0.5.2) httpclient (2.7.0.1) @@ -365,7 +389,7 @@ GEM inflecto (0.0.2) ipaddress (0.8.0) jquery-atwho-rails (1.3.2) - jquery-rails (3.1.3) + jquery-rails (3.1.4) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) jquery-scrollto-rails (1.4.3) @@ -376,19 +400,21 @@ GEM jquery-ui-rails (4.2.1) railties (>= 3.2.16) json (1.8.3) - jwt (1.5.1) + jwt (1.5.2) kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) - kgio (2.9.3) + kgio (2.10.0) launchy (2.4.3) addressable (~> 2.3) letter_opener (1.1.2) launchy (~> 2.2) - listen (2.10.1) - celluloid (~> 0.16.0) + listen (2.9.0) + celluloid (>= 0.15.2) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) + loofah (2.0.3) + nokogiri (>= 1.5.9) macaddr (1.7.1) systemu (~> 2.6.2) mail (2.6.3) @@ -405,16 +431,14 @@ GEM multipart-post (2.0.0) mysql2 (0.3.20) nested_form (0.3.2) - net-ldap (0.11) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (2.9.2) - netrc (0.10.3) + net-ldap (0.12.1) + net-ssh (3.0.1) + netrc (0.11.0) newrelic-grape (2.0.0) grape newrelic_rpm newrelic_rpm (3.9.4.245) - nokogiri (1.6.6.2) + nokogiri (1.6.6.4) mini_portile (~> 0.6.0) nprogress-rails (0.1.6.7) oauth (0.4.7) @@ -438,12 +462,15 @@ GEM omniauth-github (1.1.2) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) - omniauth-gitlab (1.0.0) + omniauth-gitlab (1.0.1) omniauth (~> 1.0) omniauth-oauth2 (~> 1.0) - omniauth-google-oauth2 (0.2.6) - omniauth (> 1.0) - omniauth-oauth2 (~> 1.1) + omniauth-google-oauth2 (0.2.10) + addressable (~> 2.3) + jwt (~> 1.0) + multi_json (~> 1.3) + omniauth (>= 1.1.1) + omniauth-oauth2 (~> 1.3.1) omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) @@ -467,18 +494,18 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - opennebula (4.12.1) + opennebula (4.14.2) json nokogiri rbvmomi org-ruby (0.9.12) rubypants (~> 0.2) orm_adapter (0.5.0) - paranoia (2.1.3) + paranoia (2.1.4) activerecord (~> 4.0) - parser (2.2.2.6) + parser (2.2.3.0) ast (>= 1.1, < 3.0) - pg (0.18.2) + pg (0.18.4) poltergeist (1.6.0) capybara (~> 2.1) cliver (~> 0.3.1) @@ -486,7 +513,7 @@ GEM websocket-driver (>= 0.2.0) posix-spawn (0.3.11) powerpack (0.0.9) - pry (0.10.1) + pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) slop (~> 3.4) @@ -495,7 +522,7 @@ GEM pyu-ruby-sasl (0.0.3.3) quiet_assets (1.0.3) railties (>= 3.1, < 5.0) - rack (1.5.5) + rack (1.6.4) rack-accept (0.4.5) rack (>= 0.4) rack-attack (4.3.0) @@ -513,28 +540,37 @@ GEM rack rack-test (0.6.3) rack (>= 1.0) - rails (4.1.14) - actionmailer (= 4.1.14) - actionpack (= 4.1.14) - actionview (= 4.1.14) - activemodel (= 4.1.14) - activerecord (= 4.1.14) - activesupport (= 4.1.14) + rails (4.2.4) + actionmailer (= 4.2.4) + actionpack (= 4.2.4) + actionview (= 4.2.4) + activejob (= 4.2.4) + activemodel (= 4.2.4) + activerecord (= 4.2.4) + activesupport (= 4.2.4) bundler (>= 1.3.0, < 2.0) - railties (= 4.1.14) - sprockets-rails (~> 2.0) + railties (= 4.2.4) + sprockets-rails + rails-deprecated_sanitizer (1.0.3) + activesupport (>= 4.2.0.alpha) + rails-dom-testing (1.0.7) + activesupport (>= 4.2.0.beta, < 5.0) + nokogiri (~> 1.6.0) + rails-deprecated_sanitizer (>= 1.0.1) + rails-html-sanitizer (1.0.2) + loofah (~> 2.0) rails-observers (0.1.2) activemodel (~> 4.0) - railties (4.1.14) - actionpack (= 4.1.14) - activesupport (= 4.1.14) + railties (4.2.4) + actionpack (= 4.2.4) + activesupport (= 4.2.4) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.0.0) raindrops (0.15.0) rake (10.4.2) raphael-rails (2.1.2) - rb-fsevent (0.9.5) + rb-fsevent (0.9.6) rb-inotify (0.9.5) ffi (>= 0.5.0) rblineprof (0.3.6) @@ -546,13 +582,13 @@ GEM rdoc (3.12.2) json (~> 1.4) redcarpet (3.3.3) - redis (3.2.1) - redis-actionpack (4.0.0) + redis (3.2.2) + redis-actionpack (4.0.1) actionpack (~> 4) redis-rack (~> 1.5.0) redis-store (~> 1.1.0) - redis-activesupport (4.1.1) - activesupport (~> 4) + redis-activesupport (4.1.5) + activesupport (>= 3, < 5) redis-store (~> 1.1.0) redis-namespace (1.5.2) redis (~> 3.0, >= 3.0.4) @@ -563,20 +599,20 @@ GEM redis-actionpack (~> 4) redis-activesupport (~> 4) redis-store (~> 1.1.0) - redis-store (1.1.6) + redis-store (1.1.7) redis (>= 2.2) - request_store (1.2.0) + request_store (1.2.1) rerun (0.10.0) listen (~> 2.7, >= 2.7.3) - responders (1.1.2) - railties (>= 3.2, < 4.2) + responders (2.1.0) + railties (>= 4.2.0, < 5) rest-client (1.8.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 3.0) netrc (~> 0.7) rinku (1.7.3) rotp (2.1.1) - rouge (1.10.1) + rouge (1.7.7) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) @@ -716,14 +752,14 @@ GEM terminal-table (1.5.2) test_after_commit (0.2.7) activerecord (>= 3.2) - thin (1.6.3) + thin (1.6.4) daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0) + eventmachine (~> 1.0, >= 1.0.4) rack (~> 1.0) thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.0.4) + timers (4.1.1) hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) @@ -756,9 +792,9 @@ GEM kgio (~> 2.6) rack raindrops (~> 0.7) - unicorn-worker-killer (0.4.3) + unicorn-worker-killer (0.4.4) get_process_mem (~> 0) - unicorn (~> 4) + unicorn (>= 4, < 6) uniform_notifier (1.9.0) uuid (2.3.8) macaddr (~> 1.0) @@ -770,10 +806,15 @@ GEM equalizer (~> 0.0, >= 0.0.9) warden (1.2.3) rack (>= 1.0) + web-console (2.2.1) + activemodel (>= 4.0) + binding_of_caller (>= 0.7.2) + railties (>= 4.0) + sprockets-rails (>= 2.0, < 4.0) webmock (1.21.0) addressable (>= 2.3.6) crack (>= 0.3.2) - websocket-driver (0.6.2) + websocket-driver (0.6.3) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) wikicloth (0.8.1) @@ -865,6 +906,7 @@ DEPENDENCIES mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) nested_form (~> 0.3.2) + net-ssh (~> 3.0.1) newrelic-grape newrelic_rpm (~> 3.9.4.245) nprogress-rails (~> 0.1.6.7) @@ -890,7 +932,7 @@ DEPENDENCIES rack-attack (~> 4.3.0) rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) - rails (= 4.1.14) + rails (= 4.2.4) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) @@ -898,6 +940,7 @@ DEPENDENCIES redis-rails (~> 4.0.0) request_store (~> 1.2.0) rerun (~> 0.10.0) + responders (~> 2.0) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) rubocop (~> 0.28.0) @@ -938,6 +981,7 @@ DEPENDENCIES unicorn-worker-killer (~> 0.4.2) version_sorter (~> 2.0.0) virtus (~> 1.0.1) + web-console (~> 2.0) webmock (~> 1.21.0) wikicloth (= 0.8.1) diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 3eed5c16e45..d8fe65b06f6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -17,9 +17,8 @@ class SentNotification < ActiveRecord::Base belongs_to :noteable, polymorphic: true belongs_to :recipient, class_name: "User" - validate :project, :recipient, :reply_key, presence: true - validate :reply_key, uniqueness: true - + validates :project, :recipient, :reply_key, presence: true + validates :reply_key, uniqueness: true validates :noteable_id, presence: true, unless: :for_commit? validates :commit_id, presence: true, if: :for_commit? validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 916a99bb273..c4d8595d45d 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -53,7 +53,7 @@ class EmailsOnPushWorker reverse_compare: reverse_compare, send_from_committer_email: send_from_committer_email, disable_diffs: disable_diffs - ).deliver + ).deliver_now # These are input errors and won't be corrected even if Sidekiq retries rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e logger.info("Failed to send e-mail for project '#{project.name_with_namespace}' to #{recipient}: #{e}") diff --git a/bin/ci/upgrade.rb b/bin/ci/upgrade.rb old mode 100644 new mode 100755 diff --git a/bin/rails b/bin/rails index 7feb6a30e69..5191e6927af 100755 --- a/bin/rails +++ b/bin/rails @@ -1,8 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end -APP_PATH = File.expand_path('../../config/application', __FILE__) +APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' diff --git a/bin/rake b/bin/rake index 0fb4e07e13a..17240489f64 100755 --- a/bin/rake +++ b/bin/rake @@ -1,7 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path("../spring", __FILE__) -rescue LoadError -end -require 'bundler/setup' -load Gem.bin_path('rake', 'rake') +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 00000000000..acdb2c1389c --- /dev/null +++ b/bin/setup @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +Dir.chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file: + + puts "== Installing dependencies ==" + system "gem install bundler --conservative" + system "bundle check || bundle install" + + # puts "\n== Copying sample files ==" + # unless File.exist?("config/database.yml") + # system "cp config/database.yml.sample config/database.yml" + # end + + puts "\n== Preparing database ==" + system "bin/rake db:setup" + + puts "\n== Removing old logs and tempfiles ==" + system "rm -f log/*" + system "rm -rf tmp/cache" + + puts "\n== Restarting application server ==" + system "touch tmp/restart.txt" +end diff --git a/bin/upgrade.rb b/bin/upgrade.rb old mode 100644 new mode 100755 diff --git a/config/environment.rb b/config/environment.rb index 3b186a9d57a..df3006d349c 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -2,4 +2,4 @@ require File.expand_path('../application', __FILE__) # Initialize the rails application -Gitlab::Application.initialize! +Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index 827a110c249..c22722c606b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # In the development environment your application's code is reloaded on diff --git a/config/environments/production.rb b/config/environments/production.rb index 3316ece3873..e8250d66452 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # Code is not reloaded between requests diff --git a/config/environments/test.rb b/config/environments/test.rb index 955540837d3..46982be2864 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,4 +1,4 @@ -Gitlab::Application.configure do +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb # The test environment is used exclusively to run your application's diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb index 43adac8b2c6..54516e3f23d 100644 --- a/config/initializers/cookies_serializer.rb +++ b/config/initializers/cookies_serializer.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Gitlab::Application.config.action_dispatch.cookies_serializer = :hybrid +Rails.application.config.action_dispatch.cookies_serializer = :hybrid diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb index f9f88f95db9..8fd27b1d88e 100644 --- a/config/initializers/default_url_options.rb +++ b/config/initializers/default_url_options.rb @@ -8,4 +8,4 @@ unless Gitlab.config.gitlab_on_standard_port? default_url_options[:port] = Gitlab.config.gitlab.port end -Gitlab::Application.routes.default_url_options = default_url_options +Rails.application.routes.default_url_options = default_url_options diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example index 2155ea14562..b1bbcca1d61 100644 --- a/config/initializers/rack_attack.rb.example +++ b/config/initializers/rack_attack.rb.example @@ -4,13 +4,13 @@ # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests paths_to_be_protected = [ - "#{Gitlab::Application.config.relative_url_root}/users/password", - "#{Gitlab::Application.config.relative_url_root}/users/sign_in", - "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session.json", - "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session", - "#{Gitlab::Application.config.relative_url_root}/users", - "#{Gitlab::Application.config.relative_url_root}/users/confirmation", - "#{Gitlab::Application.config.relative_url_root}/unsubscribes/" + "#{Rails.application.config.relative_url_root}/users/password", + "#{Rails.application.config.relative_url_root}/users/sign_in", + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json", + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session", + "#{Rails.application.config.relative_url_root}/users", + "#{Rails.application.config.relative_url_root}/users/confirmation", + "#{Rails.application.config.relative_url_root}/unsubscribes/" ] diff --git a/config/initializers/rack_lineprof.rb b/config/initializers/rack_lineprof.rb index f0c006d811b..22e77a32c61 100644 --- a/config/initializers/rack_lineprof.rb +++ b/config/initializers/rack_lineprof.rb @@ -2,7 +2,7 @@ # with darker backgrounds. This patch tweaks the colors a bit so the output is # actually readable. if Rails.env.development? and RUBY_ENGINE == 'ruby' and ENV['ENABLE_LINEPROF'] - Gitlab::Application.config.middleware.use(Rack::Lineprof) + Rails.application.config.middleware.use(Rack::Lineprof) module Rack class Lineprof diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb index 1b518c3becf..dae3a4a9a93 100644 --- a/config/initializers/secret_token.rb +++ b/config/initializers/secret_token.rb @@ -22,15 +22,15 @@ def find_secure_token end end -Gitlab::Application.config.secret_token = find_secure_token -Gitlab::Application.config.secret_key_base = find_secure_token +Rails.application.config.secret_token = find_secure_token +Rails.application.config.secret_key_base = find_secure_token # CI def generate_new_secure_token SecureRandom.hex(64) end -if Gitlab::Application.secrets.db_key_base.blank? +if Rails.application.secrets.db_key_base.blank? warn "Missing `db_key_base` for '#{Rails.env}' environment. The secrets will be generated and stored in `config/secrets.yml`" all_secrets = YAML.load_file('config/secrets.yml') if File.exist?('config/secrets.yml') @@ -46,5 +46,5 @@ if Gitlab::Application.secrets.db_key_base.blank? file.write(YAML.dump(all_secrets)) end - Gitlab::Application.secrets.db_key_base = env_secrets['db_key_base'] + Rails.application.secrets.db_key_base = env_secrets['db_key_base'] end diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index f30178ff711..d5208b8c93e 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -13,11 +13,11 @@ end unless Rails.env.test? Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. - servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store + servers: Rails.application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store key: '_gitlab_session', secure: Gitlab.config.gitlab.https, httponly: true, expire_after: Settings.gitlab['session_expire_delay'] * 60, - path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root + path: (Rails.application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root ) end diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb index 42b0d78c85f..8f2ababb712 100644 --- a/config/initializers/sherlock.rb +++ b/config/initializers/sherlock.rb @@ -1,5 +1,5 @@ if Gitlab::Sherlock.enabled? - Gitlab::Application.configure do |config| + Rails.application.configure do |config| config.middleware.use(Gitlab::Sherlock::Middleware) end end diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample index 25ec247a095..ec182502d4e 100644 --- a/config/initializers/smtp_settings.rb.sample +++ b/config/initializers/smtp_settings.rb.sample @@ -8,7 +8,7 @@ # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests if Rails.env.production? - Gitlab::Application.config.action_mailer.delivery_method = :smtp + Rails.application.config.action_mailer.delivery_method = :smtp ActionMailer::Base.smtp_settings = { address: "email.server.com", diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index e6d5600edb7..d9042c652bb 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,4 +1,4 @@ -app = Gitlab::Application +app = Rails.application if app.config.serve_static_assets # The `ActionDispatch::Static` middleware intercepts requests for static files diff --git a/config/routes.rb b/config/routes.rb index ac81a2aac76..5c114452a3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ require 'sidekiq/web' require 'api/api' -Gitlab::Application.routes.draw do +Rails.application.routes.draw do if Gitlab::Sherlock.enabled? namespace :sherlock do resources :transactions, only: [:index, :show] do diff --git a/db/schema.rb b/db/schema.rb index 5bbe0c908ef..fbcb711e569 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,7 +16,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" - create_table "abuse_reports", force: true do |t| + create_table "abuse_reports", force: :cascade do |t| t.integer "reporter_id" t.integer "user_id" t.text "message" @@ -24,7 +24,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.datetime "updated_at" end - create_table "application_settings", force: true do |t| + create_table "application_settings", force: :cascade do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" t.boolean "signin_enabled" @@ -51,7 +51,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "max_artifacts_size", default: 100, null: false end - create_table "audit_events", force: true do |t| + create_table "audit_events", force: :cascade do |t| t.integer "author_id", null: false t.string "type", null: false t.integer "entity_id", null: false @@ -65,7 +65,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree - create_table "broadcast_messages", force: true do |t| + create_table "broadcast_messages", force: :cascade do |t| t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" @@ -76,14 +76,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.string "font" end - create_table "ci_application_settings", force: true do |t| + create_table "ci_application_settings", force: :cascade do |t| t.boolean "all_broken_builds" t.boolean "add_pusher" t.datetime "created_at" t.datetime "updated_at" end - create_table "ci_builds", force: true do |t| + create_table "ci_builds", force: :cascade do |t| t.integer "project_id" t.string "status" t.datetime "finished_at" @@ -123,7 +123,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree - create_table "ci_commits", force: true do |t| + create_table "ci_commits", force: :cascade do |t| t.integer "project_id" t.string "ref" t.string "sha" @@ -144,7 +144,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree - create_table "ci_events", force: true do |t| + create_table "ci_events", force: :cascade do |t| t.integer "project_id" t.integer "user_id" t.integer "is_admin" @@ -157,7 +157,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_events", ["is_admin"], name: "index_ci_events_on_is_admin", using: :btree add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree - create_table "ci_jobs", force: true do |t| + create_table "ci_jobs", force: :cascade do |t| t.integer "project_id", null: false t.text "commands" t.boolean "active", default: true, null: false @@ -174,7 +174,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_jobs", ["deleted_at"], name: "index_ci_jobs_on_deleted_at", using: :btree add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree - create_table "ci_projects", force: true do |t| + create_table "ci_projects", force: :cascade do |t| t.string "name" t.integer "timeout", default: 3600, null: false t.datetime "created_at" @@ -200,7 +200,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_projects", ["gitlab_id"], name: "index_ci_projects_on_gitlab_id", using: :btree add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree - create_table "ci_runner_projects", force: true do |t| + create_table "ci_runner_projects", force: :cascade do |t| t.integer "runner_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -210,7 +210,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree - create_table "ci_runners", force: true do |t| + create_table "ci_runners", force: :cascade do |t| t.string "token" t.datetime "created_at" t.datetime "updated_at" @@ -225,7 +225,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.string "architecture" end - create_table "ci_services", force: true do |t| + create_table "ci_services", force: :cascade do |t| t.string "type" t.string "title" t.integer "project_id", null: false @@ -237,7 +237,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree - create_table "ci_sessions", force: true do |t| + create_table "ci_sessions", force: :cascade do |t| t.string "session_id", null: false t.text "data" t.datetime "created_at" @@ -247,7 +247,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_sessions", ["session_id"], name: "index_ci_sessions_on_session_id", using: :btree add_index "ci_sessions", ["updated_at"], name: "index_ci_sessions_on_updated_at", using: :btree - create_table "ci_taggings", force: true do |t| + create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" t.string "taggable_type" @@ -260,14 +260,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "ci_taggings_idx", unique: true, using: :btree add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree - create_table "ci_tags", force: true do |t| + create_table "ci_tags", force: :cascade do |t| t.string "name" t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree - create_table "ci_trigger_requests", force: true do |t| + create_table "ci_trigger_requests", force: :cascade do |t| t.integer "trigger_id", null: false t.text "variables" t.datetime "created_at" @@ -275,7 +275,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "commit_id" end - create_table "ci_triggers", force: true do |t| + create_table "ci_triggers", force: :cascade do |t| t.string "token" t.integer "project_id", null: false t.datetime "deleted_at" @@ -285,7 +285,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree - create_table "ci_variables", force: true do |t| + create_table "ci_variables", force: :cascade do |t| t.integer "project_id", null: false t.string "key" t.text "value" @@ -296,14 +296,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree - create_table "ci_web_hooks", force: true do |t| + create_table "ci_web_hooks", force: :cascade do |t| t.string "url", null: false t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end - create_table "deploy_keys_projects", force: true do |t| + create_table "deploy_keys_projects", force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -312,7 +312,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree - create_table "emails", force: true do |t| + create_table "emails", force: :cascade do |t| t.integer "user_id", null: false t.string "email", null: false t.datetime "created_at" @@ -322,7 +322,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree - create_table "events", force: true do |t| + create_table "events", force: :cascade do |t| t.string "target_type" t.integer "target_id" t.string "title" @@ -341,7 +341,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree - create_table "forked_project_links", force: true do |t| + create_table "forked_project_links", force: :cascade do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false t.datetime "created_at" @@ -350,7 +350,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree - create_table "identities", force: true do |t| + create_table "identities", force: :cascade do |t| t.string "extern_uid" t.string "provider" t.integer "user_id" @@ -361,7 +361,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "identities", ["created_at", "id"], name: "index_identities_on_created_at_and_id", using: :btree add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree - create_table "issues", force: true do |t| + create_table "issues", force: :cascade do |t| t.string "title" t.integer "assignee_id" t.integer "author_id" @@ -387,7 +387,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "issues", ["state"], name: "index_issues_on_state", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree - create_table "keys", force: true do |t| + create_table "keys", force: :cascade do |t| t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -401,7 +401,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree - create_table "label_links", force: true do |t| + create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" t.string "target_type" @@ -412,7 +412,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "label_links", ["label_id"], name: "index_label_links_on_label_id", using: :btree add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree - create_table "labels", force: true do |t| + create_table "labels", force: :cascade do |t| t.string "title" t.string "color" t.integer "project_id" @@ -423,7 +423,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree - create_table "lfs_objects", force: true do |t| + create_table "lfs_objects", force: :cascade do |t| t.string "oid", null: false t.integer "size", null: false t.datetime "created_at" @@ -433,7 +433,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree - create_table "lfs_objects_projects", force: true do |t| + create_table "lfs_objects_projects", force: :cascade do |t| t.integer "lfs_object_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -442,7 +442,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree - create_table "members", force: true do |t| + create_table "members", force: :cascade do |t| t.integer "access_level", null: false t.integer "source_id", null: false t.string "source_type", null: false @@ -464,7 +464,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "members", ["type"], name: "index_members_on_type", using: :btree add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree - create_table "merge_request_diffs", force: true do |t| + create_table "merge_request_diffs", force: :cascade do |t| t.string "state" t.text "st_commits" t.text "st_diffs" @@ -475,7 +475,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree - create_table "merge_requests", force: true do |t| + create_table "merge_requests", force: :cascade do |t| t.string "target_branch", null: false t.string "source_branch", null: false t.integer "source_project_id", null: false @@ -507,7 +507,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree - create_table "milestones", force: true do |t| + create_table "milestones", force: :cascade do |t| t.string "title", null: false t.integer "project_id", null: false t.text "description" @@ -523,7 +523,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree - create_table "namespaces", force: true do |t| + create_table "namespaces", force: :cascade do |t| t.string "name", null: false t.string "path", null: false t.integer "owner_id" @@ -542,7 +542,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree - create_table "notes", force: true do |t| + create_table "notes", force: :cascade do |t| t.text "note" t.string "noteable_type" t.integer "author_id" @@ -571,7 +571,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree - create_table "oauth_access_grants", force: true do |t| + create_table "oauth_access_grants", force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false @@ -584,7 +584,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree - create_table "oauth_access_tokens", force: true do |t| + create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false @@ -599,7 +599,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree - create_table "oauth_applications", force: true do |t| + create_table "oauth_applications", force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false @@ -614,12 +614,12 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree - create_table "project_import_data", force: true do |t| + create_table "project_import_data", force: :cascade do |t| t.integer "project_id" t.text "data" end - create_table "projects", force: true do |t| + create_table "projects", force: :cascade do |t| t.string "name" t.string "path" t.text "description" @@ -656,7 +656,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree - create_table "protected_branches", force: true do |t| + create_table "protected_branches", force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at" @@ -666,7 +666,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree - create_table "releases", force: true do |t| + create_table "releases", force: :cascade do |t| t.string "tag" t.text "description" t.integer "project_id" @@ -677,7 +677,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree - create_table "sent_notifications", force: true do |t| + create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" t.string "noteable_type" @@ -689,7 +689,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree - create_table "services", force: true do |t| + create_table "services", force: :cascade do |t| t.string "type" t.string "title" t.integer "project_id" @@ -709,7 +709,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["template"], name: "index_services_on_template", using: :btree - create_table "snippets", force: true do |t| + create_table "snippets", force: :cascade do |t| t.string "title" t.text "content" t.integer "author_id", null: false @@ -729,7 +729,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree - create_table "subscriptions", force: true do |t| + create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" t.string "subscribable_type" @@ -740,7 +740,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree - create_table "taggings", force: true do |t| + create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" t.string "taggable_type" @@ -753,14 +753,14 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree - create_table "tags", force: true do |t| + create_table "tags", force: :cascade do |t| t.string "name" t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree - create_table "users", force: true do |t| + create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" @@ -826,7 +826,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree - create_table "users_star_projects", force: true do |t| + create_table "users_star_projects", force: :cascade do |t| t.integer "project_id", null: false t.integer "user_id", null: false t.datetime "created_at" @@ -837,7 +837,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do add_index "users_star_projects", ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree - create_table "web_hooks", force: true do |t| + create_table "web_hooks", force: :cascade do |t| t.string "url" t.integer "project_id" t.datetime "created_at" -- cgit v1.2.1 From e55473ad6880a68a86f355b7825dbdaf67e1f375 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 11:30:33 -0800 Subject: Expire application settings from cache at startup If a DB migration occurs, there's a chance that the application settings are loaded from the cache and provide stale values, causing Error 500s. This ensures that at startup the settings are always refreshed. Closes #3643 --- CHANGELOG | 1 + app/models/application_setting.rb | 12 ++++++++++-- app/models/ci/application_setting.rb | 12 ++++++++++-- config/initializers/1_settings.rb | 4 ++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..a6bef82693a 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.3.0 (unreleased) + - Ensure cached application settings are refreshed at startup (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index b2d5fe1558f..3df8135acf1 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -73,15 +73,23 @@ class ApplicationSetting < ActiveRecord::Base end after_commit do - Rails.cache.write('application_setting.last', self) + Rails.cache.write(cache_key, self) end def self.current - Rails.cache.fetch('application_setting.last') do + Rails.cache.fetch(cache_key) do ApplicationSetting.last end end + def self.expire + Rails.cache.delete(cache_key) + end + + def self.cache_key + 'application_setting.last' + end + def self.create_from_defaults create( default_projects_limit: Settings.gitlab['default_projects_limit'], diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 1307fa0b472..4e512d290ee 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -14,11 +14,15 @@ module Ci extend Ci::Model after_commit do - Rails.cache.write('ci_application_setting.last', self) + Rails.cache.write(cache_key, self) + end + + def self.expire + Rails.cache.delete(cache_key) end def self.current - Rails.cache.fetch('ci_application_setting.last') do + Rails.cache.fetch(cache_key) do Ci::ApplicationSetting.last end end @@ -29,5 +33,9 @@ module Ci add_pusher: Settings.gitlab_ci['add_pusher'], ) end + + def self.cache_key + 'ci_application_setting.last' + end end end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b162b8a83fc..80b480eac37 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -293,3 +293,7 @@ if Rails.env.test? Settings.gitlab['default_can_create_group'] = true Settings.gitlab['default_can_create_team'] = false end + +# Force a refresh of application settings at startup +ApplicationSetting.expire +Ci::ApplicationSetting.expire -- cgit v1.2.1 From 8dcef120cd94717b4f82db864191698826ca02a5 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 25 Nov 2015 17:24:07 -0200 Subject: Fix raw private snippets access workflow --- CHANGELOG | 1 + app/controllers/snippets_controller.rb | 2 +- spec/controllers/snippets_controller_spec.rb | 115 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..d61d85e4051 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission + - Fix: Raw private snippets access workflow v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 08f2483af33..c72df73af46 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -2,7 +2,7 @@ class SnippetsController < ApplicationController before_action :snippet, only: [:show, :edit, :destroy, :update, :raw] # Allow read snippet - before_action :authorize_read_snippet!, only: [:show] + before_action :authorize_read_snippet!, only: [:show, :raw] # Allow modify snippet before_action :authorize_update_snippet!, only: [:edit, :update] diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index e9b823c523c..b3dcb52c500 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -115,4 +115,119 @@ describe SnippetsController do end end end + + describe 'GET #raw' do + let(:user) { create(:user) } + + context 'when the personal snippet is private' do + let(:personal_snippet) { create(:personal_snippet, :private, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + context 'when signed in user is not the author' do + let(:other_author) { create(:author) } + let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) } + + it 'responds with status 404' do + get :raw, id: other_personal_snippet.to_param + + expect(response.status).to eq(404) + end + end + + context 'when signed in user is the author' do + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is internal' do + let(:personal_snippet) { create(:personal_snippet, :internal, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'redirects to the sign in page' do + get :raw, id: personal_snippet.to_param + + expect(response).to redirect_to(new_user_session_path) + end + end + end + + context 'when the personal snippet is public' do + let(:personal_snippet) { create(:personal_snippet, :public, author: user) } + + context 'when signed in' do + before do + sign_in(user) + end + + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + + context 'when not signed in' do + it 'renders the raw snippet' do + get :raw, id: personal_snippet.to_param + + expect(assigns(:snippet)).to eq(personal_snippet) + expect(response.status).to eq(200) + end + end + end + + context 'when the personal snippet does not exist' do + context 'when signed in' do + before do + sign_in(user) + end + + it 'responds with status 404' do + get :raw, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + + context 'when not signed in' do + it 'responds with status 404' do + get :raw, id: 'doesntexist' + + expect(response.status).to eq(404) + end + end + end + end end -- cgit v1.2.1 From 73eca2a95ef401a8e3061a14949ee1dbf34f99b9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 16:26:15 -0500 Subject: Decouple Markdown previews from DropzoneInput --- app/assets/javascripts/dropzone_input.js.coffee | 80 ++------------------- app/assets/javascripts/markdown_preview.js.coffee | 87 +++++++++++++++++++++++ app/views/projects/_md_preview.html.haml | 2 +- 3 files changed, 92 insertions(+), 77 deletions(-) create mode 100644 app/assets/javascripts/markdown_preview.js.coffee diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 6f789e668af..30a35a04339 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -1,3 +1,5 @@ +#= require markdown_preview + class @DropzoneInput constructor: (form) -> Dropzone.autoDiscover = false @@ -11,17 +13,14 @@ class @DropzoneInput uploadProgress = $("
    ") btnAlert = "" project_uploads_path = window.project_uploads_path or null - markdown_preview_path = window.markdown_preview_path or null max_file_size = gon.max_file_size or 10 form_textarea = $(form).find("textarea.markdown-area") form_textarea.wrap "
    " form_textarea.on 'paste', (event) => handlePaste(event) - form_textarea.on "input", -> - hideReferencedUsers() - form_textarea.on "blur", -> - renderMarkdown() + + $(form).setupMarkdownPreview() form_dropzone = $(form).find('.div-dropzone') form_dropzone.parent().addClass "div-dropzone-wrapper" @@ -34,42 +33,6 @@ class @DropzoneInput "opacity": 0 "display": "none" - # Preview button - $(document).off "click", ".js-md-preview-button" - $(document).on "click", ".js-md-preview-button", (e) -> - ### - Shows the Markdown preview. - - Lets the server render GFM into Html and displays it. - ### - e.preventDefault() - form = $(this).closest("form") - # toggle tabs - form.find(".js-md-write-button").parent().removeClass "active" - form.find(".js-md-preview-button").parent().addClass "active" - - # toggle content - form.find(".md-write-holder").hide() - form.find(".md-preview-holder").show() - - renderMarkdown() - - # Write button - $(document).off "click", ".js-md-write-button" - $(document).on "click", ".js-md-write-button", (e) -> - ### - Shows the Markdown textarea. - ### - e.preventDefault() - form = $(this).closest("form") - # toggle tabs - form.find(".js-md-write-button").parent().addClass "active" - form.find(".js-md-preview-button").parent().removeClass "active" - - # toggle content - form.find(".md-write-holder").show() - form.find(".md-preview-holder").hide() - dropzone = form_dropzone.dropzone( url: project_uploads_path dictDefaultMessage: "" @@ -136,41 +99,6 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") - hideReferencedUsers = -> - referencedUsers = form.find(".referenced-users") - referencedUsers.hide() - - renderReferencedUsers = (users) -> - referencedUsers = form.find(".referenced-users") - - if referencedUsers.length - if users.length >= 10 - referencedUsers.show() - referencedUsers.find(".js-referenced-users-count").text users.length - else - referencedUsers.hide() - - renderMarkdown = -> - preview = form.find(".js-md-preview") - mdText = form.find(".markdown-area").val() - if mdText.trim().length is 0 - preview.text "Nothing to preview." - hideReferencedUsers() - else - preview.text "Loading..." - $.ajax( - type: "POST", - url: markdown_preview_path, - data: { - text: mdText - }, - dataType: "json" - ).success (data) -> - preview.html data.body - preview.syntaxHighlight() - - renderReferencedUsers data.references.users - formatLink = (link) -> text = "[#{link.alt}](#{link.url})" text = "!#{text}" if link.is_image diff --git a/app/assets/javascripts/markdown_preview.js.coffee b/app/assets/javascripts/markdown_preview.js.coffee new file mode 100644 index 00000000000..98fc8f17340 --- /dev/null +++ b/app/assets/javascripts/markdown_preview.js.coffee @@ -0,0 +1,87 @@ +# MarkdownPreview +# +# Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, +# and showing a warning when more than `x` users are referenced. +# +class @MarkdownPreview + # Minimum number of users referenced before triggering a warning + referenceThreshold: 10 + + showPreview: (form) -> + preview = form.find('.js-md-preview') + mdText = form.find('textarea.markdown-area').val() + + if mdText.trim().length == 0 + preview.text('Nothing to preview.') + @hideReferencedUsers(form) + else + preview.text('Loading...') + @renderMarkdown mdText, (response) => + preview.html(response.body) + preview.syntaxHighlight() + @renderReferencedUsers(response.references.users, form) + + renderMarkdown: (text, success) -> + return unless window.markdown_preview_path + + $.ajax + type: 'POST' + url: window.markdown_preview_path + data: { text: text } + dataType: 'json' + success: success + + hideReferencedUsers: (form) -> + referencedUsers = form.find('.referenced-users') + referencedUsers.hide() + + renderReferencedUsers: (users, form) -> + referencedUsers = form.find('.referenced-users') + + if referencedUsers.length + if users.length >= @referenceThreshold + referencedUsers.show() + referencedUsers.find('.js-referenced-users-count').text(users.length) + else + referencedUsers.hide() + +markdownPreview = new MarkdownPreview() + +previewButtonSelector = '.js-md-preview-button' +writeButtonSelector = '.js-md-write-button' + +$.fn.setupMarkdownPreview = -> + $form = $(this) + + form_textarea = $form.find('textarea.markdown-area') + + form_textarea.on 'input', -> markdownPreview.hideReferencedUsers($form) + form_textarea.on 'blur', -> markdownPreview.showPreview($form) + +$(document).on 'click', previewButtonSelector, (e) -> + e.preventDefault() + + $form = $(this).closest('form') + + # toggle tabs + $form.find(writeButtonSelector).parent().removeClass('active') + $form.find(previewButtonSelector).parent().addClass('active') + + # toggle content + $form.find('.md-write-holder').hide() + $form.find('.md-preview-holder').show() + + markdownPreview.showPreview($form) + +$(document).on 'click', writeButtonSelector, (e) -> + e.preventDefault() + + $form = $(this).closest('form') + + # toggle tabs + $form.find(writeButtonSelector).parent().addClass('active') + $form.find(previewButtonSelector).parent().removeClass('active') + + # toggle content + $form.find('.md-write-holder').show() + $form.find('.md-preview-holder').hide() diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 7b21095ea3e..8218cf11201 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -5,7 +5,7 @@ %a.js-md-write-button(href="#md-write-holder" tabindex="-1") Write %li - %a.js-md-preview-button(href="md-preview-holder" tabindex="-1") + %a.js-md-preview-button(href="#md-preview-holder" tabindex="-1") Preview - if defined?(referenced_users) && referenced_users -- cgit v1.2.1 From 36bde0fcb197aa7ca93bb615cda070f55f5e268f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 14:00:35 -0800 Subject: Fix Error 500 when viewing user's personal projects from admin page This is a regression introduced in 4d7f00f. Closes #3680 --- CHANGELOG | 1 + app/views/admin/users/_projects.html.haml | 13 +++++++++++++ app/views/admin/users/projects.html.haml | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 app/views/admin/users/_projects.html.haml diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..6c78cc70b73 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.3.0 (unreleased) + - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/views/admin/users/_projects.html.haml b/app/views/admin/users/_projects.html.haml new file mode 100644 index 00000000000..a126a858ea8 --- /dev/null +++ b/app/views/admin/users/_projects.html.haml @@ -0,0 +1,13 @@ +- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present? + .panel.panel-default.contributed-projects + .panel-heading Projects contributed to + = render 'shared/projects/list', + projects: contributed_projects.sort_by(&:star_count).reverse, + projects_limit: 5, stars: true, avatar: false + +- if local_assigns.has_key?(:projects) && projects.present? + .panel.panel-default + .panel-heading Personal projects + = render 'shared/projects/list', + projects: projects.sort_by(&:star_count).reverse, + projects_limit: 10, stars: true, avatar: false diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml index 0d7a1a25a80..b655b2a15f5 100644 --- a/app/views/admin/users/projects.html.haml +++ b/app/views/admin/users/projects.html.haml @@ -14,7 +14,7 @@ .row .col-md-6 - if @personal_projects.present? - = render 'users/projects', projects: @personal_projects + = render 'admin/users/projects', projects: @personal_projects - else .nothing-here-block This user has no personal projects. -- cgit v1.2.1 From 59cc61e246dd059d041793cf4fd29ee540c7d7d2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:01:02 -0500 Subject: Bump doorkeeper to ~> 2.2.0 Closes #2746 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..491a6acd43b 100644 --- a/Gemfile +++ b/Gemfile @@ -16,7 +16,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 'doorkeeper', '~> 2.2.0' gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' gem 'omniauth-facebook', '~> 3.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..3cb986b39d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -164,7 +164,7 @@ GEM docile (1.1.5) domain_name (0.5.24) unf (>= 0.0.5, < 1.0.0) - doorkeeper (2.1.4) + doorkeeper (2.2.2) railties (>= 3.2) dropzonejs-rails (0.7.1) rails (> 3.1) @@ -824,7 +824,7 @@ DEPENDENCIES devise-async (~> 0.9.0) devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) - doorkeeper (~> 2.1.3) + doorkeeper (~> 2.2.0) dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) -- cgit v1.2.1 From 91c0aa5e982ee5e3299e6b8c899bfc396fe51279 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:03:30 -0500 Subject: Bump asana to ~> 0.4.0 Closes #2830 --- Gemfile | 2 +- Gemfile.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..8325c4d8e6a 100644 --- a/Gemfile +++ b/Gemfile @@ -153,7 +153,7 @@ gem "gemnasium-gitlab-service", "~> 0.2" gem "slack-notifier", "~> 1.2.0" # Asana integration -gem 'asana', '~> 0.0.6' +gem 'asana', '~> 0.4.0' # FogBugz integration gem 'ruby-fogbugz', '~> 0.2.1' diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..24aadef5c76 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,10 +29,6 @@ GEM actionpack (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5) railties (>= 4.0.0, < 5) - activeresource (4.0.0) - activemodel (~> 4.0) - activesupport (~> 4.0) - rails-observers (~> 0.1.1) activesupport (4.1.14) i18n (~> 0.6, >= 0.6.9) json (~> 1.7, >= 1.7.7) @@ -48,8 +44,11 @@ GEM activerecord (>= 3.2, <= 4.3) rake (~> 10.4) arel (5.0.1.20140414130214) - asana (0.0.6) - activeresource (>= 3.2.3) + asana (0.4.0) + faraday (~> 0.9) + faraday_middleware (~> 0.9) + faraday_middleware-multi_json (~> 0.0) + oauth2 (~> 1.0) asciidoctor (1.5.2) ast (2.1.0) astrolabe (1.3.1) @@ -191,6 +190,9 @@ GEM multipart-post (>= 1.2, < 3) faraday_middleware (0.10.0) faraday (>= 0.7.4, < 0.10) + faraday_middleware-multi_json (0.0.6) + faraday_middleware + multi_json fastercsv (1.5.5) ffaker (2.0.0) ffi (1.9.10) @@ -523,8 +525,6 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 4.1.14) sprockets-rails (~> 2.0) - rails-observers (0.1.2) - activemodel (~> 4.0) railties (4.1.14) actionpack (= 4.1.14) activesupport (= 4.1.14) @@ -795,7 +795,7 @@ DEPENDENCIES addressable (~> 2.3.8) after_commit_queue annotate (~> 2.6.0) - asana (~> 0.0.6) + asana (~> 0.4.0) asciidoctor (~> 1.5.2) attr_encrypted (~> 1.3.4) awesome_print (~> 1.2.0) -- cgit v1.2.1 From 5f0d1f30b0f0e31d31a2eb0db9f92776bf551f26 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 25 Nov 2015 17:06:04 -0500 Subject: Remove enumerize gem --- Gemfile | 3 --- Gemfile.lock | 3 --- app/models/project.rb | 3 +-- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 4762e2e223f..1beeabd44d2 100644 --- a/Gemfile +++ b/Gemfile @@ -62,9 +62,6 @@ gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' # based on human-friendly examples gem "stamp", '~> 0.6.0' -# Enumeration fields -gem 'enumerize', '~> 0.7.0' - # Pagination gem "kaminari", "~> 0.16.3" diff --git a/Gemfile.lock b/Gemfile.lock index 46247dd88e2..ef083754f0f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -173,8 +173,6 @@ GEM launchy (~> 2.1) mail (~> 2.2) encryptor (1.3.0) - enumerize (0.7.0) - activesupport (>= 3.2) equalizer (0.0.11) erubis (2.7.0) escape_utils (1.1.0) @@ -828,7 +826,6 @@ DEPENDENCIES dropzonejs-rails (~> 0.7.1) email_reply_parser (~> 0.5.8) email_spec (~> 1.6.0) - enumerize (~> 0.7.0) factory_girl_rails (~> 4.3.0) ffaker (~> 2.0.0) flay diff --git a/app/models/project.rb b/app/models/project.rb index f0a4b6aae7b..6010770a5f2 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -42,9 +42,8 @@ class Project < ActiveRecord::Base include Sortable include AfterCommitQueue include CaseSensitivity - + extend Gitlab::ConfigHelper - extend Enumerize UNKNOWN_IMPORT_URL = 'http://unknown.git' -- cgit v1.2.1 From 7adfb30c349396f19b71a703419a475bd1d4cd5b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 17:06:36 -0800 Subject: Remove duplicate entry shipped in 8.1.4 --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..18381984177 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,7 +14,6 @@ v 8.2.0 - Expose build artifacts path as config option - Fix grouping of contributors by email in graph. - Improved performance of finding issues with/without labels - - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu) - Fix Drone CI service template not saving properly (Stan Hu) - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu) - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749) -- cgit v1.2.1 From 2497d3d550dc0d4e095c8c3fe75d4452fb163252 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 23:11:35 -0800 Subject: Fix 404 in redirection after removing a project Closes https://github.com/gitlabhq/gitlabhq/issues/9844 Closes #3559 --- CHANGELOG | 1 + app/controllers/projects_controller.rb | 2 +- spec/controllers/projects_controller_spec.rb | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 8916b9f0403..3dd96e34dcf 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.3.0 (unreleased) + - Fix 404 in redirection after removing a project (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 23453195e85..10c75370d7b 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -123,7 +123,7 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute flash[:alert] = "Project '#{@project.name}' was deleted." - redirect_back_or_default(default: dashboard_projects_path, options: {}) + redirect_to dashboard_projects_path rescue Projects::DestroyService::DestroyError => ex redirect_to edit_project_path(@project), alert: ex.message end diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 4bb47c6b025..665526fde93 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -88,6 +88,22 @@ describe ProjectsController do end end + describe "#destroy" do + let(:admin) { create(:admin) } + + it "redirects to the dashboard" do + controller.instance_variable_set(:@project, project) + sign_in(admin) + + orig_id = project.id + delete :destroy, namespace_id: project.namespace.path, id: project.path + + expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound) + expect(response.status).to eq(302) + expect(response).to redirect_to(dashboard_projects_path) + end + end + describe "POST #toggle_star" do it "toggles star if user is signed in" do sign_in(user) -- cgit v1.2.1 From 38489d8d7d0a352789c23d3bf9cadb169f765d5b Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 26 Nov 2015 11:57:04 +0200 Subject: update test_after_commit gem to 0.4.2 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 4a9f70a1297..dc801d60b22 100644 --- a/Gemfile +++ b/Gemfile @@ -274,7 +274,7 @@ group :test do gem 'shoulda-matchers', '~> 2.8.0', require: false gem 'email_spec', '~> 1.6.0' gem 'webmock', '~> 1.21.0' - gem 'test_after_commit', '~> 0.2.2' + gem 'test_after_commit', '~> 0.4.2' gem 'sham_rack' end diff --git a/Gemfile.lock b/Gemfile.lock index e9460515051..bb4f94aeef9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -750,7 +750,7 @@ GEM term-ansicolor (1.3.2) tins (~> 1.0) terminal-table (1.5.2) - test_after_commit (0.2.7) + test_after_commit (0.4.2) activerecord (>= 3.2) thin (1.6.4) daemons (~> 1.0, >= 1.0.9) @@ -970,7 +970,7 @@ DEPENDENCIES task_list (~> 1.0.2) teaspoon (~> 1.0.0) teaspoon-jasmine (~> 2.2.0) - test_after_commit (~> 0.2.2) + test_after_commit (~> 0.4.2) thin (~> 1.6.1) tinder (~> 1.10.0) turbolinks (~> 2.5.0) -- cgit v1.2.1 From 7f214cee74796ceaf7b01bd6e133d4d54c5123db Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 26 Nov 2015 15:48:01 +0200 Subject: Migrate mailers to ActiveJob --- Gemfile | 1 + Gemfile.lock | 1 + Procfile | 2 +- app/controllers/abuse_reports_controller.rb | 2 +- app/mailers/base_mailer.rb | 4 -- app/models/project_services/ci/mail_service.rb | 2 +- app/services/notification_service.rb | 74 ++++++++++++++++++-------- app/workers/email_receiver_worker.rb | 2 +- config/application.rb | 4 ++ config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/static_files.rb | 2 +- lib/gitlab/markdown/label_reference_filter.rb | 3 +- lib/gitlab/seeder.rb | 2 +- 14 files changed, 67 insertions(+), 36 deletions(-) diff --git a/Gemfile b/Gemfile index 808c5df7caf..91c909a2e7d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source "https://rubygems.org" gem 'rails', '4.2.4' +gem 'rails-deprecated_sanitizer', '~> 1.0.3' # Responders respond_to and respond_with gem 'responders', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1671edbc6fd..08001379910 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -930,6 +930,7 @@ DEPENDENCIES rack-cors (~> 0.4.0) rack-oauth2 (~> 1.2.1) rails (= 4.2.4) + rails-deprecated_sanitizer (~> 1.0.3) raphael-rails (~> 2.1.2) rblineprof rdoc (~> 3.6) diff --git a/Procfile b/Procfile index 08880b9c425..fd5f7ecb94b 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default +worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 2f4054eaa11..d8e90594332 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController if @abuse_report.save if current_application_settings.admin_notification_email.present? - AbuseReportMailer.delay.notify(@abuse_report.id) + AbuseReportMailer.deliver_later.notify(@abuse_report.id) end message = "Thank you for your report. A GitLab administrator will look into it shortly." diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb index aedb0889185..8b83bbd93b7 100644 --- a/app/mailers/base_mailer.rb +++ b/app/mailers/base_mailer.rb @@ -8,10 +8,6 @@ class BaseMailer < ActionMailer::Base default from: Proc.new { default_sender_address.format } default reply_to: Proc.new { default_reply_to_address.format } - def self.delay - delay_for(2.seconds) - end - def can? Ability.abilities.allowed?(current_user, action, subject) end diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index d31dd6899c1..bdc85667e9d 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -78,7 +78,7 @@ module Ci end def mailer - Ci::Notify.delay + Ci::Notify.deliver_later end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index d6550fbb555..03fe8c8fe11 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -13,14 +13,14 @@ class NotificationService # even if user disabled notifications def new_key(key) if key.user - mailer.new_ssh_key_email(key.id) + mailer.new_ssh_key_email(key.id).deliver_later end end # Always notify user about email added to profile def new_email(email) if email.user - mailer.new_email_email(email.id) + mailer.new_email_email(email.id).deliver_later end end @@ -79,17 +79,27 @@ class NotificationService end def merge_mr(merge_request, current_user) - close_resource_email(merge_request, merge_request.target_project, current_user, 'merged_merge_request_email') + close_resource_email( + merge_request, + merge_request.target_project, + current_user, + 'merged_merge_request_email' + ) end def reopen_mr(merge_request, current_user) - reopen_resource_email(merge_request, merge_request.target_project, current_user, 'merge_request_status_email', 'reopened') + reopen_resource_email( + merge_request, + merge_request.target_project, + current_user, 'merge_request_status_email', + 'reopened' + ) end # Notify new user with email after creation def new_user(user, token = nil) # Don't email omniauth created users - mailer.new_user_email(user.id, token) unless user.identities.any? + mailer.new_user_email(user.id, token).deliver_later unless user.identities.any? end # Notify users on new note in system @@ -140,48 +150,58 @@ class NotificationService notify_method = "note_#{note.noteable_type.underscore}_email".to_sym recipients.each do |recipient| - mailer.send(notify_method, recipient.id, note.id) + mailer.send(notify_method, recipient.id, note.id).deliver_later end end def invite_project_member(project_member, token) - mailer.project_member_invited_email(project_member.id, token) + mailer.project_member_invited_email(project_member.id, token).deliver_later end def accept_project_invite(project_member) - mailer.project_invite_accepted_email(project_member.id) + mailer.project_invite_accepted_email(project_member.id).deliver_later end def decline_project_invite(project_member) - mailer.project_invite_declined_email(project_member.project.id, project_member.invite_email, project_member.access_level, project_member.created_by_id) + mailer.project_invite_declined_email( + project_member.project.id, + project_member.invite_email, + project_member.access_level, + project_member.created_by_id + ).deliver_later end def new_project_member(project_member) - mailer.project_access_granted_email(project_member.id) + mailer.project_access_granted_email(project_member.id).deliver_later end def update_project_member(project_member) - mailer.project_access_granted_email(project_member.id) + mailer.project_access_granted_email(project_member.id).deliver_later end def invite_group_member(group_member, token) - mailer.group_member_invited_email(group_member.id, token) + mailer.group_member_invited_email(group_member.id, token).deliver_later end def accept_group_invite(group_member) - mailer.group_invite_accepted_email(group_member.id) + mailer.group_invite_accepted_email(group_member.id).deliver_later end def decline_group_invite(group_member) - mailer.group_invite_declined_email(group_member.group.id, group_member.invite_email, group_member.access_level, group_member.created_by_id) + mailer.group_invite_declined_email( + group_member.group.id, + group_member.invite_email, + group_member.access_level, + group_member.created_by_id + ).deliver_later end def new_group_member(group_member) - mailer.group_access_granted_email(group_member.id) + mailer.group_access_granted_email(group_member.id).deliver_later end def update_group_member(group_member) - mailer.group_access_granted_email(group_member.id) + mailer.group_access_granted_email(group_member.id).deliver_later end def project_was_moved(project, old_path_with_namespace) @@ -189,7 +209,11 @@ class NotificationService recipients = reject_muted_users(recipients, project) recipients.each do |recipient| - mailer.project_was_moved_email(project.id, recipient.id, old_path_with_namespace) + mailer.project_was_moved_email( + project.id, + recipient.id, + old_path_with_namespace + ).deliver_later end end @@ -339,7 +363,7 @@ class NotificationService recipients = build_recipients(target, project, target.author) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id) + mailer.send(method, recipient.id, target.id).deliver_later end end @@ -347,7 +371,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, current_user.id) + mailer.send(method, recipient.id, target.id, current_user.id).deliver end end @@ -358,7 +382,13 @@ class NotificationService recipients = build_recipients(target, project, current_user, [previous_assignee]) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, previous_assignee_id, current_user.id) + mailer.send( + method, + recipient.id, + target.id, + previous_assignee_id, + current_user.id + ).deliver_later end end @@ -366,7 +396,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, status, current_user.id) + mailer.send(method, recipient.id, target.id, status, current_user.id).deliver end end @@ -388,7 +418,7 @@ class NotificationService end def mailer - Notify.delay + Notify end def previous_record(object, attribute) diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 5a921a73fe9..1df8de1db79 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -46,6 +46,6 @@ class EmailReceiverWorker return end - EmailRejectionMailer.delay.rejection(reason, raw, can_retry) + EmailRejectionMailer.deliver_later.rejection(reason, raw, can_retry) end end diff --git a/config/application.rb b/config/application.rb index bfa2a809dd7..d255ff0719f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -99,6 +99,10 @@ module Gitlab redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever config.cache_store = :redis_store, redis_config_hash + config.active_record.raise_in_transactional_callbacks = true + + config.active_job.queue_adapter = :sidekiq + # This is needed for gitlab-shell ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH'] end diff --git a/config/environments/production.rb b/config/environments/production.rb index e8250d66452..317b113e100 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -9,7 +9,7 @@ Rails.application.configure do config.action_controller.perform_caching = true # Disable Rails's static asset server (Apache or nginx will already do this) - config.serve_static_assets = false + config.serve_static_files = false # Compress JavaScripts and CSS. config.assets.js_compressor = :uglifier diff --git a/config/environments/test.rb b/config/environments/test.rb index 46982be2864..2eddf0605d2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -8,7 +8,7 @@ Rails.application.configure do config.cache_classes = false # Configure static asset server for tests with Cache-Control for performance - config.serve_static_assets = true + config.serve_static_files = true config.static_cache_control = "public, max-age=3600" # Show full error reports and disable caching diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb index d9042c652bb..d6dbf8b9fbf 100644 --- a/config/initializers/static_files.rb +++ b/config/initializers/static_files.rb @@ -1,6 +1,6 @@ app = Rails.application -if app.config.serve_static_assets +if app.config.serve_static_files # The `ActionDispatch::Static` middleware intercepts requests for static files # by checking if they exist in the `/public` directory. # We're replacing it with our `Gitlab::Middleware::Static` that does the same, diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 618acb7a578..13581b8fb13 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -60,8 +60,7 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers h.namespace_project_issues_path(project.namespace, project, - label_name: label.name, - only_path: context[:only_path]) + label_name: label.name) end def render_colored_label(label) diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb index 31aa3528c4c..2ef0e982256 100644 --- a/lib/gitlab/seeder.rb +++ b/lib/gitlab/seeder.rb @@ -14,7 +14,7 @@ module Gitlab def self.mute_mailer code = <<-eos -def Notify.delay +def Notify.deliver_later self end eos -- cgit v1.2.1 From b9df1a63550c78396d43b661bd24d2745604f6fc Mon Sep 17 00:00:00 2001 From: Jose Corcuera Date: Thu, 26 Nov 2015 10:16:50 -0500 Subject: Strip attributes for Milestone and Issuable. #3428 --- CHANGELOG | 1 + app/controllers/projects/issues_controller.rb | 4 +-- .../projects/merge_requests_controller.rb | 4 +-- app/models/concerns/issuable.rb | 2 ++ app/models/concerns/strip_attribute.rb | 34 ++++++++++++++++++++++ app/models/milestone.rb | 3 ++ spec/models/concerns/strip_attribute_spec.rb | 20 +++++++++++++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 app/models/concerns/strip_attribute.rb create mode 100644 spec/models/concerns/strip_attribute_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 0e1e1a3671d..39a57231d9c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow + - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 5250a0f5e67..ae474cf8d68 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -158,12 +158,10 @@ class Projects::IssuesController < Projects::ApplicationController end def issue_params - permitted = params.require(:issue).permit( + params.require(:issue).permit( :title, :assignee_id, :position, :description, :milestone_id, :state_event, :task_num, label_ids: [] ) - params[:issue][:title].strip! if params[:issue][:title] - permitted end def bulk_update_params diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 6378a1f56b0..3f47f2ddb2c 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -276,13 +276,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_request_params - permitted = params.require(:merge_request).permit( + params.require(:merge_request).permit( :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :state_event, :description, :task_num, label_ids: [] ) - params[:merge_request][:title].strip! if params[:merge_request][:title] - permitted end # Make sure merge requests created before 8.0 diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 68138688aab..badeadfa418 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -8,6 +8,7 @@ module Issuable extend ActiveSupport::Concern include Participable include Mentionable + include StripAttribute included do belongs_to :author, class_name: "User" @@ -51,6 +52,7 @@ module Issuable attr_mentionable :title, :description participant :author, :assignee, :notes_with_associations + strip_attributes :title end module ClassMethods diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb new file mode 100644 index 00000000000..8806ebe897a --- /dev/null +++ b/app/models/concerns/strip_attribute.rb @@ -0,0 +1,34 @@ +# == Strip Attribute module +# +# Contains functionality to clean attributes before validation +# +# Usage: +# +# class Milestone < ActiveRecord::Base +# strip_attributes :title +# end +# +# +module StripAttribute + extend ActiveSupport::Concern + + module ClassMethods + def strip_attributes(*attrs) + strip_attrs.concat(attrs) + end + + def strip_attrs + @strip_attrs ||= [] + end + end + + included do + before_validation :strip_attributes + end + + def strip_attributes + self.class.strip_attrs.each do |attr| + self[attr].strip! if self[attr] && self[attr].respond_to?(:strip!) + end + end +end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 2ff16e2825c..c2642b75b8a 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base include InternalId include Sortable + include StripAttribute belongs_to :project has_many :issues @@ -35,6 +36,8 @@ class Milestone < ActiveRecord::Base validates :title, presence: true validates :project, presence: true + strip_attributes :title + state_machine :state, initial: :active do event :close do transition active: :closed diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb new file mode 100644 index 00000000000..6445e29c3ef --- /dev/null +++ b/spec/models/concerns/strip_attribute_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Milestone, "StripAttribute" do + let(:milestone) { create(:milestone) } + + describe ".strip_attributes" do + it { expect(Milestone).to respond_to(:strip_attributes) } + it { expect(Milestone.strip_attrs).to include(:title) } + end + + describe "#strip_attributes" do + before do + milestone.title = ' 8.3 ' + milestone.valid? + end + + it { expect(milestone.title).to eq('8.3') } + end + +end -- cgit v1.2.1 From 78c1ab40e20f2c412719a2140d9de61ada26d1b8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 26 Nov 2015 07:55:21 -0800 Subject: Gracefully handle when Redis is not available --- config/initializers/1_settings.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 80b480eac37..444e91109df 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -295,5 +295,10 @@ if Rails.env.test? end # Force a refresh of application settings at startup -ApplicationSetting.expire -Ci::ApplicationSetting.expire +begin + ApplicationSetting.expire + Ci::ApplicationSetting.expire +rescue + # Gracefully handle when Redis is not available. For example, + # omnibus may fail here during assets:precompile. +end -- cgit v1.2.1 From 295d378e9ab1a8e052e35c6e6fedf8d6890b74d0 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 27 Nov 2015 13:56:26 +0100 Subject: Repeat "client_max_body_size 0" everywhere It turns out that if we do not the declaration from "location /" wins. --- lib/support/nginx/gitlab | 6 ++++++ lib/support/nginx/gitlab-ssl | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 0cf5292b290..1dfcac91cdc 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -114,24 +114,28 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -139,6 +143,7 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -146,6 +151,7 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 31a651c87fd..7cd32de073b 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -161,24 +161,28 @@ server { } location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; } location ~ ^/api/v3/projects/.*/repository/archive { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -186,6 +190,7 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; @@ -193,6 +198,7 @@ server { # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { + client_max_body_size 0; # 'Error' 418 is a hack to re-use the @gitlab-workhorse block error_page 418 = @gitlab-workhorse; return 418; -- cgit v1.2.1 From 04049b6b17c9354555115b5d2f049daffa311c16 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 27 Nov 2015 13:57:53 +0100 Subject: Fix indentation in NGINX config --- lib/support/nginx/gitlab-ssl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 7cd32de073b..016f7a536fb 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -191,17 +191,17 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } location @gitlab-workhorse { -- cgit v1.2.1 From f1710073b46c940245d38b46e5436c0745223aee Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 14:39:55 -0500 Subject: Fix alignment [ci skip] --- lib/support/nginx/gitlab | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 1dfcac91cdc..2a79fbdcf93 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -144,17 +144,17 @@ server { # Build artifacts should be submitted to this location location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } # Build artifacts should be submitted to this location location ~ /ci/api/v1/builds/[0-9]+/artifacts { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; + # 'Error' 418 is a hack to re-use the @gitlab-workhorse block + error_page 418 = @gitlab-workhorse; + return 418; } location @gitlab-workhorse { -- cgit v1.2.1 From 3723182a8831595502570c90211610abb4770781 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 16:15:06 -0500 Subject: Move project visibility level off of the clone panel --- app/assets/stylesheets/pages/projects.scss | 5 +++++ app/views/projects/_home_panel.html.haml | 10 ++++++---- app/views/shared/_clone_panel.html.haml | 4 ---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ad607ead2bf..4a0fe546844 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -90,7 +90,12 @@ } .visibility-level-label { + @extend .btn; + @extend .btn-gray; + color: $gray; + cursor: auto; + i { color: inherit; } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 88d54bf6f21..b30036966a7 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,5 +1,5 @@ - empty_repo = @project.empty_repo? -.project-home-panel.clearfix{:class => ("empty-project" if empty_repo)} +.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)} .project-identicon-holder = project_icon(@project, alt: '', class: 'project-avatar avatar s90') .project-home-desc @@ -12,8 +12,10 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - - + .cover-controls + .visibility-level-label + = visibility_level_icon(@project.visibility_level) + = visibility_level_label(@project.visibility_level) .project-repo-buttons .split-one @@ -21,7 +23,7 @@ = render 'projects/buttons/fork' = render "shared/clone_panel" - + .split-repo-buttons = render "projects/buttons/download" = render 'projects/buttons/dropdown' diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 408a46aaafc..edb5778f424 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -8,7 +8,3 @@ = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true .input-group-btn = clipboard_button(clipboard_target: '#project_clone') - - if project.kind_of?(Project) - .input-group-addon.has_tooltip{title: "#{visibility_level_label(project.visibility_level)} project", data: { container: "body" } } - .visibility-level-label - = visibility_level_icon(project.visibility_level) -- cgit v1.2.1 From 344cb89718d9424e239a24dbc888d7b7a98cb474 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 27 Nov 2015 16:34:59 -0500 Subject: Bump jquery-turbolinks to ~> 2.1.0 See #2857 --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 685b126a793..51ce6c9ed72 100644 --- a/Gemfile +++ b/Gemfile @@ -183,7 +183,7 @@ gem "sass-rails", '~> 4.0.5' gem "coffee-rails", '~> 4.1.0' gem "uglifier", '~> 2.7.2' gem 'turbolinks', '~> 2.5.0' -gem 'jquery-turbolinks', '~> 2.0.1' +gem 'jquery-turbolinks', '~> 2.1.0' gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 8fb2ef778ea..54a63ea22df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -121,7 +121,7 @@ GEM coffee-script (2.4.1) coffee-script-source execjs - coffee-script-source (1.9.1.1) + coffee-script-source (1.10.0) colorize (0.7.7) connection_pool (2.2.0) coveralls (0.8.2) @@ -370,7 +370,7 @@ GEM thor (>= 0.14, < 2.0) jquery-scrollto-rails (1.4.3) railties (> 3.1, < 5.0) - jquery-turbolinks (2.0.2) + jquery-turbolinks (2.1.0) railties (>= 3.1.0) turbolinks jquery-ui-rails (4.2.1) @@ -853,7 +853,7 @@ DEPENDENCIES jquery-atwho-rails (~> 1.3.2) jquery-rails (~> 3.1.3) jquery-scrollto-rails (~> 1.4.3) - jquery-turbolinks (~> 2.0.1) + jquery-turbolinks (~> 2.1.0) jquery-ui-rails (~> 4.2.1) kaminari (~> 0.16.3) letter_opener (~> 1.1.2) -- cgit v1.2.1 From 85ad2140fa4ef2209b9d217403e183e33ce4dd37 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Sat, 28 Nov 2015 20:53:17 +0100 Subject: Remove reverence to satellites [ci skip] --- doc/raketasks/backup_restore.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 4e645b21a85..b4d2786bd76 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -274,9 +274,6 @@ sudo gitlab-rake gitlab:backup:restore BACKUP=1393513186 # Start GitLab sudo gitlab-ctl start -# Create satellites -sudo gitlab-rake gitlab:satellites:create - # Check GitLab sudo gitlab-rake gitlab:check SANITIZE=true ``` -- cgit v1.2.1 From 7d6323a9d4ada6be6c00f7d160b366219952f77f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sun, 29 Nov 2015 11:51:02 +0200 Subject: Update phantomjs to 2.0.0 * Version 1.9 was removed from upstream Debian repos --- scripts/prepare_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index dca5e1c5db3..5ff4d7fa76a 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ -f /.dockerinit ]; then - wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_1.9.0-1+b1_amd64.deb - dpkg -i phantomjs_1.9.0-1+b1_amd64.deb + wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_2.0.0+dfsg-1_amd64.deb + dpkg -i phantomjs_2.0.0+dfsg-1_amd64.deb apt-get update -qq apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client -- cgit v1.2.1 From bb6ce2bb10734cfc45e488378acaa17000fdacc2 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sun, 29 Nov 2015 13:44:19 +0200 Subject: Test using a 1.9.8 phantomjs version built with fpm https://gitlab.com/axil/phantomjs-debian --- scripts/prepare_build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 5ff4d7fa76a..119cc90fc1e 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,7 +1,7 @@ #!/bin/bash if [ -f /.dockerinit ]; then - wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_2.0.0+dfsg-1_amd64.deb - dpkg -i phantomjs_2.0.0+dfsg-1_amd64.deb + wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb + dpkg -i phantomjs_1.9.8-0jessie_amd64.deb apt-get update -qq apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client -- cgit v1.2.1 From e1522ec85582962c12f875a46a5853d6ae570d0b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 29 Nov 2015 17:52:40 -0500 Subject: Simplify `build_gitlab_shell_ssh_path_prefix` --- config/initializers/1_settings.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index b162b8a83fc..abe08d57186 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -33,13 +33,15 @@ class Settings < Settingslogic end def build_gitlab_shell_ssh_path_prefix + user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}" + if gitlab_shell.ssh_port != 22 - "ssh://#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:#{gitlab_shell.ssh_port}/" + "ssh://#{user_host}:#{gitlab_shell.ssh_port}/" else if gitlab_shell.ssh_host.include? ':' - "[#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}]:" + "[#{user_host}]:" else - "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:" + "#{user_host}:" end end end -- cgit v1.2.1 From 461731f0769a826d00c4d5846ff6d2f55fd4b829 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 11:21:10 +0200 Subject: fix notification_service specs --- app/services/notification_service.rb | 5 +- config/environments/test.rb | 2 + spec/services/notification_service_spec.rb | 345 ++++++++++++----------------- spec/spec_helper.rb | 1 + 4 files changed, 144 insertions(+), 209 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 03fe8c8fe11..388a4defb26 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -148,7 +148,6 @@ class NotificationService # build notify method like 'note_commit_email' notify_method = "note_#{note.noteable_type.underscore}_email".to_sym - recipients.each do |recipient| mailer.send(notify_method, recipient.id, note.id).deliver_later end @@ -371,7 +370,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, current_user.id).deliver + mailer.send(method, recipient.id, target.id, current_user.id).deliver_later end end @@ -396,7 +395,7 @@ class NotificationService recipients = build_recipients(target, project, current_user) recipients.each do |recipient| - mailer.send(method, recipient.id, target.id, status, current_user.id).deliver + mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later end end diff --git a/config/environments/test.rb b/config/environments/test.rb index 2eddf0605d2..f96ac6f9753 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -32,4 +32,6 @@ Rails.application.configure do config.eager_load = false config.cache_store = :null_store + + config.active_job.queue_adapter = :test end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 520140917aa..a4e2b2953cc 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -3,6 +3,12 @@ require 'spec_helper' describe NotificationService do let(:notification) { NotificationService.new } + around(:each) do |example| + perform_enqueued_jobs do + example.run + end + end + describe 'Keys' do describe :new_key do let!(:key) { create(:personal_key) } @@ -10,8 +16,7 @@ describe NotificationService do it { expect(notification.new_key(key)).to be_truthy } it 'should sent email to key owner' do - expect(Notify).to receive(:new_ssh_key_email).with(key.id) - notification.new_key(key) + expect{ notification.new_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1) end end end @@ -23,8 +28,7 @@ describe NotificationService do it { expect(notification.new_email(email)).to be_truthy } it 'should send email to email owner' do - expect(Notify).to receive(:new_email_email).with(email.id) - notification.new_email(email) + expect{ notification.new_email(email) }.to change{ ActionMailer::Base.deliveries.size }.by(1) end end end @@ -47,18 +51,20 @@ describe NotificationService do it do add_users_with_subscription(note.project, issue) - should_email(@u_watcher.id) - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_email(@u_mentioned.id) - should_email(@subscriber.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_outsider_mentioned) + ActionMailer::Base.deliveries.clear notification.new_note(note) + + should_email(@u_watcher) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_email(@u_mentioned) + should_email(@subscriber) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + should_not_email(@unsubscriber) + should_not_email(@u_outsider_mentioned) end it 'filters out "mentioned in" notes' do @@ -82,26 +88,20 @@ describe NotificationService do group_member = note.project.group.group_members.find_by_user_id(@u_watcher.id) group_member.notification_level = Notification::N_GLOBAL group_member.save + ActionMailer::Base.deliveries.clear end it do - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_email(@u_mentioned.id) - should_not_email(@u_watcher.id) - should_not_email(note.author_id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_note(note) - end - end - def should_email(user_id) - expect(Notify).to receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_email(@u_mentioned) + should_not_email(@u_watcher) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) + end end end @@ -113,24 +113,26 @@ describe NotificationService do before do build_team(note.project) + ActionMailer::Base.deliveries.clear end describe :new_note do it do + notification.new_note(note) + # Notify all team members note.project.team.members.each do |member| # User with disabled notification should not be notified next if member.id == @u_disabled.id - should_email(member.id) + should_email(member) end - should_email(note.noteable.author_id) - should_email(note.noteable.assignee_id) - should_not_email(note.author_id) - should_not_email(@u_mentioned.id) - should_not_email(@u_disabled.id) - should_not_email(@u_not_mentioned.id) - notification.new_note(note) + should_email(note.noteable.author) + should_email(note.noteable.assignee) + should_not_email(note.author) + should_email(@u_mentioned) + should_not_email(@u_disabled) + should_email(@u_not_mentioned) end it 'filters out "mentioned in" notes' do @@ -140,14 +142,6 @@ describe NotificationService do notification.new_note(mentioned_note) end end - - def should_email(user_id) - expect(Notify).to receive(:note_issue_email).with(user_id, note.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id) - end end context 'commit note' do @@ -156,43 +150,38 @@ describe NotificationService do before do build_team(note.project) + ActionMailer::Base.deliveries.clear allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) end - describe :new_note do + describe :new_note, :perform_enqueued_jobs do it do - should_email(@u_committer.id, note) - should_email(@u_watcher.id, note) - should_not_email(@u_mentioned.id, note) - should_not_email(note.author_id, note) - should_not_email(@u_participating.id, note) - should_not_email(@u_disabled.id, note) notification.new_note(note) + + should_email(@u_committer) + should_email(@u_watcher) + should_not_email(@u_mentioned) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do note.update_attribute(:note, '@mention referenced') - should_email(@u_committer.id, note) - should_email(@u_watcher.id, note) - should_email(@u_mentioned.id, note) - should_not_email(note.author_id, note) - should_not_email(@u_participating.id, note) - should_not_email(@u_disabled.id, note) notification.new_note(note) + + should_email(@u_committer) + should_email(@u_watcher) + should_email(@u_mentioned) + should_not_email(note.author) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do @u_committer.update_attributes(notification_level: Notification::N_MENTION) - should_not_email(@u_committer.id, note) notification.new_note(note) - end - - def should_email(user_id, n) - expect(Notify).to receive(:note_commit_email).with(user_id, n.id) - end - - def should_not_email(user_id, n) - expect(Notify).not_to receive(:note_commit_email).with(user_id, n.id) + should_not_email(@u_committer) end end end @@ -205,99 +194,69 @@ describe NotificationService do before do build_team(issue.project) add_users_with_subscription(issue.project, issue) + ActionMailer::Base.deliveries.clear end describe :new_issue do it do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_not_email(@u_mentioned.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_issue(issue, @u_disabled) + + should_email(issue.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_not_email(@u_mentioned) + should_not_email(@u_participating) + should_not_email(@u_disabled) end it do issue.assignee.update_attributes(notification_level: Notification::N_MENTION) - should_not_email(issue.assignee_id) notification.new_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:new_issue_email).with(user_id, issue.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:new_issue_email).with(user_id, issue.id) + should_not_email(issue.assignee) end end describe :reassigned_issue do it 'should email new assignee' do - should_email(issue.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reassigned_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id) + should_email(issue.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :close_issue do it 'should sent email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.close_issue(issue, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id) + should_email(issue.assignee) + should_email(issue.author) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reopen_issue do it 'should send email to issue assignee and issue author' do - should_email(issue.assignee_id) - should_email(issue.author_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) - notification.reopen_issue(issue, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id) + should_email(issue.assignee) + should_email(issue.author) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) end end end @@ -309,108 +268,74 @@ describe NotificationService do before do build_team(merge_request.target_project) add_users_with_subscription(merge_request.target_project, merge_request) + ActionMailer::Base.deliveries.clear end describe :new_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.new_merge_request(merge_request, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:new_merge_request_email).with(user_id, merge_request.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:new_merge_request_email).with(user_id, merge_request.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reassigned_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.reassigned_merge_request(merge_request, merge_request.author) - end - - def should_email(user_id) - expect(Notify).to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :closed_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.close_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :merged_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.merge_mr(merge_request, @u_disabled) - end - - def should_email(user_id) - expect(Notify).to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) - end - def should_not_email(user_id) - expect(Notify).not_to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end describe :reopen_merge_request do it do - should_email(merge_request.assignee_id) - should_email(@u_watcher.id) - should_email(@u_participant_mentioned.id) - should_email(@subscriber.id) - should_not_email(@unsubscriber.id) - should_not_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.reopen_mr(merge_request, @u_disabled) - end - def should_email(user_id) - expect(Notify).to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) - end - - def should_not_email(user_id) - expect(Notify).not_to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id) + should_email(merge_request.assignee) + should_email(@u_watcher) + should_email(@u_participant_mentioned) + should_email(@subscriber) + should_not_email(@unsubscriber) + should_not_email(@u_participating) + should_not_email(@u_disabled) end end end @@ -420,22 +345,16 @@ describe NotificationService do before do build_team(project) + ActionMailer::Base.deliveries.clear end describe :project_was_moved do it do - should_email(@u_watcher.id) - should_email(@u_participating.id) - should_not_email(@u_disabled.id) notification.project_was_moved(project, "gitlab/gitlab") - end - - def should_email(user_id) - expect(Notify).to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab") - end - def should_not_email(user_id) - expect(Notify).not_to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab") + should_email(@u_watcher) + should_email(@u_participating) + should_not_email(@u_disabled) end end end @@ -469,4 +388,18 @@ describe NotificationService do issuable.subscriptions.create(user: @subscriber, subscribed: true) issuable.subscriptions.create(user: @unsubscriber, subscribed: false) end + + def sent_to_user?(user) + ActionMailer::Base.deliveries.any? do |message| + message.to.include?(user.email) + end + end + + def should_email(user) + expect(sent_to_user?(user)).to be_truthy + end + + def should_not_email(user) + expect(sent_to_user?(user)).to be_falsey + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2be13bb3e6a..0225a0ee53f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -31,6 +31,7 @@ RSpec.configure do |config| config.include StubConfiguration config.include RelativeUrl, type: feature config.include TestEnv + config.include ActiveJob::TestHelper config.include StubGitlabCalls config.include StubGitlabData config.include BenchmarkMatchers, benchmark: true -- cgit v1.2.1 From 113325a9e02b06d9cef82b913e2ab6f33331371b Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 30 Nov 2015 13:43:09 +0100 Subject: Retarget release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fb15225eedf..53380d3f6e9 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.3.0 (unreleased) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) + - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) v 8.2.1 - Forcefully update builds that didn't want to update with state machine @@ -56,7 +57,6 @@ v 8.2.0 - Show specific runners from projects where user is master or owner - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) - Make color of "Accept Merge Request" button consistent with current build status - Add ignore white space option in merge request diff and commit and compare view - Ability to add release notes (markdown text and attachments) to git tags (aka Releases) -- cgit v1.2.1 From e92ceb7b57139e985674a44cfe75534c52ed4acd Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 16:12:31 +0200 Subject: fix specs --- Gemfile.lock | 4 +- app/controllers/abuse_reports_controller.rb | 2 +- app/models/project_services/ci/mail_service.rb | 6 +-- app/views/ci/admin/runners/show.html.haml | 2 +- app/views/ci/notify/build_success_email.html.haml | 2 +- app/views/projects/edit.html.haml | 2 +- app/views/projects/runners/edit.html.haml | 2 +- app/workers/email_receiver_worker.rb | 2 +- lib/gitlab/github_import/client.rb | 2 +- lib/gitlab/gitlab_import/client.rb | 2 +- spec/controllers/abuse_reports_controller_spec.rb | 36 +++++++------ spec/features/admin/admin_users_spec.rb | 2 +- spec/helpers/application_helper_spec.rb | 2 +- .../gitlab/markdown/label_reference_filter_spec.rb | 6 +-- .../ci/project_services/mail_service_spec.rb | 62 +++++++++------------- spec/models/project_services/jira_service_spec.rb | 6 +-- spec/workers/email_receiver_worker_spec.rb | 14 ++--- 17 files changed, 73 insertions(+), 81 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 08001379910..f06c4a4165f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -332,7 +332,7 @@ GEM github-markup (~> 1.3.3) gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.7.4) + rouge (~> 1.10.1) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (6.0.1) @@ -610,7 +610,7 @@ GEM netrc (~> 0.7) rinku (1.7.3) rotp (2.1.1) - rouge (1.7.7) + rouge (1.10.1) rqrcode (0.7.0) chunky_png rqrcode-rails3 (0.1.7) diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index d8e90594332..20bc5173f1d 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController if @abuse_report.save if current_application_settings.admin_notification_email.present? - AbuseReportMailer.deliver_later.notify(@abuse_report.id) + AbuseReportMailer.notify(@abuse_report.id).deliver_later end message = "Thank you for your report. A GitLab administrator will look into it shortly." diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb index bdc85667e9d..bb961d06972 100644 --- a/app/models/project_services/ci/mail_service.rb +++ b/app/models/project_services/ci/mail_service.rb @@ -64,9 +64,9 @@ module Ci build.project_recipients.each do |recipient| case build.status.to_sym when :success - mailer.build_success_email(build.id, recipient) + mailer.build_success_email(build.id, recipient).deliver_later when :failed - mailer.build_fail_email(build.id, recipient) + mailer.build_fail_email(build.id, recipient).deliver_later end end end @@ -78,7 +78,7 @@ module Ci end def mailer - Ci::Notify.deliver_later + Ci::Notify end end end diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml index 1498db46a80..fd3d33d657b 100644 --- a/app/views/ci/admin/runners/show.html.haml +++ b/app/views/ci/admin/runners/show.html.haml @@ -37,7 +37,7 @@ = label_tag :tag_list, class: 'control-label' do Tags .col-sm-10 - = f.text_field :tag_list, class: 'form-control' + = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup builds to only use runners with specific tags .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml index 24c439e50eb..6ef1fd67d89 100644 --- a/app/views/ci/notify/build_success_email.html.haml +++ b/app/views/ci/notify/build_success_email.html.haml @@ -8,7 +8,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 3ebc175648e..0c10de1604c 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -35,7 +35,7 @@ .form-group = f.label :tag_list, "Tags", class: 'control-label' .col-sm-10 - = f.text_field :tag_list, maxlength: 2000, class: "form-control" + = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" %p.help-block Separate tags with commas. %fieldset.features diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index dde9e448cb9..a0324701690 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -23,7 +23,7 @@ = label_tag :tag_list, class: 'control-label' do Tags .col-sm-10 - = f.text_field :tag_list, class: 'form-control' + = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup jobs to only use runners with specific tags .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb index 1df8de1db79..f2649e38eb3 100644 --- a/app/workers/email_receiver_worker.rb +++ b/app/workers/email_receiver_worker.rb @@ -46,6 +46,6 @@ class EmailReceiverWorker return end - EmailRejectionMailer.deliver_later.rejection(reason, raw, can_retry) + EmailRejectionMailer.rejection(reason, raw, can_retry).deliver_later end end diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index 270cbcd9ccd..74d1529e1ff 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -46,7 +46,7 @@ module Gitlab end def github_options - OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys + OmniAuth::Strategies::GitHub.default_options[:client_options].to_h.symbolize_keys end end end diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb index 9c00896c913..86fb6c51765 100644 --- a/lib/gitlab/gitlab_import/client.rb +++ b/lib/gitlab/gitlab_import/client.rb @@ -75,7 +75,7 @@ module Gitlab end def gitlab_options - OmniAuth::Strategies::GitLab.default_options[:client_options].symbolize_keys + OmniAuth::Strategies::GitLab.default_options[:client_options].to_h.symbolize_keys end end end diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb index 0faab8d7ff0..15824a1c67f 100644 --- a/spec/controllers/abuse_reports_controller_spec.rb +++ b/spec/controllers/abuse_reports_controller_spec.rb @@ -18,27 +18,31 @@ describe AbuseReportsController do end it "sends a notification email" do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - - email = ActionMailer::Base.deliveries.last - - expect(email.to).to eq([admin_email]) - expect(email.subject).to include(user.username) - expect(email.text_part.body).to include(message) - end - - it "saves the abuse report" do - expect do + perform_enqueued_jobs do post :create, abuse_report: { user_id: user.id, message: message } - end.to change { AbuseReport.count }.by(1) + + email = ActionMailer::Base.deliveries.last + + expect(email.to).to eq([admin_email]) + expect(email.subject).to include(user.username) + expect(email.text_part.body).to include(message) + end + end + + it "saves the abuse report" do + perform_enqueued_jobs do + expect do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + end.to change { AbuseReport.count }.by(1) + end end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 4c756a8e732..c6b6ac58acb 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -87,7 +87,7 @@ describe "Admin::Users", feature: true do end it "should call send mail" do - expect(Notify).to receive(:new_user_email) + expect_any_instance_of(NotificationService).to receive(:new_user) click_button "Create user" end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 1dfae0fbd3f..4b8000ecc44 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -59,7 +59,7 @@ describe ApplicationHelper do avatar_url = "http://localhost/uploads/project/avatar/#{project.id}/banana_sample.gif" expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s). - to eq "\"Banana" + to eq "\"Banana" end it 'should give uploaded icon when present' do diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index fc21b65a843..ae286c8be2b 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -71,7 +71,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do @@ -94,7 +94,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm' end @@ -118,7 +118,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_url(project.namespace, project, label_name: label.name) + namespace_project_issues_path(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm references' end diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index d9b3d34ff15..c03be3ef75f 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -44,13 +44,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end @@ -67,13 +64,10 @@ describe Ci::MailService do end it do - should_email("git@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) + expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) + end end end @@ -95,14 +89,12 @@ describe Ci::MailService do end it do - should_email("git@example.com") - should_email("jeroen@example.com") - mail.execute(build) - end - - def should_email(email) - expect(Ci::Notify).to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2) + expect( + ActionMailer::Base.deliveries.map(&:to).flatten + ).to include("git@example.com", "jeroen@example.com") + end end end @@ -124,14 +116,11 @@ describe Ci::MailService do end it do - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end @@ -177,14 +166,11 @@ describe Ci::MailService do it do Ci::Build.retry(build) - should_email(commit.git_author_email) - should_email("jeroen@example.com") - mail.execute(build) if mail.can_execute?(build) - end - - def should_email(email) - expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email) - expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email) + perform_enqueued_jobs do + expect do + mail.execute(build) if mail.can_execute?(build) + end.to_not change{ ActionMailer::Base.deliveries.size } + end end end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index ddd2cce212c..576f5fc79eb 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -94,9 +94,9 @@ describe JiraService do end it 'should be prepopulated with the settings' do - expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a') - expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id") - expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new") + expect(@service.properties["project_url"]).to eq('http://jira.sample/projects/project_a') + expect(@service.properties["issues_url"]).to eq("http://jira.sample/issues/:id") + expect(@service.properties["new_issue_url"]).to eq("http://jira.sample/projects/project_a/issues/new") end end end diff --git a/spec/workers/email_receiver_worker_spec.rb b/spec/workers/email_receiver_worker_spec.rb index 65a8d7d9197..de40a6f78af 100644 --- a/spec/workers/email_receiver_worker_spec.rb +++ b/spec/workers/email_receiver_worker_spec.rb @@ -21,12 +21,14 @@ describe EmailReceiverWorker do end it "sends out a rejection email" do - described_class.new.perform(raw_message) - - email = ActionMailer::Base.deliveries.last - expect(email).not_to be_nil - expect(email.to).to eq(["jake@adventuretime.ooo"]) - expect(email.subject).to include("Rejected") + perform_enqueued_jobs do + described_class.new.perform(raw_message) + + email = ActionMailer::Base.deliveries.last + expect(email).not_to be_nil + expect(email.to).to eq(["jake@adventuretime.ooo"]) + expect(email.subject).to include("Rejected") + end end end end -- cgit v1.2.1 From f1504e1ad52df7aec241b93d0d015da13ddce5a9 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 30 Nov 2015 18:03:07 +0200 Subject: test fix --- app/models/project_services/gitlab_ci_service.rb | 2 +- spec/features/admin/admin_users_spec.rb | 5 ++++- spec/services/issues/close_service_spec.rb | 4 +++- spec/services/issues/update_service_spec.rb | 5 ++++- spec/services/merge_requests/close_service_spec.rb | 4 +++- spec/services/merge_requests/merge_service_spec.rb | 5 +++-- spec/services/merge_requests/reopen_service_spec.rb | 4 +++- spec/services/merge_requests/update_service_spec.rb | 6 ++++-- 8 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index c5657b5070e..234e8e8b580 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -55,7 +55,7 @@ class GitlabCiService < CiService end def get_ci_commit(sha, ref) - Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha) + Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha) end def commit_status(sha, ref) diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index c6b6ac58acb..86f01faffb4 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -93,7 +93,10 @@ describe "Admin::Users", feature: true do end it "should send valid email to user with email & password" do - click_button "Create user" + perform_enqueued_jobs do + click_button "Create user" + end + user = User.find_by(username: 'bang') email = ActionMailer::Base.deliveries.last expect(email.subject).to have_content('Account was created') diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index db547ce0d50..d711e3da104 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -14,7 +14,9 @@ describe Issues::CloseService do describe :execute do context "valid params" do before do - @issue = Issues::CloseService.new(project, user, {}).execute(issue) + perform_enqueued_jobs do + @issue = Issues::CloseService.new(project, user, {}).execute(issue) + end end it { expect(@issue).to be_valid } diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index cc3e6483261..73d0b7f7abe 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -36,7 +36,10 @@ describe Issues::UpdateService do label_ids: [label.id] } - @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + perform_enqueued_jobs do + @issue = Issues::UpdateService.new(project, user, opts).execute(issue) + end + @issue.reload end diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index b3cbfd4b5b8..272f3897938 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -18,7 +18,9 @@ describe MergeRequests::CloseService do before do allow(service).to receive(:execute_hooks) - @merge_request = service.execute(merge_request) + perform_enqueued_jobs do + @merge_request = service.execute(merge_request) + end end it { expect(@merge_request).to be_valid } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 7483f51de03..c0961ceb11e 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -17,8 +17,9 @@ describe MergeRequests::MergeService do before do allow(service).to receive(:execute_hooks) - - service.execute(merge_request, 'Awesome message') + perform_enqueued_jobs do + service.execute(merge_request, 'Awesome message') + end end it { expect(merge_request).to be_valid } diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index 9401bc3b558..05146bf43f4 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -19,7 +19,9 @@ describe MergeRequests::ReopenService do allow(service).to receive(:execute_hooks) merge_request.state = :closed - service.execute(merge_request) + perform_enqueued_jobs do + service.execute(merge_request) + end end it { expect(merge_request).to be_valid } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index 97f5c009aec..d899b1f01d1 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -42,8 +42,10 @@ describe MergeRequests::UpdateService do before do allow(service).to receive(:execute_hooks) - @merge_request = service.execute(merge_request) - @merge_request.reload + perform_enqueued_jobs do + @merge_request = service.execute(merge_request) + @merge_request.reload + end end it { expect(@merge_request).to be_valid } -- cgit v1.2.1 From ae18ba16327abd79cf6207b83209d4ef96d3e158 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 24 Nov 2015 13:35:07 +0200 Subject: Fire update hook from GitLab --- CHANGELOG | 1 + app/models/repository.rb | 10 +++++++--- lib/gitlab/git/hook.rb | 17 +++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..4a18ac821f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Fire update hook from GitLab v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/models/repository.rb b/app/models/repository.rb index c1836103463..d247b0f5012 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -571,9 +571,13 @@ class Repository # Run GitLab pre-receive hook pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) - status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) + pre_receive_hook_status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) - if status + # Run GitLab update hook + update_hook = Gitlab::Git::Hook.new('update', path_to_repo) + update_hook_status = update_hook.trigger(gl_id, oldrev, newrev, ref) + + if pre_receive_hook_status && update_hook_status if was_empty # Create branch rugged.references.create(ref, newrev) @@ -596,7 +600,7 @@ class Repository # Remove tmp ref and return error to user rugged.references.delete(tmp_ref) - raise PreReceiveError.new('Commit was rejected by pre-receive hook') + raise PreReceiveError.new('Commit was rejected by git hook') end end diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb index dd393fe09d2..07b856ca64c 100644 --- a/lib/gitlab/git/hook.rb +++ b/lib/gitlab/git/hook.rb @@ -16,6 +16,17 @@ module Gitlab def trigger(gl_id, oldrev, newrev, ref) return true unless exists? + case name + when "pre-receive", "post-receive" + call_receive_hook(gl_id, oldrev, newrev, ref) + when "update" + call_update_hook(gl_id, oldrev, newrev, ref) + end + end + + private + + def call_receive_hook(gl_id, oldrev, newrev, ref) changes = [oldrev, newrev, ref].join(" ") # function will return true if succesful @@ -54,6 +65,12 @@ module Gitlab exit_status end + + def call_update_hook(gl_id, oldrev, newrev, ref) + Dir.chdir(repo_path) do + system({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev) + end + end end end end -- cgit v1.2.1 From ceeb93fa77783a3fa9a60529195cd187af191bba Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 30 Nov 2015 12:02:44 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 259a81f42da..1e8fb670325 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,14 +1,17 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - - Ensure cached application settings are refreshed at startup (Stan Hu) - - Fix 404 in redirection after removing a project (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) +v 8.2.2 + - Fix 404 in redirection after removing a project (Stan Hu) + - Ensure cached application settings are refreshed at startup (Stan Hu) + - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) + - Fix: Raw private snippets access workflow + - Prevent "413 Request entity too large" errors when pushing large files with LFS + v 8.2.1 - Forcefully update builds that didn't want to update with state machine - Fix: saving GitLabCiService as Admin Template -- cgit v1.2.1 From 7ba00b50827d8d30ea4c54acb2623d6fb387dcf1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 30 Nov 2015 19:04:44 +0100 Subject: Use gitlab-shell 2.6.8 in update guide --- doc/update/8.1-to-8.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index b57e999cfb7..7b228d6a22f 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -68,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.6.7 +sudo -u git -H git checkout v2.6.8 ``` ### 5. Replace gitlab-git-http-server with gitlab-workhorse -- cgit v1.2.1 From a9642ba046db9aa4490d3361b32cdba48b3fbb9c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 30 Nov 2015 19:05:28 +0100 Subject: Install gitlab-shell 2.6.8 --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 3f5c03a890a..61647780607 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.7] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.8] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: -- cgit v1.2.1 From 8f43298d967d715628b9b17d357cb9169a3606bf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:09:36 +0100 Subject: Show local issues and MRs in "This X closes... / closed by..." sentence --- app/helpers/issues_helper.rb | 6 +++++- app/helpers/merge_requests_helper.rb | 6 +++++- app/models/merge_request.rb | 2 +- app/views/projects/issues/_closed_by_box.html.haml | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..b45d4e1ea8e 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -84,7 +84,11 @@ module IssuesHelper end def merge_requests_sentence(merge_requests) - merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ') + # Sorting based on the `!123` or `group/project!123` reference will sort + # local merge requests first. + merge_requests.map do |merge_request| + merge_request.to_reference(@project) + end.sort.to_sentence(last_word_connector: ', or ') end def url_to_emoji(name) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index b804d4f4e3b..a6010721dff 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -39,7 +39,11 @@ module MergeRequestsHelper end def issues_sentence(issues) - issues.map(&:to_reference).to_sentence + # Sorting based on the `#123` or `group/project#123` reference will sort + # local issues first. + issues.map do |issue| + issue.to_reference(@project) + end.sort.to_sentence end def mr_change_branches_path(merge_request) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1b3d6079d2c..939df8cd8d1 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -316,7 +316,7 @@ class MergeRequest < ActiveRecord::Base issues = commits.flat_map { |c| c.closes_issues(current_user) } issues.push(*Gitlab::ClosingIssueExtractor.new(project, current_user). closed_by_message(description)) - issues.uniq.sort_by(&:id) + issues.uniq else [] end diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml index aef352029d0..917d5181689 100644 --- a/app/views/projects/issues/_closed_by_box.html.haml +++ b/app/views/projects/issues/_closed_by_box.html.haml @@ -1,3 +1,3 @@ .issue-closed-by-widget = icon('check') - This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests.sort))} is accepted. + This issue will be closed automatically when merge request #{gfm(merge_requests_sentence(@closed_by_merge_requests))} is accepted. -- cgit v1.2.1 From a7be01cd07430a4302668224947b2ed135c2d7bb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:10:52 +0100 Subject: Render commit range reference with short shas, link to full shas. --- app/models/commit_range.rb | 70 ++++++++----- .../markdown/commit_range_reference_filter_spec.rb | 7 +- spec/models/commit_range_spec.rb | 111 +++++++++------------ 3 files changed, 94 insertions(+), 94 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 86fc9eb01a3..fd23e24aff6 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -2,12 +2,12 @@ # # Examples: # -# range = CommitRange.new('f3f85602...e86e1013') +# range = CommitRange.new('f3f85602...e86e1013', project) # range.exclude_start? # => false # range.reference_title # => "Commits f3f85602 through e86e1013" # range.to_s # => "f3f85602...e86e1013" # -# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae') +# range = CommitRange.new('f3f856029bc5f966c5a7ee24cf7efefdd20e6019..e86e1013709735be5bb767e2b228930c543f25ae', project) # range.exclude_start? # => true # range.reference_title # => "Commits f3f85602^ through e86e1013" # range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"} @@ -21,7 +21,7 @@ class CommitRange include ActiveModel::Conversion include Referable - attr_reader :sha_from, :notation, :sha_to + attr_reader :commit_from, :notation, :commit_to # Optional Project model attr_accessor :project @@ -53,17 +53,22 @@ class CommitRange # project - An optional Project model. # # Raises ArgumentError if `range_string` does not match `PATTERN`. - def initialize(range_string, project = nil) + def initialize(range_string, project) + @project = project + range_string.strip! unless range_string.match(/\A#{PATTERN}\z/) raise ArgumentError, "invalid CommitRange string format: #{range_string}" end - @exclude_start = !range_string.include?('...') - @sha_from, @notation, @sha_to = range_string.split(/(\.{2,3})/, 2) + ref_from, @notation, ref_to = range_string.split(/(\.{2,3})/, 2) - @project = project + @exclude_start = @notation == '..' + if project.valid_repo? + @commit_from = project.commit(ref_from) + @commit_to = project.commit(ref_to) + end end def inspect @@ -71,15 +76,16 @@ class CommitRange end def to_s - "#{sha_from[0..7]}#{notation}#{sha_to[0..7]}" + sha_from + notation + sha_to end + alias_method :id, :to_s + def to_reference(from_project = nil) - # Not using to_s because we want the full SHAs - reference = sha_from + notation + sha_to + reference = Commit.truncate_sha(sha_from) + notation + Commit.truncate_sha(sha_to) if cross_project_reference?(from_project) - reference = project.to_reference + '@' + reference + reference = project.to_reference + self.class.reference_prefix + reference end reference @@ -87,14 +93,14 @@ class CommitRange # Returns a String for use in a link's title attribute def reference_title - "Commits #{suffixed_sha_from} through #{sha_to}" + "Commits #{sha_start} through #{sha_to}" end # Return a Hash of parameters for passing to a URL helper # # See `namespace_project_compare_url` def to_param - { from: suffixed_sha_from, to: sha_to } + { from: sha_start, to: sha_to } end def exclude_start? @@ -105,28 +111,42 @@ class CommitRange # repository # # project - An optional Project to check (default: `project`) - def valid_commits?(project = project) - return nil unless project.present? - return false unless project.valid_repo? - - commit_from.present? && commit_to.present? + def valid_commits? + commit_start.present? && commit_end.present? end def persisted? true end - def commit_from - @commit_from ||= project.repository.commit(suffixed_sha_from) + def sha_from + return nil unless @commit_from + + @commit_from.id end - def commit_to - @commit_to ||= project.repository.commit(sha_to) + def sha_to + return nil unless @commit_to + + @commit_to.id + end + + def sha_start + return nil unless sha_from + + exclude_start? ? sha_from + '^' : sha_from end - private + def commit_start + return nil unless sha_start - def suffixed_sha_from - sha_from + (exclude_start? ? '^' : '') + if exclude_start? + @commit_start ||= project.commit(sha_start) + else + commit_from + end end + + alias_method :sha_end, :sha_to + alias_method :commit_end, :commit_to end diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index e5b8d723fe5..e078ff26814 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -8,8 +8,8 @@ module Gitlab::Markdown let(:commit1) { project.commit } let(:commit2) { project.commit("HEAD~2") } - let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}") } - let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}") } + let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}", project) } + let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}", project) } it 'requires project context' do expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) @@ -53,7 +53,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("See (#{reference}.)") - exp = Regexp.escape(range.to_s) + exp = Regexp.escape(range.to_reference) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -62,6 +62,7 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(commit1.id.reverse) + expect(project.repository).to receive(:commit).with(commit2.id) expect(filter(act).to_html).to eq exp end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 1031af097bd..58283f06972 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -7,50 +7,56 @@ describe CommitRange do it { is_expected.to include_module(Referable) } end - let(:sha_from) { 'f3f85602' } - let(:sha_to) { 'e86e1013' } + let!(:project) { create(:project, :public) } + let!(:commit1) { project.commit("HEAD~2") } + let!(:commit2) { project.commit } - let(:range) { described_class.new("#{sha_from}...#{sha_to}") } - let(:range2) { described_class.new("#{sha_from}..#{sha_to}") } + let(:sha_from) { commit1.short_id } + let(:sha_to) { commit2.short_id } + + let(:full_sha_from) { commit1.id } + let(:full_sha_to) { commit2.id } + + let(:range) { described_class.new("#{sha_from}...#{sha_to}", project) } + let(:range2) { described_class.new("#{sha_from}..#{sha_to}", project) } it 'raises ArgumentError when given an invalid range string' do - expect { described_class.new("Foo") }.to raise_error(ArgumentError) + expect { described_class.new("Foo", project) }.to raise_error(ArgumentError) end describe '#to_s' do it 'is correct for three-dot syntax' do - expect(range.to_s).to eq "#{sha_from[0..7]}...#{sha_to[0..7]}" + expect(range.to_s).to eq "#{full_sha_from}...#{full_sha_to}" end it 'is correct for two-dot syntax' do - expect(range2.to_s).to eq "#{sha_from[0..7]}..#{sha_to[0..7]}" + expect(range2.to_s).to eq "#{full_sha_from}..#{full_sha_to}" end end describe '#to_reference' do - let(:project) { double('project', to_reference: 'namespace1/project') } + let(:cross) { create(:project) } - before do - range.project = project + it 'returns a String reference to the object' do + expect(range.to_reference).to eq "#{sha_from}...#{sha_to}" end it 'returns a String reference to the object' do - expect(range.to_reference).to eq range.to_s + expect(range2.to_reference).to eq "#{sha_from}..#{sha_to}" end it 'supports a cross-project reference' do - cross = double('project') - expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{range.to_s}" + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" end end describe '#reference_title' do it 'returns the correct String for three-dot ranges' do - expect(range.reference_title).to eq "Commits #{sha_from} through #{sha_to}" + expect(range.reference_title).to eq "Commits #{full_sha_from} through #{full_sha_to}" end it 'returns the correct String for two-dot ranges' do - expect(range2.reference_title).to eq "Commits #{sha_from}^ through #{sha_to}" + expect(range2.reference_title).to eq "Commits #{full_sha_from}^ through #{full_sha_to}" end end @@ -60,11 +66,11 @@ describe CommitRange do end it 'includes the correct values for a three-dot range' do - expect(range.to_param).to eq({ from: sha_from, to: sha_to }) + expect(range.to_param).to eq({ from: full_sha_from, to: full_sha_to }) end it 'includes the correct values for a two-dot range' do - expect(range2.to_param).to eq({ from: sha_from + '^', to: sha_to }) + expect(range2.to_param).to eq({ from: full_sha_from + '^', to: full_sha_to }) end end @@ -79,64 +85,37 @@ describe CommitRange do end describe '#valid_commits?' do - context 'without a project' do - it 'returns nil' do - expect(range.valid_commits?).to be_nil + context 'with a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(true) end - end - it 'accepts an optional project argument' do - project1 = double('project1').as_null_object - project2 = double('project2').as_null_object + it 'is false when `sha_from` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_return(nil) + expect(project).to receive(:commit).with(sha_to).and_call_original - # project1 gets assigned through the accessor, but ignored when not given - # as an argument to `valid_commits?` - expect(project1).not_to receive(:present?) - range.project = project1 + expect(range).not_to be_valid_commits + end - # project2 gets passed to `valid_commits?` - expect(project2).to receive(:present?).and_return(false) + it 'is false when `sha_to` is invalid' do + expect(project).to receive(:commit).with(sha_from).and_call_original + expect(project).to receive(:commit).with(sha_to).and_return(nil) - range.valid_commits?(project2) - end + expect(range).not_to be_valid_commits + end - context 'with a project' do - let(:project) { double('project', repository: double('repository')) } - - context 'with a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(true) - range.project = project - end - - it 'is false when `sha_from` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(false) - expect(project.repository).not_to receive(:commit).with(sha_to) - expect(range).not_to be_valid_commits - end - - it 'is false when `sha_to` is invalid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(false) - expect(range).not_to be_valid_commits - end - - it 'is true when both `sha_from` and `sha_to` are valid' do - expect(project.repository).to receive(:commit).with(sha_from).and_return(true) - expect(project.repository).to receive(:commit).with(sha_to).and_return(true) - expect(range).to be_valid_commits - end + it 'is true when both `sha_from` and `sha_to` are valid' do + expect(range).to be_valid_commits end + end - context 'without a valid repo' do - before do - expect(project).to receive(:valid_repo?).and_return(false) - range.project = project - end + context 'without a valid repo' do + before do + expect(project).to receive(:valid_repo?).and_return(false) + end - it 'returns false' do - expect(range).not_to be_valid_commits - end + it 'returns false' do + expect(range).not_to be_valid_commits end end end -- cgit v1.2.1 From d6a5b45c8ea5ec7a68e213636fde405c52bb90e4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:14:46 +0100 Subject: Recognize issue/MR/snippet/commit links as references. --- app/models/commit.rb | 15 +++-- app/models/commit_range.rb | 11 +++- app/models/concerns/referable.rb | 19 ++++++ app/models/issue.rb | 11 +++- app/models/merge_request.rb | 11 +++- app/models/snippet.rb | 11 +++- lib/gitlab/markdown.rb | 5 +- lib/gitlab/markdown/abstract_reference_filter.rb | 31 +++++++--- .../markdown/commit_range_reference_filter.rb | 70 ++++++---------------- lib/gitlab/markdown/commit_reference_filter.rb | 68 +++++---------------- lib/gitlab/markdown/external_link_filter.rb | 3 + .../markdown/merge_request_reference_filter.rb | 10 ++++ lib/gitlab/markdown/redactor_filter.rb | 5 +- .../markdown/commit_range_reference_filter_spec.rb | 39 +++++++++++- .../markdown/commit_reference_filter_spec.rb | 31 ++++++++++ .../gitlab/markdown/issue_reference_filter_spec.rb | 32 ++++++++++ .../merge_request_reference_filter_spec.rb | 29 ++++++++- .../markdown/snippet_reference_filter_spec.rb | 30 ++++++++++ 18 files changed, 301 insertions(+), 130 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 492f6be1ce3..fc03d2580d7 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -73,16 +73,23 @@ class Commit # This pattern supports cross-project references. def self.reference_pattern %r{ - (?:#{Project.reference_pattern}#{reference_prefix})? - (?\h{6,40}) + #{link_reference_pattern} | + (?: + (?:#{Project.reference_pattern}#{reference_prefix})? + (?\h{6,40}) + ) }x end + def self.link_reference_pattern + super("commit", /(?\h{6,40})/) + end + def to_reference(from_project = nil) if cross_project_reference?(from_project) - "#{project.to_reference}@#{id}" + project.to_reference + self.class.reference_prefix + self.short_id else - id + self.short_id end end diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index fd23e24aff6..98067771b71 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -42,11 +42,18 @@ class CommitRange # This pattern supports cross-project references. def self.reference_pattern %r{ - (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{PATTERN}) + #{link_reference_pattern} | + (?: + (?:#{Project.reference_pattern}#{reference_prefix})? + (?#{PATTERN}) + ) }x end + def self.link_reference_pattern + super("compare", /(?#{PATTERN})/) + end + # Initialize a CommitRange # # range_string - The String commit range. diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index cced66cc1e4..16e4d054869 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -44,6 +44,25 @@ module Referable def reference_pattern raise NotImplementedError, "#{self} does not implement #{__method__}" end + + def link_reference_pattern(route, pattern) + %r{ + (? + #{Regexp.escape(Gitlab.config.gitlab.url)} + \/#{Project.reference_pattern} + \/#{Regexp.escape(route)} + \/#{pattern} + (? + (\/[a-z0-9_=-]+)* + )? + (? + \?[a-z0-9_=-]+ + (&[a-z0-9_=-]+)* + )? + (?\#[a-z0-9_-]+)? + ) + }x + end end private diff --git a/app/models/issue.rb b/app/models/issue.rb index 72183108033..e62acfdfd91 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -64,11 +64,18 @@ class Issue < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("issues", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 939df8cd8d1..c1d3874adee 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -146,11 +146,18 @@ class MergeRequest < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("merge_requests", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{iid}" diff --git a/app/models/snippet.rb b/app/models/snippet.rb index b0831982aa7..8ec12ddf6ef 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -60,11 +60,18 @@ class Snippet < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) + #{link_reference_pattern} | + (?: + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) + ) }x end + def self.link_reference_pattern + super("snippets", /(?\d+)/) + end + def to_reference(from_project = nil) reference = "#{self.class.reference_prefix}#{id}" diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b082bfc434b..ee458eda025 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -181,8 +181,6 @@ module Gitlab Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::IssueReferenceFilter, @@ -193,6 +191,9 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, + Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index fd5b7eb9332..4adc44361b7 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -40,7 +40,7 @@ module Gitlab # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(object_class.reference_pattern) do |match| - yield match, $~[object_sym].to_i, $~[:project] + yield match, $~[object_sym].to_i, $~[:project], $~ end end @@ -74,26 +74,41 @@ module Gitlab # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. def object_link_filter(text) - references_in(text) do |match, id, project_ref| + references_in(text) do |match, id, project_ref, matches| project = project_from_ref(project_ref) if project && object = find_object(project, id) - title = escape_once("#{object_title}: #{object.title}") + title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - data = data_attribute(project: project.id, object_sym => object.id) - url = url_for_object(object, project) + data = data_attribute(project: project.id, object_sym => object.id, original: match) + url = matches[:url] || url_for_object(object, project) + + text = object.to_reference(context[:project]) + + extras = object_link_text_extras(object, matches) + text += " (#{extras.join(", ")})" if extras.any? %(#{match}) + class="#{klass}">#{text}) else match end end end - def object_title - object_class.name.titleize + def object_link_text_extras(object, matches) + extras = [] + + if matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ + extras << "comment #{$1}" + end + + extras + end + + def object_link_title(object) + "#{object_class.name.titleize}: #{object.title}" end end end diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index e070edae0a4..f24bed76193 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -5,24 +5,14 @@ module Gitlab # HTML filter that replaces commit range references with links. # # This filter supports cross-project references. - class CommitRangeReferenceFilter < ReferenceFilter - include CrossProjectReference + class CommitRangeReferenceFilter < AbstractReferenceFilter + def self.object_class + CommitRange + end - # Public: Find commit range references in text - # - # CommitRangeReferenceFilter.references_in(text) do |match, commit_range, project_ref| - # "#{commit_range}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit range, and an optional String - # of the external project reference. - # - # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(CommitRange.reference_pattern) do |match| - yield match, $~[:commit_range], $~[:project] + yield match, $~[:commit_range], $~[:project], $~ end end @@ -31,9 +21,9 @@ module Gitlab return unless project id = node.attr("data-commit-range") - range = CommitRange.new(id, project) + range = find_object(project, id) - return unless range.valid_commits? + return unless range { commit_range: range } end @@ -44,49 +34,25 @@ module Gitlab @commit_map = {} end - def call - replace_text_nodes_matching(CommitRange.reference_pattern) do |content| - commit_range_link_filter(content) - end - end - - # Replace commit range references in text with links to compare the commit - # ranges. - # - # text - String text to replace references in. - # - # Returns a String with commit range references replaced with links. All - # links have `gfm` and `gfm-commit_range` class names attached for - # styling. - def commit_range_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - range = CommitRange.new(id, project) - - if range.valid_commits? - url = url_for_commit_range(project, range) - - title = range.reference_title - klass = reference_class(:commit_range) - data = data_attribute(project: project.id, commit_range: id) + def self.find_object(project, id) + range = CommitRange.new(id, project) - project_ref += '@' if project_ref + range.valid_commits? ? range : nil + end - %(#{project_ref}#{range}) - else - match - end - end + def find_object(*args) + self.class.find_object(*args) end - def url_for_commit_range(project, range) + def url_for_object(range, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_compare_url(project.namespace, project, range.to_param.merge(only_path: context[:only_path])) end + + def object_link_title(range) + range.reference_title + end end end end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index 8cdbeb1f9cf..cc7abc08c87 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -5,24 +5,14 @@ module Gitlab # HTML filter that replaces commit references with links. # # This filter supports cross-project references. - class CommitReferenceFilter < ReferenceFilter - include CrossProjectReference + class CommitReferenceFilter < AbstractReferenceFilter + def self.object_class + Commit + end - # Public: Find commit references in text - # - # CommitReferenceFilter.references_in(text) do |match, commit, project_ref| - # "#{commit}" - # end - # - # text - String text to search. - # - # Yields the String match, the String commit identifier, and an optional - # String of the external project reference. - # - # Returns a String replaced with the return of the block. def self.references_in(text) text.gsub(Commit.reference_pattern) do |match| - yield match, $~[:commit], $~[:project] + yield match, $~[:commit], $~[:project], $~ end end @@ -31,58 +21,32 @@ module Gitlab return unless project id = node.attr("data-commit") - commit = commit_from_ref(project, id) + commit = find_object(project, id) return unless commit { commit: commit } end - def call - replace_text_nodes_matching(Commit.reference_pattern) do |content| - commit_link_filter(content) - end - end - - # Replace commit references in text with links to the commit specified. - # - # text - String text to replace references in. - # - # Returns a String with commit references replaced with links. All links - # have `gfm` and `gfm-commit` class names attached for styling. - def commit_link_filter(text) - self.class.references_in(text) do |match, id, project_ref| - project = self.project_from_ref(project_ref) - - if commit = self.class.commit_from_ref(project, id) - url = url_for_commit(project, commit) - - title = escape_once(commit.link_title) - klass = reference_class(:commit) - data = data_attribute(project: project.id, commit: id) - - project_ref += '@' if project_ref - - %(#{project_ref}#{commit.short_id}) - else - match - end - end - end - - def self.commit_from_ref(project, id) + def self.find_object(project, id) if project && project.valid_repo? project.commit(id) end end - def url_for_commit(project, commit) + def find_object(*args) + self.class.find_object(*args) + end + + def url_for_object(commit, project) h = Gitlab::Application.routes.url_helpers h.namespace_project_commit_url(project.namespace, project, commit, only_path: context[:only_path]) end + + def object_link_title(commit) + commit.link_title + end end end end diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb index 29e51b6ade6..b6792932016 100644 --- a/lib/gitlab/markdown/external_link_filter.rb +++ b/lib/gitlab/markdown/external_link_filter.rb @@ -10,6 +10,9 @@ module Gitlab doc.search('a').each do |node| next unless node.has_attribute?('href') + klass = node.attribute('class') + next if klass && klass.include?('gfm') + link = node.attribute('href').value # Skip non-HTTP(S) links diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 1f47f03c94e..3780a14a130 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -20,6 +20,16 @@ module Gitlab h.namespace_project_merge_request_url(project.namespace, project, mr, only_path: context[:only_path]) end + + def object_link_text_extras(object, matches) + extras = super + + if matches[:path] && matches[:path] == '/diffs' + extras.unshift "diffs" + end + + extras + end end end end diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb index a1f3a8a8ebf..2a58c798f9f 100644 --- a/lib/gitlab/markdown/redactor_filter.rb +++ b/lib/gitlab/markdown/redactor_filter.rb @@ -12,7 +12,10 @@ module Gitlab def call doc.css('a.gfm').each do |node| unless user_can_reference?(node) - node.replace(node.text) + # The reference should be replaced by the original text, + # which is not always the same as the rendered text. + text = node.attribute('data-original') || node.text + node.replace(text) end end diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index e078ff26814..753140d60e6 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -125,7 +125,44 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape("#{project2.to_reference}@#{range.to_s}") + exp = Regexp.escape("#{project2.to_reference}@#{range.to_reference}") + expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" + expect(filter(act).to_html).to eq exp + + exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit_range]).not_to be_empty + end + end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) } + + before do + range.project = project2 + end + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + + exp = Regexp.escape(range.to_reference(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index d080efbf3d4..c1bcf29b1ba 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -131,5 +131,36 @@ module Gitlab::Markdown expect(result[:references][:commit]).not_to be_empty end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:commit) { project2.commit } + let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + + exp = Regexp.escape(project2.to_reference) + expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) + end + + it 'ignores invalid commit IDs on the referenced project' do + exp = act = "Committed #{invalidate_reference(reference)}" + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("See #{reference}") + expect(result[:references][:commit]).not_to be_empty + end + end end end diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 94c80ae6611..296e8868f46 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -135,5 +135,37 @@ module Gitlab::Markdown expect(result[:references][:issue]).to eq [issue] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" } + + it 'ignores valid references when cross-reference project uses external tracker' do + expect_any_instance_of(Project).to receive(:get_issue). + with(issue.iid).and_return(nil) + + exp = act = "Issue #{reference}" + expect(filter(act).to_html).to eq exp + end + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end end end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index 3ef6cdfff33..49d8a6e44da 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -89,7 +89,7 @@ module Gitlab::Markdown context 'cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } - let(:merge) { create(:merge_request, source_project: project2) } + let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } let(:reference) { merge.to_reference(project) } it 'links to a valid reference' do @@ -97,7 +97,7 @@ module Gitlab::Markdown expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_merge_request_url(project2.namespace, - project, merge) + project2, merge) end it 'links with adjacent text' do @@ -116,5 +116,30 @@ module Gitlab::Markdown expect(result[:references][:merge_request]).to eq [merge] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:project, :public, namespace: namespace) } + let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } + let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, + project2, merge) + '/diffs#note_123' } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = filter("Merge (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Merge #{reference}") + expect(result[:references][:merge_request]).to eq [merge] + end + end end end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 9d9652dba46..3080a8a3608 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -114,5 +114,35 @@ module Gitlab::Markdown expect(result[:references][:snippet]).to eq [snippet] end end + + context 'URL cross-project reference' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:snippet) { create(:project_snippet, project: project2) } + let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) } + + it 'links to a valid reference' do + doc = filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) + end + + it 'links with adjacent text' do + doc = filter("See (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/) + end + + it 'ignores invalid snippet IDs on the referenced project' do + exp = act = "See #{invalidate_reference(reference)}" + + expect(filter(act).to_html).to eq exp + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Snippet #{reference}") + expect(result[:references][:snippet]).to eq [snippet] + end + end end end -- cgit v1.2.1 From f9017a2b0384eef3f99ec2f3f1f2ef156afff2b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:15:19 +0100 Subject: Have ClosingIssueExtractor recognize all referenced issues --- config/initializers/1_settings.rb | 2 +- lib/gitlab/closing_issue_extractor.rb | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 62619241001..63d8ae17436 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -164,7 +164,7 @@ Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled']. Settings.gitlab['twitter_sharing_enabled'] ||= true if Settings.gitlab['twitter_sharing_enabled'].nil? Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? -Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil? +Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10 diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index aeec595782c..70b9943d7eb 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -1,6 +1,10 @@ module Gitlab class ClosingIssueExtractor - ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern) + ISSUE_CLOSING_REGEX = begin + pattern = Gitlab.config.gitlab.issue_closing_pattern + pattern = pattern.sub('%{issue_ref}', "(?:#{Issue.reference_pattern})") + Regexp.new(pattern).freeze + end def initialize(project, current_user = nil) @extractor = Gitlab::ReferenceExtractor.new(project, current_user) @@ -9,10 +13,12 @@ module Gitlab def closed_by_message(message) return [] if message.nil? - closing_statements = message.scan(ISSUE_CLOSING_REGEX). - map { |ref| ref[0] }.join(" ") + closing_statements = [] + message.scan(ISSUE_CLOSING_REGEX) do + closing_statements << Regexp.last_match[0] + end - @extractor.analyze(closing_statements) + @extractor.analyze(closing_statements.join(" ")) @extractor.issues end -- cgit v1.2.1 From 4a292aa6042f33d0b63bf95d223ae87bddcea2ce Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:15:34 +0100 Subject: Proper cross-project references when MR is closed by issue --- app/services/system_note_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7e2bc834176..09c159510cd 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -125,7 +125,7 @@ class SystemNoteService # Returns the created Note object def self.change_status(noteable, project, author, status, source) body = "Status changed to #{status}" - body += " by #{source.gfm_reference}" if source + body += " by #{source.gfm_reference(project)}" if source create_note(noteable: noteable, project: project, author: author, note: body) end -- cgit v1.2.1 From bf5e7252ee5ffb5198b7916d1ad8f3436723721a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 21:36:13 +0100 Subject: Recognize commit range with named refs in compare URLs. --- app/models/commit_range.rb | 31 +++++++++++++--------- .../markdown/commit_range_reference_filter_spec.rb | 11 ++++---- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 98067771b71..7b1164b024c 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -22,16 +22,19 @@ class CommitRange include Referable attr_reader :commit_from, :notation, :commit_to + attr_reader :ref_from, :ref_to # Optional Project model attr_accessor :project - # See `exclude_start?` - attr_reader :exclude_start - - # The beginning and ending SHAs can be between 6 and 40 hex characters, and + # The beginning and ending refs can be named or SHAs, and # the range notation can be double- or triple-dot. - PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ + REF_PATTERN = /[0-9a-zA-Z][0-9a-zA-Z_.-]*[0-9a-zA-Z\^]/ + PATTERN = /#{REF_PATTERN}\.{2,3}#{REF_PATTERN}/ + + # In text references, the beginning and ending refs can only be SHAs + # between 6 and 40 hex characters. + STRICT_PATTERN = /\h{6,40}\.{2,3}\h{6,40}/ def self.reference_prefix '@' @@ -45,7 +48,7 @@ class CommitRange #{link_reference_pattern} | (?: (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{PATTERN}) + (?#{STRICT_PATTERN}) ) }x end @@ -69,12 +72,16 @@ class CommitRange raise ArgumentError, "invalid CommitRange string format: #{range_string}" end - ref_from, @notation, ref_to = range_string.split(/(\.{2,3})/, 2) + @ref_from, @notation, @ref_to = range_string.split(/(\.{2,3})/, 2) - @exclude_start = @notation == '..' if project.valid_repo? - @commit_from = project.commit(ref_from) - @commit_to = project.commit(ref_to) + @commit_from = project.commit(@ref_from) + @commit_to = project.commit(@ref_to) + end + + if valid_commits? + @ref_from = Commit.truncate_sha(sha_from) if sha_from.start_with?(@ref_from) + @ref_to = Commit.truncate_sha(sha_to) if sha_to.start_with?(@ref_to) end end @@ -89,7 +96,7 @@ class CommitRange alias_method :id, :to_s def to_reference(from_project = nil) - reference = Commit.truncate_sha(sha_from) + notation + Commit.truncate_sha(sha_to) + reference = ref_from + notation + ref_to if cross_project_reference?(from_project) reference = project.to_reference + self.class.reference_prefix + reference @@ -111,7 +118,7 @@ class CommitRange end def exclude_start? - exclude_start + @notation == '..' end # Check if both the starting and ending commit IDs exist in a project's diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 753140d60e6..4b4c769a110 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -5,8 +5,8 @@ module Gitlab::Markdown include FilterSpecHelper let(:project) { create(:project, :public) } - let(:commit1) { project.commit } - let(:commit2) { project.commit("HEAD~2") } + let(:commit1) { project.commit("HEAD~2") } + let(:commit2) { project.commit } let(:range) { CommitRange.new("#{commit1.id}...#{commit2.id}", project) } let(:range2) { CommitRange.new("#{commit1.id}..#{commit2.id}", project) } @@ -89,7 +89,7 @@ module Gitlab::Markdown link = doc.css('a').first expect(link).to have_attribute('data-commit-range') - expect(link.attr('data-commit-range')).to eq range.to_reference + expect(link.attr('data-commit-range')).to eq range.to_s end it 'supports an :only_path option' do @@ -146,7 +146,8 @@ module Gitlab::Markdown context 'URL cross-project reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } - let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) } + let(:range) { CommitRange.new("#{commit1.id}...master", project) } + let(:reference) { urls.namespace_project_compare_url(project2.namespace, project2, from: commit1.id, to: 'master') } before do range.project = project2 @@ -156,7 +157,7 @@ module Gitlab::Markdown doc = filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) + to eq reference end it 'links with adjacent text' do -- cgit v1.2.1 From 4a0ebccf6b96184888f2d28b6e97592c6cb239c7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 30 Nov 2015 22:43:54 +0100 Subject: Fix specs --- app/models/concerns/mentionable.rb | 11 ++++++++--- .../lib/gitlab/markdown/commit_reference_filter_spec.rb | 1 - .../markdown/merge_request_reference_filter_spec.rb | 3 +-- spec/models/commit_spec.rb | 16 ++++++---------- spec/support/mentionable_shared_examples.rb | 17 +++++++---------- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 193c91f1742..0d6cd86aade 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -62,13 +62,18 @@ module Mentionable return [] if text.blank? refs = all_references(current_user, text, load_lazy_references: load_lazy_references) - (refs.issues + refs.merge_requests + refs.commits) - [local_reference] + refs = (refs.issues + refs.merge_requests + refs.commits) + + # We're using this method instead of Array diffing because that requires + # both of the object's `hash` values to be the same, which may not be the + # case for otherwise identical Commit objects. + refs.reject! { |ref| ref == local_reference } end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. def create_cross_references!(author = self.author, without = [], text = self.mentionable_text) refs = referenced_mentionables(author, text) - + # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. @@ -111,7 +116,7 @@ module Mentionable # Only include changed fields that are mentionable source.select { |key, val| mentionable.include?(key) } end - + # Determine whether or not a cross-reference Note has already been created between this Mentionable and # the specified target. def cross_reference_exists?(target) diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index c1bcf29b1ba..a8c9c7efd56 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -148,7 +148,6 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape(project2.to_reference) expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index 49d8a6e44da..ca3e7151e02 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -121,8 +121,7 @@ module Gitlab::Markdown let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } - let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, - project2, merge) + '/diffs#note_123' } + let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } it 'links to a valid reference' do doc = filter("See #{reference}") diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 90be9324951..1aaa927c216 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -15,12 +15,12 @@ describe Commit do describe '#to_reference' do it 'returns a String reference to the object' do - expect(commit.to_reference).to eq commit.id + expect(commit.to_reference).to eq commit.short_id end it 'supports a cross-project reference' do cross = double('project') - expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}" + expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.short_id}" end end @@ -77,14 +77,10 @@ eos let(:other_issue) { create :issue, project: other_project } it 'detects issues that this commit is marked as closing' do - allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid}") - expect(commit.closes_issues).to eq([issue]) - end - - it 'does not detect issues from other projects' do ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}" - allow(commit).to receive(:safe_message).and_return("Fixes #{ext_ref}") - expect(commit.closes_issues).to be_empty + allow(commit).to receive(:safe_message).and_return("Fixes ##{issue.iid} and #{ext_ref}") + expect(commit.closes_issues).to include(issue) + expect(commit.closes_issues).to include(other_issue) end end @@ -92,7 +88,7 @@ eos subject { create(:project).commit } let(:author) { create(:user, email: subject.author_email) } - let(:backref_text) { "commit #{subject.id}" } + let(:backref_text) { "commit #{subject.short_id}" } let(:set_mentionable_text) do ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } end diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index 3bb568f4d49..33d2b14583c 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -10,12 +10,12 @@ def common_mentionable_setup let(:mentioned_issue) { create(:issue, project: project) } let!(:mentioned_mr) { create(:merge_request, :simple, source_project: project) } - let(:mentioned_commit) { project.commit } + let(:mentioned_commit) { project.commit("HEAD~1") } let(:ext_proj) { create(:project, :public) } let(:ext_issue) { create(:issue, project: ext_proj) } let(:ext_mr) { create(:merge_request, :simple, source_project: ext_proj) } - let(:ext_commit) { ext_proj.commit } + let(:ext_commit) { ext_proj.commit("HEAD~2") } # Override to add known commits to the repository stub. let(:extra_commits) { [] } @@ -45,14 +45,11 @@ def common_mentionable_setup before do # Wire the project's repository to return the mentioned commit, and +nil+ # for any unrecognized commits. - commitmap = { - mentioned_commit.id => mentioned_commit - } - extra_commits.each { |c| commitmap[c.short_id] = c } - - allow(Project).to receive(:find).and_call_original - allow(Project).to receive(:find).with(project.id.to_s).and_return(project) - allow(project.repository).to receive(:commit) { |sha| commitmap[sha] } + allow_any_instance_of(::Repository).to receive(:commit).and_call_original + allow_any_instance_of(::Repository).to receive(:commit).with(mentioned_commit.short_id).and_return(mentioned_commit) + extra_commits.each do |commit| + allow_any_instance_of(::Repository).to receive(:commit).with(commit.short_id).and_return(commit) + end set_mentionable_text.call(ref_string) end -- cgit v1.2.1 From 8c4a3c77d87e89bf3fd237fef49fc87fb6170d86 Mon Sep 17 00:00:00 2001 From: Minsik Yoon Date: Mon, 30 Nov 2015 18:30:44 +0900 Subject: Add ignore whitespace change option to commit view --- CHANGELOG | 1 + app/controllers/projects/commit_controller.rb | 7 ++++++- spec/controllers/commit_controller_spec.rb | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..35bb9951d86 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Add ignore whitespace change option to commit view v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index deefdd76667..3f137440e28 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -67,7 +67,12 @@ class Projects::CommitController < Projects::ApplicationController end def define_show_vars - @diffs = commit.diffs + if params[:w].to_i == 1 + @diffs = commit.diffs({ ignore_whitespace_change: true }) + else + @diffs = commit.diffs + end + @notes_count = commit.notes.count @builds = ci_commit.builds if ci_commit diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index bb3d87f3840..5337a69e84b 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -69,6 +69,21 @@ describe Projects::CommitController do expect(response.body).to start_with("diff --git") end + + it "should really only be a git diff without whitespace changes" do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: '66eceea0db202bb39c4e445e8ca28689645366c5', + # id: commit.id, + format: format, + w: 1) + + expect(response.body).to start_with("diff --git") + # without whitespace option, there are more than 2 diff_splits + diff_splits = assigns(:diffs)[0].diff.split("\n") + expect(diff_splits.length).to be <= 2 + end end describe "as patch" do -- cgit v1.2.1 From 5f6117c0aa391a6e9c96493ca01a8a23eb40f0cd Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Thu, 5 Nov 2015 16:53:05 +0100 Subject: update ci docs with yml reason and better quickstart --- doc/ci/quick_start/README.md | 202 +++++++++++++++++++++++++++++-------------- doc/ci/yaml/README.md | 30 ++++--- 2 files changed, 156 insertions(+), 76 deletions(-) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index d69064a91fd..74626c8f8a4 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -1,44 +1,62 @@ # Quick Start -To start building projects with GitLab CI a few steps needs to be done. +GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You +only need to enable it in the Services settings of your project. -## 1. Install GitLab and CI +This guide assumes that you: -First you need to have a working GitLab and GitLab CI instance. +- have a working GitLab instance of version 8.0 or higher or are using + [GitLab.com](https://gitlab.com/users/sign_in) +- have a project in GitLab that you would like to use CI for -You can omit this step if you use [GitLab.com](https://GitLab.com/). +In brief, the steps needed to have a working CI can be summed up to: -## 2. Create repository on GitLab +1. Create a new project +1. Add `.gitlab-ci.yml` to the git repository and push to GitLab +1. Configure a Runner -Once you login on your GitLab add a new repository where you will store your source code. -Push your application to that repository. +From there on, on every push to your git repository the build will be +automagically started by the Runner and will appear under the project's +`/builds` page. -## 3. Add project to CI +Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -The next part is to login to GitLab CI. -Point your browser to the URL you have set GitLab or use [gitlab.com/ci](https://gitlab.com/ci/). +## 1. Creating a `.gitlab-ci.yml` file -On the first screen you will see a list of GitLab's projects that you have access to: + **GitLab CI** service is enabled automatically on the first push of a + `.gitlab-ci.yml` file in your repository and this is the recommended way. -![Projects](projects.png) +For other methods read [how to enable the GitLab CI service](../enable_ci.md). -Click **Add Project to CI**. -This will create project in CI and authorize GitLab CI to fetch sources from GitLab. +### What is `.gitlab-ci.yml` -> GitLab CI creates unique token that is used to configure GitLab CI service in GitLab. -> This token allows to access GitLab's repository and configures GitLab to trigger GitLab CI webhook on **Push events** and **Tag push events**. -> You can see that token by going to Project's Settings > Services > GitLab CI. -> You will see there token, the same token is assigned in GitLab CI settings of project. +The `.gitlab-ci.yml` file is where you configure what CI does with your project. +It lives in the root of your repository. -## 4. Create project's configuration - .gitlab-ci.yml +On any push to your repository, GitLab will look for the `.gitlab-ci.yml` +file and start builds on _Runners_ according to the contents of the file, +for that commit. -The next: You have to define how your project will be built. -GitLab CI uses [YAML](https://en.wikipedia.org/wiki/YAML) file to store build configuration. -You need to create `.gitlab-ci.yml` in root directory of your repository: +Because `.gitlab-ci.yml` is in the repository, it is version controlled, +old versions still build succesfully, forks can easily make use of CI, +branches can have separate builds and you have a single source of truth for CI. +You can read more about the reasons why we are using `.gitlab-ci.yml` +[in our blog about it][blog-ci]. + +`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file. + +### Creating a simple `.gitlab-ci.yml` file + +You need to create a file named `.gitlab-ci.yml` in the root directory of your +repository. Below is an example for a Ruby on Rails project. ```yaml before_script: - - bundle install + - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs + - ruby -v + - which ruby + - gem install bundler --no-ri --no-rdoc + - bundle install --jobs $(nproc) "${FLAGS[@]}" rspec: script: @@ -49,71 +67,129 @@ rubocop: - bundle exec rubocop ``` -This is the simplest possible build configuration that will work for most Ruby applications: -1. Define two jobs `rspec` and `rubocop` with two different commands to be executed. -1. Before every job execute commands defined by `before_script`. +This is the simplest possible build configuration that will work for most Ruby +applications: + +1. Define two jobs `rspec` and `rubocop` with different commands to be executed. +1. Before every job, the commands defined by `before_script` are executed. + +The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when +they should be run. The jobs are defined as top-level elements with a name and +always have to contain the `script` name. Jobs are used to create builds, +which are then picked by [Runners](../runners/README.md) and executed within +the environment of the Runner. + +What is important is that each job is run independently from each other. -The `.gitlab-ci.yml` defines set of jobs with constrains how and when they should be run. -The jobs are defined as top-level elements with name and always have to contain the `script`. -Jobs are used to create builds, which are then picked by [runners](../runners/README.md) and executed within environment of the runner. -What is important that each job is run independently from each other. +If you want to check whether your `.gitlab-ci.yml` file is valid, there is a +Lint tool under the page `/ci/lint` of your GitLab instance. You can also find +the link under **Settings** -> **CI settings** in your project. -For more information and complete `.gitlab-ci.yml` syntax, please check the [Configuring project (.gitlab-ci.yml)](../yaml/README.md). +For more information and a complete `.gitlab-ci.yml` syntax, please check +[the documentation on .gitlab-ci.yml](../yaml/README.md). -## 5. Add file and push .gitlab-ci.yml to repository +### Push `.gitlab-ci.yml` to GitLab -Once you created `.gitlab-ci.yml` you should add it to git repository and push it to GitLab. +Once you've created `.gitlab-ci.yml`, you should add it to your git repository +and push it to GitLab. ```bash git add .gitlab-ci.yml -git commit +git commit -m "Add .gitlab-ci.yml" git push origin master ``` -If you refresh the project's page on GitLab CI you will notice a one new commit: +Now if you go to the **Builds** page you will see that the builds are pending. + +You can also go to the **Commits** page and notice the little clock icon next +to the commit SHA. + +![New commit pending](img/new_commit.png) + +Clicking on the clock icon you will be directed to the builds page for that +specific commit. + +![Single commit builds page](img/single_commit_status_pending.png) + +Notice that there are two jobs pending which are named after what we wrote in +`.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured +yet for these builds. + +The next step is to configure a Runner so that it picks the pending jobs. + +## 2. Configuring a Runner + +In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. +A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker +container or even a cluster of containers. GitLab and the Runners communicate +through an API, so the only needed requirement is that the machine on which the +Runner is configured to have Internet access. + +A Runner can be specific to a certain project or serve multiple projects in +GitLab. If it serves all projects it's called a _Shared Runner_. + +Find more information about different Runners in the +[Runners](../runners/README.md) documentation. + +You can find whether any Runners are assigned to your project by going to +**Settings** -> **Runners**. + +Setting up a Runner is easy and straightforward. The official Runner supported +by GitLab is written in Go and can be found at +. + +In order to have a functional Runner you need to: + +1. [Install it][runner-install] +2. [Configure it](../runners/README.md#registering-a-specific-runner) + +For other types of unofficial Runners written in other languages, see the +[instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner). + +Once the Runner has been set up, you should see it on the Runners page of your +project, following **Settings** -> **Runners**. -![](new_commit.png) +![Activated runners](img/runners_activated.png) -However the commit has status **pending** which means that commit was not yet picked by runner. +### Shared Runners -## 6. Configure runner +If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners** +provided by GitLab Inc. -In GitLab CI, Runners run your builds. -A runner is a machine (can be virtual, bare-metal or VPS) that picks up builds through the coordinator API of GitLab CI. +These are special virtual machines that are run on GitLab's infrastructure that +can build any project. -A runner can be specific to a certain project or serve any project in GitLab CI. -A runner that serves all projects is called a shared runner. -More information about different runner types can be found in [Configuring runner](../runners/README.md). +To enable **Shared Runners** you have to go to your project's +**Settings** -> **Runners** and click **Enable shared runners**. -To check if you have runners assigned to your project go to **Runners**. You will find there information how to setup project specific runner: +[Read more on Shared Runners](../runners/README.md). -1. Install GitLab Runner software. Checkout the [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner) section to install it. -1. Specify following URL during runner setup: https://gitlab.com/ci/ -1. Use the following registration token during setup: TOKEN +## 3. Seeing the status of your build -If you do it correctly your runner should be shown under **Runners activated for this project**: +After configuring the Runner succesfully, you should see the status of your +last commit change from _pending_ to either _running_, _success_ or _failed_. -![](runners_activated.png) +You can view all builds, by going to the **Builds** page in your project. -### Shared runners +![Commit status](img/builds_status.png) -If you use [gitlab.com/ci](https://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc. -These are special virtual machines that are run on GitLab's infrastructure that can build any project. -To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project. +By clicking on a Build ID, you will be able to see the log of that build. +This is important to diagnose why a build failed or acted differently than +you expected. -## 7. Check status of commit +![Build log](img/build_log.png) -If everything went OK and you go to commit, the status of the commit should change from **pending** to either **running**, **success** or **failed**. +You are also able to view the status of any commit in the various pages in +GitLab, such as **Commits** and **Merge Requests**. -![](commit_status.png) +## Next steps -You can click **Build ID** to view build log for specific job. +Awesome! You started using CI in GitLab! -## 8. Congratulations! +Next you can look into doing more with the CI. Many people are using GitLab +to package, containerize, test and deploy software. -You managed to build your first project using GitLab CI. -You may need to tune your `.gitlab-ci.yml` file to implement build plan for your project. -A few examples how it can be done you can find on [Examples](../examples/README.md) page. +We have a number of [examples](../examples/README.md) available. -GitLab CI also offers **the Lint** tool to verify validity of your `.gitlab-ci.yml` which can be useful to troubleshoot potential problems. -The Lint is available from project's settings or by adding `/lint` to GitLab CI url. +[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation +[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3dbf1afc7a9..9ee26c41e6d 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -20,6 +20,22 @@ Of course a command can execute code directly (`./configure;make;make install`) Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner. What is important, is that each job is run independently from each other. +## Why `.gitlab-ci.yml` + +By placing a single configuration file in the root of your repository, +it is version controlled and you get all the advantages of git. + +In addition, builds for older versions of the repository will work just fine, +as GitLab look at the `.gitlab-ci.yml` of the pushed commit. +This means that forks also build without any problem. + +You can even set up different builds for different branches. This allows you +to only deploy the `production` branch, for instance. + +By having a single source of truth, everyone can view and contribute to the +stability of your CI builds, eventually improving the quality of your development +cycle. + ## .gitlab-ci.yml The YAML syntax allows for using more complex job specifications than in the above example: @@ -185,7 +201,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. +1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -198,18 +214,6 @@ job: - branches # use special keyword ``` -1. `only` and `except` allow for specify repository path to filter jobs for forks. -The repository path can be used to have jobs executed only for parent repository. - -```yaml -job: - only: - - branches@gitlab-org/gitlab-ce - except: - - master@gitlab-org/gitlab-ce -``` -The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . - ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. -- cgit v1.2.1 From 2c30d11e80a4bb4112d01be7e1fbe51a321b3beb Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 5 Nov 2015 22:49:57 +0200 Subject: Clean up intro --- doc/ci/quick_start/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 74626c8f8a4..92dad2be3e8 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,8 +16,8 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the Runner and will appear under the project's -`/builds` page. +automagically started by the runner and will appear under the project's `/builds` +page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -- cgit v1.2.1 From 1058652a920d1106d29452aadfef953937a188e5 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 6 Nov 2015 10:43:28 +0200 Subject: Add section of enabling GitLab CI --- doc/ci/quick_start/README.md | 7 +++++++ doc/ci/quick_start/builds_tab.png | Bin 0 -> 3845 bytes doc/ci/quick_start/ci_service_disabled.png | Bin 0 -> 3598 bytes doc/ci/quick_start/ci_service_enabled.png | Bin 0 -> 3545 bytes doc/ci/quick_start/ci_service_mark_active.png | Bin 0 -> 17193 bytes doc/ci/quick_start/enable_ci.md | 24 ++++++++++++++++++++++++ 6 files changed, 31 insertions(+) create mode 100644 doc/ci/quick_start/builds_tab.png create mode 100644 doc/ci/quick_start/ci_service_disabled.png create mode 100644 doc/ci/quick_start/ci_service_enabled.png create mode 100644 doc/ci/quick_start/ci_service_mark_active.png create mode 100644 doc/ci/quick_start/enable_ci.md diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 92dad2be3e8..d45ad3e5e23 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -21,6 +21,13 @@ page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. +## 1. Enable GitLab CI + +After creating a new project, the first thing to do is enable the **GitLab CI** +service in your project's settings if it isn't already enabled. + +Read [how to enable the GitLab CI service](enable_ci.md). + ## 1. Creating a `.gitlab-ci.yml` file **GitLab CI** service is enabled automatically on the first push of a diff --git a/doc/ci/quick_start/builds_tab.png b/doc/ci/quick_start/builds_tab.png new file mode 100644 index 00000000000..d088b8b329d Binary files /dev/null and b/doc/ci/quick_start/builds_tab.png differ diff --git a/doc/ci/quick_start/ci_service_disabled.png b/doc/ci/quick_start/ci_service_disabled.png new file mode 100644 index 00000000000..351de4267a4 Binary files /dev/null and b/doc/ci/quick_start/ci_service_disabled.png differ diff --git a/doc/ci/quick_start/ci_service_enabled.png b/doc/ci/quick_start/ci_service_enabled.png new file mode 100644 index 00000000000..dfe7488c1ba Binary files /dev/null and b/doc/ci/quick_start/ci_service_enabled.png differ diff --git a/doc/ci/quick_start/ci_service_mark_active.png b/doc/ci/quick_start/ci_service_mark_active.png new file mode 100644 index 00000000000..8bc8694cec3 Binary files /dev/null and b/doc/ci/quick_start/ci_service_mark_active.png differ diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md new file mode 100644 index 00000000000..6f539c752d8 --- /dev/null +++ b/doc/ci/quick_start/enable_ci.md @@ -0,0 +1,24 @@ +# Enable GitLab CI + +GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You +only need to enable it in the **Services** settings of your project. + +First, head over your project's page that you would like to enable CI for. +If you can see the **Builds** tab in the sidebar, then CI is enabled. + +![Builds tab](builds_tab.png) + +If not, go to **Settings > Services** and search for **GitLab CI**. Its state +should be disabled. + +![CI service disabled](ci_service_disabled.png) + +Click on **GitLab CI** to enter its settings, mark it as active and hit +**Save**. + +![Mark CI service as active](ci_service_mark_active.png) + +Do you see that green dot? Then good, the service is now enabled! You can also +check its status under **Services**. + +![CI service enabled](ci_service_enabled.png) -- cgit v1.2.1 From 3b4806bad95e55a40d3efe02e329d271995d8a70 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Sat, 7 Nov 2015 09:11:37 +0200 Subject: Add option to enable GitLab CI via .gitlab-ci.yml --- doc/ci/quick_start/enable_ci.md | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md index 6f539c752d8..42635e35d5d 100644 --- a/doc/ci/quick_start/enable_ci.md +++ b/doc/ci/quick_start/enable_ci.md @@ -1,15 +1,30 @@ # Enable GitLab CI -GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You -only need to enable it in the **Services** settings of your project. +GitLab Continuous Integration (CI) is fully integrated into GitLab itself as +of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). -First, head over your project's page that you would like to enable CI for. -If you can see the **Builds** tab in the sidebar, then CI is enabled. +First, head over your project's page that you would like to enable GitLab CI +for. If you can see the **Builds** tab in the sidebar, then GitLab CI is +already enabled and you can skip reading the rest of this guide. ![Builds tab](builds_tab.png) -If not, go to **Settings > Services** and search for **GitLab CI**. Its state -should be disabled. +If not, there are two ways to enable it in your project. + +## Use .gitlab-ci.yml to enable GitLab CI + +GitLab CI will be automatically enabled if you just push a +[`.gitlab-ci.yml`](../yaml/README.md) in your git repository. GitLab will +pick up the change immediately and GitLab CI will be enabled for this project. +This is the recommended way. + +## Manually enable GitLab CI + +The second way is to manually enable it in the project's **Services** settings +and this is also the way to disable it if needed. + +Go to **Settings > Services** and search for **GitLab CI**. Its state should +be disabled. ![CI service disabled](ci_service_disabled.png) -- cgit v1.2.1 From 32a3c3e068e957716eac56a755f44268c07c5957 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 03:26:51 +0200 Subject: Place images in their own dir --- doc/ci/enable_ci.md | 39 ++++++++++++++++++++++++++ doc/ci/img/builds_tab.png | Bin 0 -> 3845 bytes doc/ci/img/ci_service_disabled.png | Bin 0 -> 3598 bytes doc/ci/img/ci_service_enabled.png | Bin 0 -> 3545 bytes doc/ci/img/ci_service_mark_active.png | Bin 0 -> 17193 bytes doc/ci/quick_start/README.md | 14 ++++----- doc/ci/quick_start/build_status.png | Bin 62140 -> 0 bytes doc/ci/quick_start/builds_tab.png | Bin 3845 -> 0 bytes doc/ci/quick_start/ci_service_disabled.png | Bin 3598 -> 0 bytes doc/ci/quick_start/ci_service_enabled.png | Bin 3545 -> 0 bytes doc/ci/quick_start/ci_service_mark_active.png | Bin 17193 -> 0 bytes doc/ci/quick_start/commit_status.png | Bin 33492 -> 0 bytes doc/ci/quick_start/enable_ci.md | 39 -------------------------- doc/ci/quick_start/img/build_status.png | Bin 0 -> 62140 bytes doc/ci/quick_start/img/commit_status.png | Bin 0 -> 33492 bytes doc/ci/quick_start/img/new_commit.png | Bin 0 -> 47527 bytes doc/ci/quick_start/img/projects.png | Bin 0 -> 37014 bytes doc/ci/quick_start/img/runners.png | Bin 0 -> 123048 bytes doc/ci/quick_start/img/runners_activated.png | Bin 0 -> 60769 bytes doc/ci/quick_start/new_commit.png | Bin 47527 -> 0 bytes doc/ci/quick_start/projects.png | Bin 37014 -> 0 bytes doc/ci/quick_start/runners.png | Bin 123048 -> 0 bytes doc/ci/quick_start/runners_activated.png | Bin 60769 -> 0 bytes 23 files changed, 45 insertions(+), 47 deletions(-) create mode 100644 doc/ci/enable_ci.md create mode 100644 doc/ci/img/builds_tab.png create mode 100644 doc/ci/img/ci_service_disabled.png create mode 100644 doc/ci/img/ci_service_enabled.png create mode 100644 doc/ci/img/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/build_status.png delete mode 100644 doc/ci/quick_start/builds_tab.png delete mode 100644 doc/ci/quick_start/ci_service_disabled.png delete mode 100644 doc/ci/quick_start/ci_service_enabled.png delete mode 100644 doc/ci/quick_start/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/commit_status.png delete mode 100644 doc/ci/quick_start/enable_ci.md create mode 100644 doc/ci/quick_start/img/build_status.png create mode 100644 doc/ci/quick_start/img/commit_status.png create mode 100644 doc/ci/quick_start/img/new_commit.png create mode 100644 doc/ci/quick_start/img/projects.png create mode 100644 doc/ci/quick_start/img/runners.png create mode 100644 doc/ci/quick_start/img/runners_activated.png delete mode 100644 doc/ci/quick_start/new_commit.png delete mode 100644 doc/ci/quick_start/projects.png delete mode 100644 doc/ci/quick_start/runners.png delete mode 100644 doc/ci/quick_start/runners_activated.png diff --git a/doc/ci/enable_ci.md b/doc/ci/enable_ci.md new file mode 100644 index 00000000000..01c684dabec --- /dev/null +++ b/doc/ci/enable_ci.md @@ -0,0 +1,39 @@ +# Enable GitLab CI + +GitLab Continuous Integration (CI) is fully integrated into GitLab itself as +of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). + +First, head over your project's page that you would like to enable GitLab CI +for. If you can see the **Builds** tab in the sidebar, then GitLab CI is +already enabled and you can skip reading the rest of this guide. + +![Builds tab](img/builds_tab.png) + +If not, there are two ways to enable it in your project. + +## Use .gitlab-ci.yml to enable GitLab CI + +GitLab CI will be automatically enabled if you just push a +[`.gitlab-ci.yml`](yaml/README.md) in your git repository. GitLab will +pick up the change immediately and GitLab CI will be enabled for this project. +This is the recommended way. + +## Manually enable GitLab CI + +The second way is to manually enable it in the project's **Services** settings +and this is also the way to disable it if needed. + +Go to **Settings > Services** and search for **GitLab CI**. Its state should +be disabled. + +![CI service disabled](img/ci_service_disabled.png) + +Click on **GitLab CI** to enter its settings, mark it as active and hit +**Save**. + +![Mark CI service as active](img/ci_service_mark_active.png) + +Do you see that green dot? Then good, the service is now enabled! You can also +check its status under **Services**. + +![CI service enabled](img/ci_service_enabled.png) diff --git a/doc/ci/img/builds_tab.png b/doc/ci/img/builds_tab.png new file mode 100644 index 00000000000..d088b8b329d Binary files /dev/null and b/doc/ci/img/builds_tab.png differ diff --git a/doc/ci/img/ci_service_disabled.png b/doc/ci/img/ci_service_disabled.png new file mode 100644 index 00000000000..351de4267a4 Binary files /dev/null and b/doc/ci/img/ci_service_disabled.png differ diff --git a/doc/ci/img/ci_service_enabled.png b/doc/ci/img/ci_service_enabled.png new file mode 100644 index 00000000000..dfe7488c1ba Binary files /dev/null and b/doc/ci/img/ci_service_enabled.png differ diff --git a/doc/ci/img/ci_service_mark_active.png b/doc/ci/img/ci_service_mark_active.png new file mode 100644 index 00000000000..8bc8694cec3 Binary files /dev/null and b/doc/ci/img/ci_service_mark_active.png differ diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index d45ad3e5e23..ce05a6f417c 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,19 +16,17 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the runner and will appear under the project's `/builds` -page. +automagically started by the runner and will appear under the project's +`/builds` page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -## 1. Enable GitLab CI - -After creating a new project, the first thing to do is enable the **GitLab CI** -service in your project's settings if it isn't already enabled. +## 1. Creating a `.gitlab-ci.yml` file -Read [how to enable the GitLab CI service](enable_ci.md). + **GitLab CI** service is enabled automatically on the first push of a + `.gitlab-ci.yml` file in your repository and this is the recommended way. -## 1. Creating a `.gitlab-ci.yml` file +For other methods read [how to enable the GitLab CI service](../enable_ci.md). **GitLab CI** service is enabled automatically on the first push of a `.gitlab-ci.yml` file in your repository and this is the recommended way. diff --git a/doc/ci/quick_start/build_status.png b/doc/ci/quick_start/build_status.png deleted file mode 100644 index 333259e6acd..00000000000 Binary files a/doc/ci/quick_start/build_status.png and /dev/null differ diff --git a/doc/ci/quick_start/builds_tab.png b/doc/ci/quick_start/builds_tab.png deleted file mode 100644 index d088b8b329d..00000000000 Binary files a/doc/ci/quick_start/builds_tab.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_disabled.png b/doc/ci/quick_start/ci_service_disabled.png deleted file mode 100644 index 351de4267a4..00000000000 Binary files a/doc/ci/quick_start/ci_service_disabled.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_enabled.png b/doc/ci/quick_start/ci_service_enabled.png deleted file mode 100644 index dfe7488c1ba..00000000000 Binary files a/doc/ci/quick_start/ci_service_enabled.png and /dev/null differ diff --git a/doc/ci/quick_start/ci_service_mark_active.png b/doc/ci/quick_start/ci_service_mark_active.png deleted file mode 100644 index 8bc8694cec3..00000000000 Binary files a/doc/ci/quick_start/ci_service_mark_active.png and /dev/null differ diff --git a/doc/ci/quick_start/commit_status.png b/doc/ci/quick_start/commit_status.png deleted file mode 100644 index 725b79e6f91..00000000000 Binary files a/doc/ci/quick_start/commit_status.png and /dev/null differ diff --git a/doc/ci/quick_start/enable_ci.md b/doc/ci/quick_start/enable_ci.md deleted file mode 100644 index 42635e35d5d..00000000000 --- a/doc/ci/quick_start/enable_ci.md +++ /dev/null @@ -1,39 +0,0 @@ -# Enable GitLab CI - -GitLab Continuous Integration (CI) is fully integrated into GitLab itself as -of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). - -First, head over your project's page that you would like to enable GitLab CI -for. If you can see the **Builds** tab in the sidebar, then GitLab CI is -already enabled and you can skip reading the rest of this guide. - -![Builds tab](builds_tab.png) - -If not, there are two ways to enable it in your project. - -## Use .gitlab-ci.yml to enable GitLab CI - -GitLab CI will be automatically enabled if you just push a -[`.gitlab-ci.yml`](../yaml/README.md) in your git repository. GitLab will -pick up the change immediately and GitLab CI will be enabled for this project. -This is the recommended way. - -## Manually enable GitLab CI - -The second way is to manually enable it in the project's **Services** settings -and this is also the way to disable it if needed. - -Go to **Settings > Services** and search for **GitLab CI**. Its state should -be disabled. - -![CI service disabled](ci_service_disabled.png) - -Click on **GitLab CI** to enter its settings, mark it as active and hit -**Save**. - -![Mark CI service as active](ci_service_mark_active.png) - -Do you see that green dot? Then good, the service is now enabled! You can also -check its status under **Services**. - -![CI service enabled](ci_service_enabled.png) diff --git a/doc/ci/quick_start/img/build_status.png b/doc/ci/quick_start/img/build_status.png new file mode 100644 index 00000000000..333259e6acd Binary files /dev/null and b/doc/ci/quick_start/img/build_status.png differ diff --git a/doc/ci/quick_start/img/commit_status.png b/doc/ci/quick_start/img/commit_status.png new file mode 100644 index 00000000000..725b79e6f91 Binary files /dev/null and b/doc/ci/quick_start/img/commit_status.png differ diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png new file mode 100644 index 00000000000..3839e893c17 Binary files /dev/null and b/doc/ci/quick_start/img/new_commit.png differ diff --git a/doc/ci/quick_start/img/projects.png b/doc/ci/quick_start/img/projects.png new file mode 100644 index 00000000000..0b3430a69db Binary files /dev/null and b/doc/ci/quick_start/img/projects.png differ diff --git a/doc/ci/quick_start/img/runners.png b/doc/ci/quick_start/img/runners.png new file mode 100644 index 00000000000..25b4046bc00 Binary files /dev/null and b/doc/ci/quick_start/img/runners.png differ diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png new file mode 100644 index 00000000000..c934bd12f41 Binary files /dev/null and b/doc/ci/quick_start/img/runners_activated.png differ diff --git a/doc/ci/quick_start/new_commit.png b/doc/ci/quick_start/new_commit.png deleted file mode 100644 index 3839e893c17..00000000000 Binary files a/doc/ci/quick_start/new_commit.png and /dev/null differ diff --git a/doc/ci/quick_start/projects.png b/doc/ci/quick_start/projects.png deleted file mode 100644 index 0b3430a69db..00000000000 Binary files a/doc/ci/quick_start/projects.png and /dev/null differ diff --git a/doc/ci/quick_start/runners.png b/doc/ci/quick_start/runners.png deleted file mode 100644 index 25b4046bc00..00000000000 Binary files a/doc/ci/quick_start/runners.png and /dev/null differ diff --git a/doc/ci/quick_start/runners_activated.png b/doc/ci/quick_start/runners_activated.png deleted file mode 100644 index c934bd12f41..00000000000 Binary files a/doc/ci/quick_start/runners_activated.png and /dev/null differ -- cgit v1.2.1 From e70fb9cf75d08e8439d94637726dbee7e2324ab6 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 03:49:32 +0200 Subject: New images for the commit build status --- doc/ci/quick_start/README.md | 2 -- doc/ci/quick_start/img/new_commit.png | Bin 47527 -> 9033 bytes .../quick_start/img/single_commit_status_pending.png | Bin 0 -> 36431 bytes doc/ci/quick_start/img/status_pending.png | Bin 0 -> 19782 bytes 4 files changed, 2 deletions(-) create mode 100644 doc/ci/quick_start/img/single_commit_status_pending.png create mode 100644 doc/ci/quick_start/img/status_pending.png diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index ce05a6f417c..073146b9d15 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -120,8 +120,6 @@ Notice that there are two jobs pending which are named after what we wrote in `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured yet for these builds. -The next step is to configure a Runner so that it picks the pending jobs. - ## 2. Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png index 3839e893c17..3d3c9d5c0bd 100644 Binary files a/doc/ci/quick_start/img/new_commit.png and b/doc/ci/quick_start/img/new_commit.png differ diff --git a/doc/ci/quick_start/img/single_commit_status_pending.png b/doc/ci/quick_start/img/single_commit_status_pending.png new file mode 100644 index 00000000000..23b3bb5acfc Binary files /dev/null and b/doc/ci/quick_start/img/single_commit_status_pending.png differ diff --git a/doc/ci/quick_start/img/status_pending.png b/doc/ci/quick_start/img/status_pending.png new file mode 100644 index 00000000000..a049ec2a5ba Binary files /dev/null and b/doc/ci/quick_start/img/status_pending.png differ -- cgit v1.2.1 From 11b204f1312c3d6aa579b7a04cb4bcf794ad5239 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 10 Nov 2015 21:16:12 +0200 Subject: Final touches for the quick start quide --- doc/ci/quick_start/README.md | 6 ++++-- doc/ci/quick_start/img/build_log.png | Bin 0 -> 63272 bytes doc/ci/quick_start/img/builds_status.png | Bin 0 -> 49121 bytes doc/ci/quick_start/img/runners_activated.png | Bin 60769 -> 27597 bytes 4 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 doc/ci/quick_start/img/build_log.png create mode 100644 doc/ci/quick_start/img/builds_status.png diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 073146b9d15..7771d78d91f 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -16,7 +16,7 @@ In brief, the steps needed to have a working CI can be summed up to: 1. Configure a Runner From there on, on every push to your git repository the build will be -automagically started by the runner and will appear under the project's +automagically started by the Runner and will appear under the project's `/builds` page. Now, let's break it down to pieces and work on solving the GitLab CI puzzle. @@ -120,6 +120,8 @@ Notice that there are two jobs pending which are named after what we wrote in `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured yet for these builds. +The next step is to configure a Runner so that it picks the pending jobs. + ## 2. Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. @@ -136,8 +138,8 @@ Find more information about different Runners in the You can find whether any Runners are assigned to your project by going to **Settings** -> **Runners**. - Setting up a Runner is easy and straightforward. The official Runner supported + by GitLab is written in Go and can be found at . diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png new file mode 100644 index 00000000000..89e6cd40cb6 Binary files /dev/null and b/doc/ci/quick_start/img/build_log.png differ diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png new file mode 100644 index 00000000000..b8e6c2a361a Binary files /dev/null and b/doc/ci/quick_start/img/builds_status.png differ diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png index c934bd12f41..eafcfd6ecd5 100644 Binary files a/doc/ci/quick_start/img/runners_activated.png and b/doc/ci/quick_start/img/runners_activated.png differ -- cgit v1.2.1 From aea5b72faafa6911377cb0c9fc0801269ac59e1e Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 11 Nov 2015 09:57:21 +0200 Subject: Remove old images --- doc/ci/quick_start/img/build_status.png | Bin 62140 -> 0 bytes doc/ci/quick_start/img/commit_status.png | Bin 33492 -> 0 bytes doc/ci/quick_start/img/projects.png | Bin 37014 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/ci/quick_start/img/build_status.png delete mode 100644 doc/ci/quick_start/img/commit_status.png delete mode 100644 doc/ci/quick_start/img/projects.png diff --git a/doc/ci/quick_start/img/build_status.png b/doc/ci/quick_start/img/build_status.png deleted file mode 100644 index 333259e6acd..00000000000 Binary files a/doc/ci/quick_start/img/build_status.png and /dev/null differ diff --git a/doc/ci/quick_start/img/commit_status.png b/doc/ci/quick_start/img/commit_status.png deleted file mode 100644 index 725b79e6f91..00000000000 Binary files a/doc/ci/quick_start/img/commit_status.png and /dev/null differ diff --git a/doc/ci/quick_start/img/projects.png b/doc/ci/quick_start/img/projects.png deleted file mode 100644 index 0b3430a69db..00000000000 Binary files a/doc/ci/quick_start/img/projects.png and /dev/null differ -- cgit v1.2.1 From 6805f1bc47640886020e5febf015a73e5862114c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 1 Dec 2015 10:05:24 +0200 Subject: Clean up quick start quide [ci skip] * Remove references to enabling CI since it it will be deprecated * Revert changes in yaml/README.md, refine it in a separate MR --- doc/ci/enable_ci.md | 39 ---------------------- doc/ci/img/ci_service_disabled.png | Bin 3598 -> 0 bytes doc/ci/img/ci_service_enabled.png | Bin 3545 -> 0 bytes doc/ci/img/ci_service_mark_active.png | Bin 17193 -> 0 bytes doc/ci/quick_start/README.md | 61 ++++++++++++++++------------------ doc/ci/quick_start/img/runners.png | Bin 123048 -> 0 bytes doc/ci/yaml/README.md | 30 ++++++++--------- 7 files changed, 42 insertions(+), 88 deletions(-) delete mode 100644 doc/ci/enable_ci.md delete mode 100644 doc/ci/img/ci_service_disabled.png delete mode 100644 doc/ci/img/ci_service_enabled.png delete mode 100644 doc/ci/img/ci_service_mark_active.png delete mode 100644 doc/ci/quick_start/img/runners.png diff --git a/doc/ci/enable_ci.md b/doc/ci/enable_ci.md deleted file mode 100644 index 01c684dabec..00000000000 --- a/doc/ci/enable_ci.md +++ /dev/null @@ -1,39 +0,0 @@ -# Enable GitLab CI - -GitLab Continuous Integration (CI) is fully integrated into GitLab itself as -of [version 8.0](https://about.gitlab.com/2015/09/22/gitlab-8-0-released/). - -First, head over your project's page that you would like to enable GitLab CI -for. If you can see the **Builds** tab in the sidebar, then GitLab CI is -already enabled and you can skip reading the rest of this guide. - -![Builds tab](img/builds_tab.png) - -If not, there are two ways to enable it in your project. - -## Use .gitlab-ci.yml to enable GitLab CI - -GitLab CI will be automatically enabled if you just push a -[`.gitlab-ci.yml`](yaml/README.md) in your git repository. GitLab will -pick up the change immediately and GitLab CI will be enabled for this project. -This is the recommended way. - -## Manually enable GitLab CI - -The second way is to manually enable it in the project's **Services** settings -and this is also the way to disable it if needed. - -Go to **Settings > Services** and search for **GitLab CI**. Its state should -be disabled. - -![CI service disabled](img/ci_service_disabled.png) - -Click on **GitLab CI** to enter its settings, mark it as active and hit -**Save**. - -![Mark CI service as active](img/ci_service_mark_active.png) - -Do you see that green dot? Then good, the service is now enabled! You can also -check its status under **Services**. - -![CI service enabled](img/ci_service_enabled.png) diff --git a/doc/ci/img/ci_service_disabled.png b/doc/ci/img/ci_service_disabled.png deleted file mode 100644 index 351de4267a4..00000000000 Binary files a/doc/ci/img/ci_service_disabled.png and /dev/null differ diff --git a/doc/ci/img/ci_service_enabled.png b/doc/ci/img/ci_service_enabled.png deleted file mode 100644 index dfe7488c1ba..00000000000 Binary files a/doc/ci/img/ci_service_enabled.png and /dev/null differ diff --git a/doc/ci/img/ci_service_mark_active.png b/doc/ci/img/ci_service_mark_active.png deleted file mode 100644 index 8bc8694cec3..00000000000 Binary files a/doc/ci/img/ci_service_mark_active.png and /dev/null differ diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 7771d78d91f..a9b36139de9 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -1,7 +1,7 @@ # Quick Start -GitLab Continuous Integration (CI) is fully integrated into GitLab itself. You -only need to enable it in the Services settings of your project. +Starting from version 8.0, GitLab Continuous Integration (CI) is fully +integrated into GitLab itself and is enabled by default on all projects. This guide assumes that you: @@ -21,17 +21,10 @@ automagically started by the Runner and will appear under the project's Now, let's break it down to pieces and work on solving the GitLab CI puzzle. -## 1. Creating a `.gitlab-ci.yml` file +## Creating a `.gitlab-ci.yml` file - **GitLab CI** service is enabled automatically on the first push of a - `.gitlab-ci.yml` file in your repository and this is the recommended way. - -For other methods read [how to enable the GitLab CI service](../enable_ci.md). - - **GitLab CI** service is enabled automatically on the first push of a - `.gitlab-ci.yml` file in your repository and this is the recommended way. - -For other methods read [how to enable the GitLab CI service](../enable_ci.md). +Before you create `.gitlab-ci.yml` let's first explain in brief what this is +all about. ### What is `.gitlab-ci.yml` @@ -48,7 +41,9 @@ branches can have separate builds and you have a single source of truth for CI. You can read more about the reasons why we are using `.gitlab-ci.yml` [in our blog about it][blog-ci]. -`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file. +**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file +so you have to pay extra attention to the identation. Always use spaces, not +tabs. ### Creating a simple `.gitlab-ci.yml` file @@ -75,20 +70,21 @@ rubocop: This is the simplest possible build configuration that will work for most Ruby applications: -1. Define two jobs `rspec` and `rubocop` with different commands to be executed. +1. Define two jobs `rspec` and `rubocop` (the names are arbitrary) with + different commands to be executed. 1. Before every job, the commands defined by `before_script` are executed. The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when -they should be run. The jobs are defined as top-level elements with a name and -always have to contain the `script` name. Jobs are used to create builds, -which are then picked by [Runners](../runners/README.md) and executed within -the environment of the Runner. +they should be run. The jobs are defined as top-level elements with a name (in +our case `rspec` and `rubocop`) and always have to contain the `script` keyword. +Jobs are used to create builds, which are then picked by +[Runners](../runners/README.md) and executed within the environment of the Runner. What is important is that each job is run independently from each other. If you want to check whether your `.gitlab-ci.yml` file is valid, there is a Lint tool under the page `/ci/lint` of your GitLab instance. You can also find -the link under **Settings** -> **CI settings** in your project. +the link under **Settings > CI settings** in your project. For more information and a complete `.gitlab-ci.yml` syntax, please check [the documentation on .gitlab-ci.yml](../yaml/README.md). @@ -122,13 +118,13 @@ yet for these builds. The next step is to configure a Runner so that it picks the pending jobs. -## 2. Configuring a Runner +## Configuring a Runner In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker container or even a cluster of containers. GitLab and the Runners communicate through an API, so the only needed requirement is that the machine on which the -Runner is configured to have Internet access. +Runner is configured to has Internet access. A Runner can be specific to a certain project or serve multiple projects in GitLab. If it serves all projects it's called a _Shared Runner_. @@ -137,22 +133,23 @@ Find more information about different Runners in the [Runners](../runners/README.md) documentation. You can find whether any Runners are assigned to your project by going to -**Settings** -> **Runners**. -Setting up a Runner is easy and straightforward. The official Runner supported - -by GitLab is written in Go and can be found at +**Settings > Runners**. Setting up a Runner is easy and straightforward. The +official Runner supported by GitLab is written in Go and can be found at . -In order to have a functional Runner you need to: +In order to have a functional Runner you need to follow two steps: 1. [Install it][runner-install] 2. [Configure it](../runners/README.md#registering-a-specific-runner) +Follow the links above to set up your own Runner or use a Shared Runner as +described in the next section. + For other types of unofficial Runners written in other languages, see the [instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner). Once the Runner has been set up, you should see it on the Runners page of your -project, following **Settings** -> **Runners**. +project, following **Settings > Runners**. ![Activated runners](img/runners_activated.png) @@ -161,15 +158,15 @@ project, following **Settings** -> **Runners**. If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners** provided by GitLab Inc. -These are special virtual machines that are run on GitLab's infrastructure that -can build any project. +These are special virtual machines that run on GitLab's infrastructure and can +build any project. To enable **Shared Runners** you have to go to your project's -**Settings** -> **Runners** and click **Enable shared runners**. +**Settings > Runners** and click **Enable shared runners**. [Read more on Shared Runners](../runners/README.md). -## 3. Seeing the status of your build +## Seeing the status of your build After configuring the Runner succesfully, you should see the status of your last commit change from _pending_ to either _running_, _success_ or _failed_. @@ -194,7 +191,7 @@ Awesome! You started using CI in GitLab! Next you can look into doing more with the CI. Many people are using GitLab to package, containerize, test and deploy software. -We have a number of [examples](../examples/README.md) available. +Visit our various languages examples at . [runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ diff --git a/doc/ci/quick_start/img/runners.png b/doc/ci/quick_start/img/runners.png deleted file mode 100644 index 25b4046bc00..00000000000 Binary files a/doc/ci/quick_start/img/runners.png and /dev/null differ diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 9ee26c41e6d..3dbf1afc7a9 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -20,22 +20,6 @@ Of course a command can execute code directly (`./configure;make;make install`) Jobs are used to create builds, which are then picked up by [runners](../runners/README.md) and executed within the environment of the runner. What is important, is that each job is run independently from each other. -## Why `.gitlab-ci.yml` - -By placing a single configuration file in the root of your repository, -it is version controlled and you get all the advantages of git. - -In addition, builds for older versions of the repository will work just fine, -as GitLab look at the `.gitlab-ci.yml` of the pushed commit. -This means that forks also build without any problem. - -You can even set up different builds for different branches. This allows you -to only deploy the `production` branch, for instance. - -By having a single source of truth, everyone can view and contribute to the -stability of your CI builds, eventually improving the quality of your development -cycle. - ## .gitlab-ci.yml The YAML syntax allows for using more complex job specifications than in the above example: @@ -201,7 +185,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs There are a few rules that apply to usage of refs policy: -1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account. +1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`. 1. `only` and `except` allow for using the regexp expressions. 1. `only` and `except` allow for using special keywords: `branches` and `tags`. These names can be used for example to exclude all tags and all branches. @@ -214,6 +198,18 @@ job: - branches # use special keyword ``` +1. `only` and `except` allow for specify repository path to filter jobs for forks. +The repository path can be used to have jobs executed only for parent repository. + +```yaml +job: + only: + - branches@gitlab-org/gitlab-ce + except: + - master@gitlab-org/gitlab-ce +``` +The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master . + ### tags `tags` is used to select specific runners from the list of all runners that are allowed to run this project. -- cgit v1.2.1 From bd4ab21c07061e06166b08d86157e4004665ccbc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 12:49:22 +0100 Subject: Fix code docs --- app/models/commit_range.rb | 7 ++----- lib/gitlab/markdown/abstract_reference_filter.rb | 15 +++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 7b1164b024c..449689faf65 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -13,8 +13,7 @@ # range.to_param # => {from: "f3f856029bc5f966c5a7ee24cf7efefdd20e6019^", to: "e86e1013709735be5bb767e2b228930c543f25ae"} # range.to_s # => "f3f85602..e86e1013" # -# # Assuming `project` is a Project with a repository containing both commits: -# range.project = project +# # Assuming the specified project has a repository containing both commits: # range.valid_commits? # => true # class CommitRange @@ -68,7 +67,7 @@ class CommitRange range_string.strip! - unless range_string.match(/\A#{PATTERN}\z/) + unless range_string =~ /\A#{PATTERN}\z/ raise ArgumentError, "invalid CommitRange string format: #{range_string}" end @@ -123,8 +122,6 @@ class CommitRange # Check if both the starting and ending commit IDs exist in a project's # repository - # - # project - An optional Project to check (default: `project`) def valid_commits? commit_start.present? && commit_end.present? end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 4adc44361b7..02a9e05a699 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -2,8 +2,8 @@ require 'gitlab/markdown' module Gitlab module Markdown - # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering. - # All this functionality moved to this class + # Issues, Snippets, Merge Requests, Commits and Commit Ranges share + # similar functionality in refernce filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -26,16 +26,15 @@ module Gitlab # Public: Find references in text (like `!123` for merge requests) # - # AnyReferenceFilter.references_in(text) do |match, object| - # "PREFIX#{object}" + # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches| + # object = find_object(project_ref, id) + # "#{object.to_reference}" # end # - # PREFIX - symbol that detects reference (like ! for merge requests) - # object - reference object (snippet, merget request etc) # text - String text to search. # - # Yields the String match, the Integer referenced object ID, and an optional String - # of the external project reference. + # Yields the String match, the Integer referenced object ID, an optional String + # of the external project reference, and all of the matchdata. # # Returns a String replaced with the return of the block. def self.references_in(text) -- cgit v1.2.1 From 62c14ba2edf9ac4b4bb1e8c46c0c60f1b6574909 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 12:58:45 +0100 Subject: Render commit reference using short sha, but include full sha in comment. --- app/models/commit.rb | 8 ++++++++ app/models/commit_range.rb | 8 ++++++++ app/models/concerns/referable.rb | 4 ++++ lib/gitlab/markdown/abstract_reference_filter.rb | 2 +- .../markdown/commit_range_reference_filter_spec.rb | 6 +++--- .../markdown/commit_reference_filter_spec.rb | 2 +- spec/models/commit_range_spec.rb | 22 +++++++++++++++++++--- spec/models/commit_spec.rb | 17 ++++++++++++++--- 8 files changed, 58 insertions(+), 11 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index fc03d2580d7..a5d041a49c8 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -86,6 +86,14 @@ class Commit end def to_reference(from_project = nil) + if cross_project_reference?(from_project) + project.to_reference + self.class.reference_prefix + self.id + else + self.id + end + end + + def reference_link_text(from_project = nil) if cross_project_reference?(from_project) project.to_reference + self.class.reference_prefix + self.short_id else diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index 449689faf65..f786a749b8e 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -95,6 +95,14 @@ class CommitRange alias_method :id, :to_s def to_reference(from_project = nil) + if cross_project_reference?(from_project) + reference = project.to_reference + self.class.reference_prefix + self.id + else + self.id + end + end + + def reference_link_text(from_project = nil) reference = ref_from + notation + ref_to if cross_project_reference?(from_project) diff --git a/app/models/concerns/referable.rb b/app/models/concerns/referable.rb index 16e4d054869..ce064f675ae 100644 --- a/app/models/concerns/referable.rb +++ b/app/models/concerns/referable.rb @@ -21,6 +21,10 @@ module Referable '' end + def reference_link_text(from_project = nil) + to_reference(from_project) + end + module ClassMethods # The character that prefixes the actual reference identifier # diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 02a9e05a699..f6df9518fc6 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -82,7 +82,7 @@ module Gitlab data = data_attribute(project: project.id, object_sym => object.id, original: match) url = matches[:url] || url_for_object(object, project) - text = object.to_reference(context[:project]) + text = object.reference_link_text(context[:project]) extras = object_link_text_extras(object, matches) text += " (#{extras.join(", ")})" if extras.any? diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 4b4c769a110..f65a3e8a0bd 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -53,7 +53,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("See (#{reference}.)") - exp = Regexp.escape(range.to_reference) + exp = Regexp.escape(range.reference_link_text) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -125,7 +125,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape("#{project2.to_reference}@#{range.to_reference}") + exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}") expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end @@ -163,7 +163,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - exp = Regexp.escape(range.to_reference(project)) + exp = Regexp.escape(range.reference_link_text(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index a8c9c7efd56..4cc6bbbfe94 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -148,7 +148,7 @@ module Gitlab::Markdown it 'links with adjacent text' do doc = filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(#{commit.to_reference(project)}<\/a>\.\)/) + expect(doc.to_html).to match(/\(#{commit.reference_link_text(project)}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 58283f06972..3c1009a2eb0 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -38,15 +38,31 @@ describe CommitRange do let(:cross) { create(:project) } it 'returns a String reference to the object' do - expect(range.to_reference).to eq "#{sha_from}...#{sha_to}" + expect(range.to_reference).to eq "#{full_sha_from}...#{full_sha_to}" end it 'returns a String reference to the object' do - expect(range2.to_reference).to eq "#{sha_from}..#{sha_to}" + expect(range2.to_reference).to eq "#{full_sha_from}..#{full_sha_to}" end it 'supports a cross-project reference' do - expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" + expect(range.to_reference(cross)).to eq "#{project.to_reference}@#{full_sha_from}...#{full_sha_to}" + end + end + + describe '#reference_link_text' do + let(:cross) { create(:project) } + + it 'returns a String reference to the object' do + expect(range.reference_link_text).to eq "#{sha_from}...#{sha_to}" + end + + it 'returns a String reference to the object' do + expect(range2.reference_link_text).to eq "#{sha_from}..#{sha_to}" + end + + it 'supports a cross-project reference' do + expect(range.reference_link_text(cross)).to eq "#{project.to_reference}@#{sha_from}...#{sha_to}" end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 1aaa927c216..974b52c1833 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -15,12 +15,23 @@ describe Commit do describe '#to_reference' do it 'returns a String reference to the object' do - expect(commit.to_reference).to eq commit.short_id + expect(commit.to_reference).to eq commit.id end it 'supports a cross-project reference' do cross = double('project') - expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.short_id}" + expect(commit.to_reference(cross)).to eq "#{project.to_reference}@#{commit.id}" + end + end + + describe '#reference_link_text' do + it 'returns a String reference to the object' do + expect(commit.reference_link_text).to eq commit.short_id + end + + it 'supports a cross-project reference' do + cross = double('project') + expect(commit.reference_link_text(cross)).to eq "#{project.to_reference}@#{commit.short_id}" end end @@ -88,7 +99,7 @@ eos subject { create(:project).commit } let(:author) { create(:user, email: subject.author_email) } - let(:backref_text) { "commit #{subject.short_id}" } + let(:backref_text) { "commit #{subject.id}" } let(:set_mentionable_text) do ->(txt) { allow(subject).to receive(:safe_message).and_return(txt) } end -- cgit v1.2.1 From 55d0e04ffd3f178f41ae3ef13790eb6882b1ef8e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 1 Dec 2015 14:26:57 +0100 Subject: Auto-detect the required gitlab-shell version --- doc/install/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 61647780607..618391e16d2 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -312,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.8] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: -- cgit v1.2.1 From f3ea06eb7f8bef748be0882cb0b4fb58deed8eef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 15:51:27 +0100 Subject: Autolink first so we don't pick up numeric anchors as issue references. --- app/models/commit.rb | 7 ++-- app/models/commit_range.rb | 7 ++-- app/models/issue.rb | 7 ++-- app/models/merge_request.rb | 7 ++-- app/models/snippet.rb | 7 ++-- lib/gitlab/closing_issue_extractor.rb | 2 +- lib/gitlab/markdown.rb | 5 ++- lib/gitlab/markdown/abstract_reference_filter.rb | 20 +++++++---- .../markdown/commit_range_reference_filter.rb | 4 +-- lib/gitlab/markdown/commit_reference_filter.rb | 4 +-- lib/gitlab/markdown/external_link_filter.rb | 7 ++-- .../markdown/merge_request_reference_filter.rb | 2 +- lib/gitlab/markdown/redactor_filter.rb | 2 +- lib/gitlab/markdown/reference_filter.rb | 23 +++++++++++++ lib/gitlab/reference_extractor.rb | 14 ++++++-- .../markdown/commit_range_reference_filter_spec.rb | 40 +++++++++++----------- .../markdown/commit_reference_filter_spec.rb | 36 +++++++++---------- .../gitlab/markdown/issue_reference_filter_spec.rb | 36 +++++++++---------- .../merge_request_reference_filter_spec.rb | 30 ++++++++-------- .../markdown/snippet_reference_filter_spec.rb | 32 ++++++++--------- spec/support/filter_spec_helper.rb | 19 ++++++++-- 21 files changed, 171 insertions(+), 140 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index a5d041a49c8..c0998a45709 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -73,11 +73,8 @@ class Commit # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (?:#{Project.reference_pattern}#{reference_prefix})? - (?\h{6,40}) - ) + (?:#{Project.reference_pattern}#{reference_prefix})? + (?\h{6,40}) }x end diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index f786a749b8e..b8bf36b32ce 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -44,11 +44,8 @@ class CommitRange # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (?:#{Project.reference_pattern}#{reference_prefix})? - (?#{STRICT_PATTERN}) - ) + (?:#{Project.reference_pattern}#{reference_prefix})? + (?#{STRICT_PATTERN}) }x end diff --git a/app/models/issue.rb b/app/models/issue.rb index e62acfdfd91..187b6482b6c 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -64,11 +64,8 @@ class Issue < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c1d3874adee..2a4aee7e5d9 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -146,11 +146,8 @@ class MergeRequest < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 8ec12ddf6ef..f876be7a4c8 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -60,11 +60,8 @@ class Snippet < ActiveRecord::Base # This pattern supports cross-project references. def self.reference_pattern %r{ - #{link_reference_pattern} | - (?: - (#{Project.reference_pattern})? - #{Regexp.escape(reference_prefix)}(?\d+) - ) + (#{Project.reference_pattern})? + #{Regexp.escape(reference_prefix)}(?\d+) }x end diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index 70b9943d7eb..0cf4c918736 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -2,7 +2,7 @@ module Gitlab class ClosingIssueExtractor ISSUE_CLOSING_REGEX = begin pattern = Gitlab.config.gitlab.issue_closing_pattern - pattern = pattern.sub('%{issue_ref}', "(?:#{Issue.reference_pattern})") + pattern = pattern.sub('%{issue_ref}', "(?:(?:#{Issue.link_reference_pattern})|(?:#{Issue.reference_pattern}))") Regexp.new(pattern).freeze end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index ee458eda025..b082bfc434b 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -181,6 +181,8 @@ module Gitlab Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, + Gitlab::Markdown::AutolinkFilter, + Gitlab::Markdown::ExternalLinkFilter, Gitlab::Markdown::UserReferenceFilter, Gitlab::Markdown::IssueReferenceFilter, @@ -191,9 +193,6 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, - Gitlab::Markdown::AutolinkFilter, - Gitlab::Markdown::ExternalLinkFilter, - Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index f6df9518fc6..37ed423eeda 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -37,8 +37,8 @@ module Gitlab # of the external project reference, and all of the matchdata. # # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(object_class.reference_pattern) do |match| + def self.references_in(text, pattern = object_class.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[object_sym].to_i, $~[:project], $~ end end @@ -61,7 +61,11 @@ module Gitlab def call replace_text_nodes_matching(object_class.reference_pattern) do |content| - object_link_filter(content) + object_link_filter(content, object_class.reference_pattern) + end + + replace_link_nodes_matching(object_class.link_reference_pattern) do |content| + object_link_filter(content, object_class.link_reference_pattern) end end @@ -72,15 +76,17 @@ module Gitlab # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. - def object_link_filter(text) - references_in(text) do |match, id, project_ref, matches| + def object_link_filter(text, pattern) + references_in(text, pattern) do |match, id, project_ref, matches| project = project_from_ref(project_ref) if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) data = data_attribute(project: project.id, object_sym => object.id, original: match) - url = matches[:url] || url_for_object(object, project) + + url = matches[:url] if matches.names.include?("url") + url ||= url_for_object(object, project) text = object.reference_link_text(context[:project]) @@ -99,7 +105,7 @@ module Gitlab def object_link_text_extras(object, matches) extras = [] - if matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ + if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ extras << "comment #{$1}" end diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb index f24bed76193..36b3258ef76 100644 --- a/lib/gitlab/markdown/commit_range_reference_filter.rb +++ b/lib/gitlab/markdown/commit_range_reference_filter.rb @@ -10,8 +10,8 @@ module Gitlab CommitRange end - def self.references_in(text) - text.gsub(CommitRange.reference_pattern) do |match| + def self.references_in(text, pattern = CommitRange.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[:commit_range], $~[:project], $~ end end diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index cc7abc08c87..b4036578e60 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -10,8 +10,8 @@ module Gitlab Commit end - def self.references_in(text) - text.gsub(Commit.reference_pattern) do |match| + def self.references_in(text, pattern = Commit.reference_pattern) + text.gsub(pattern) do |match| yield match, $~[:commit], $~[:project], $~ end end diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/gitlab/markdown/external_link_filter.rb index b6792932016..e09dfcb83c8 100644 --- a/lib/gitlab/markdown/external_link_filter.rb +++ b/lib/gitlab/markdown/external_link_filter.rb @@ -8,12 +8,9 @@ module Gitlab class ExternalLinkFilter < HTML::Pipeline::Filter def call doc.search('a').each do |node| - next unless node.has_attribute?('href') + link = node.attr('href') - klass = node.attribute('class') - next if klass && klass.include?('gfm') - - link = node.attribute('href').value + next unless link # Skip non-HTTP(S) links next unless link.start_with?('http') diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 3780a14a130..de71fc76a9b 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -24,7 +24,7 @@ module Gitlab def object_link_text_extras(object, matches) extras = super - if matches[:path] && matches[:path] == '/diffs' + if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs' extras.unshift "diffs" end diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/gitlab/markdown/redactor_filter.rb index 2a58c798f9f..bea714a01e7 100644 --- a/lib/gitlab/markdown/redactor_filter.rb +++ b/lib/gitlab/markdown/redactor_filter.rb @@ -14,7 +14,7 @@ module Gitlab unless user_can_reference?(node) # The reference should be replaced by the original text, # which is not always the same as the rendered text. - text = node.attribute('data-original') || node.text + text = node.attr('data-original') || node.text node.replace(text) end end diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index a4c560f578c..e52633ee74c 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,6 +122,29 @@ module Gitlab doc end + def replace_link_nodes_matching(pattern) + return doc if project.nil? + + doc.search('a').each do |node| + klass = node.attr('class') + next if klass && klass.include?('gfm') + + link = node.attr('href') + text = node.text + + # Ignore ending punctionation like periods or commas + next unless link == text && text =~ /\A#{pattern}/ + + html = yield text + + next if html == text + + node.replace(html) + end + + doc + end + # Ensure that a :project key exists in context # # Note that while the key might exist, its value could be nil! diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index da8df8a3025..3c3478a1271 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -41,14 +41,14 @@ module Gitlab # Returns the results Array for the requested filter type def pipeline_result(filter_type) return [] if @text.blank? - + klass = "#{filter_type.to_s.camelize}ReferenceFilter" filter = Gitlab::Markdown.const_get(klass) context = { project: project, current_user: current_user, - + # We don't actually care about the links generated only_path: true, ignore_blockquotes: true, @@ -58,7 +58,15 @@ module Gitlab reference_filter: filter } - pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) + # We need to autolink first to finds links to referables, and to prevent + # numeric anchors to be parsed as issue references. + filters = [ + Gitlab::Markdown::AutolinkFilter, + filter, + Gitlab::Markdown::ReferenceGathererFilter + ] + + pipeline = HTML::Pipeline.new(filters, context) result = pipeline.call(@text) values = result[:references][filter_type].uniq diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index f65a3e8a0bd..92158382790 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -18,7 +18,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Commit Range #{range.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -27,14 +27,14 @@ module Gitlab::Markdown let(:reference2) { range2.to_reference } it 'links to a valid two-dot reference' do - doc = filter("See #{reference2}") + doc = reference_filter("See #{reference2}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param) end it 'links to a valid three-dot reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param) @@ -46,12 +46,12 @@ module Gitlab::Markdown exp = commit1.short_id + '...' + commit2.short_id - expect(filter("See #{reference}").css('a').first.text).to eq exp - expect(filter("See #{reference2}").css('a').first.text).to eq exp + expect(reference_filter("See #{reference}").css('a').first.text).to eq exp + expect(reference_filter("See #{reference2}").css('a').first.text).to eq exp end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") exp = Regexp.escape(range.reference_link_text) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -63,21 +63,21 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(commit1.id.reverse) expect(project.repository).to receive(:commit).with(commit2.id) - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('title')).to eq range.reference_title end it 'includes default classes' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-commit_range' end it 'includes a data-project attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-project') @@ -85,7 +85,7 @@ module Gitlab::Markdown end it 'includes a data-commit-range attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-commit-range') @@ -93,7 +93,7 @@ module Gitlab::Markdown end it 'supports an :only_path option' do - doc = filter("See #{reference}", only_path: true) + doc = reference_filter("See #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) @@ -116,14 +116,14 @@ module Gitlab::Markdown end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") exp = Regexp.escape("#{project2.to_reference}@#{range.reference_link_text}") expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -131,10 +131,10 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -154,14 +154,14 @@ module Gitlab::Markdown end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") exp = Regexp.escape(range.reference_link_text(project)) expect(doc.to_html).to match(/\(#{exp}<\/a>\.\)/) @@ -169,10 +169,10 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Fixed #{project2.to_reference}@#{commit1.id.reverse}...#{commit2.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp exp = act = "Fixed #{project2.to_reference}@#{commit1.id}...#{commit2.id.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 4cc6bbbfe94..6fe9b165ff5 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -14,7 +14,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Commit #{commit.id}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -24,7 +24,7 @@ module Gitlab::Markdown # Let's test a variety of commit SHA sizes just to be paranoid [6, 8, 12, 18, 20, 32, 40].each do |size| it "links to a valid reference of #{size} characters" do - doc = filter("See #{reference[0...size]}") + doc = reference_filter("See #{reference[0...size]}") expect(doc.css('a').first.text).to eq commit.short_id expect(doc.css('a').first.attr('href')). @@ -33,15 +33,15 @@ module Gitlab::Markdown end it 'always uses the short ID as the link text' do - doc = filter("See #{commit.id}") + doc = reference_filter("See #{commit.id}") expect(doc.text).to eq "See #{commit.short_id}" - doc = filter("See #{commit.id[0...6]}") + doc = reference_filter("See #{commit.id[0...6]}") expect(doc.text).to eq "See #{commit.short_id}" end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") expect(doc.to_html).to match(/\(#{commit.short_id}<\/a>\.\)/) end @@ -51,28 +51,28 @@ module Gitlab::Markdown expect(project).to receive(:valid_repo?).and_return(true) expect(project.repository).to receive(:commit).with(invalid) - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('title')).to eq commit.link_title end it 'escapes the title attribute' do allow_any_instance_of(Commit).to receive(:title).and_return(%{">whatever#{exp}@#{commit.short_id}<\/a>\.\)/) @@ -123,7 +123,7 @@ module Gitlab::Markdown it 'ignores invalid commit IDs on the referenced project' do exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -139,21 +139,21 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_commit_url(project2.namespace, project2, commit.id) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{commit.reference_link_text(project)}<\/a>\.\)/) end it 'ignores invalid commit IDs on the referenced project' do exp = act = "Committed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 296e8868f46..0a741688b82 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -18,7 +18,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Issue #{issue.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -29,18 +29,18 @@ module Gitlab::Markdown expect(project).to receive(:get_issue).with(issue.iid).and_return(nil) exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'links to a valid reference' do - doc = filter("Fixed #{reference}") + doc = reference_filter("Fixed #{reference}") expect(doc.css('a').first.attr('href')). to eq helper.url_for_issue(issue.iid, project) end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end @@ -48,28 +48,28 @@ module Gitlab::Markdown invalid = invalidate_reference(reference) exp = act = "Fixed #{invalid}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Issue #{reference}") + doc = reference_filter("Issue #{reference}") expect(doc.css('a').first.attr('title')).to eq "Issue: #{issue.title}" end it 'escapes the title attribute' do issue.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid issue IDs on the referenced project' do exp = act = "Fixed #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -147,18 +147,18 @@ module Gitlab::Markdown with(issue.iid).and_return(nil) exp = act = "Issue #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(reference)}<\/a>/) end it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Fixed (#{reference}.)") + doc = reference_filter("Fixed (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index ca3e7151e02..cdb3390e793 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -14,7 +14,7 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Merge #{merge.to_reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -22,42 +22,42 @@ module Gitlab::Markdown let(:reference) { merge.to_reference } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_merge_request_url(project.namespace, project, merge) end it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") + doc = reference_filter("Merge (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid merge IDs' do exp = act = "Merge #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Merge #{reference}") + doc = reference_filter("Merge #{reference}") expect(doc.css('a').first.attr('title')).to eq "Merge Request: #{merge.title}" end it 'escapes the title attribute' do merge.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid merge IDs on the referenced project' do exp = act = "Merge #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -124,14 +124,14 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_merge_request_url(project2.namespace, project2, merge) + '/diffs#note_123' } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq reference end it 'links with adjacent text' do - doc = filter("Merge (#{reference}.)") + doc = reference_filter("Merge (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/) end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 3080a8a3608..73d20957a56 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -15,48 +15,48 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Snippet #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end context 'internal reference' do it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_snippet_url(project.namespace, project, snippet) end it 'links with adjacent text' do - doc = filter("Snippet (#{reference}.)") + doc = reference_filter("Snippet (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid snippet IDs' do exp = act = "Snippet #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'includes a title attribute' do - doc = filter("Snippet #{reference}") + doc = reference_filter("Snippet #{reference}") expect(doc.css('a').first.attr('title')).to eq "Snippet: #{snippet.title}" end it 'escapes the title attribute' do snippet.update_attribute(:title, %{">whatever#{Regexp.escape(reference)}<\/a>\.\)/) end it 'ignores invalid snippet IDs on the referenced project' do exp = act = "See #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end it 'adds to the results hash' do @@ -122,21 +122,21 @@ module Gitlab::Markdown let(:reference) { urls.namespace_project_snippet_url(project2.namespace, project2, snippet) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet) end it 'links with adjacent text' do - doc = filter("See (#{reference}.)") + doc = reference_filter("See (#{reference}.)") expect(doc.to_html).to match(/\(#{Regexp.escape(snippet.to_reference(project))}<\/a>\.\)/) end it 'ignores invalid snippet IDs on the referenced project' do exp = act = "See #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end it 'adds to the results hash' do diff --git a/spec/support/filter_spec_helper.rb b/spec/support/filter_spec_helper.rb index 97e5c270a59..91e3bee13c1 100644 --- a/spec/support/filter_spec_helper.rb +++ b/spec/support/filter_spec_helper.rb @@ -35,11 +35,24 @@ module FilterSpecHelper pipeline.call(body) end - def reference_pipeline_result(body, contexts = {}) + def reference_pipeline(contexts = {}) contexts.reverse_merge!(project: project) if defined?(project) - pipeline = HTML::Pipeline.new([described_class, Gitlab::Markdown::ReferenceGathererFilter], contexts) - pipeline.call(body) + filters = [ + Gitlab::Markdown::AutolinkFilter, + described_class, + Gitlab::Markdown::ReferenceGathererFilter + ] + + HTML::Pipeline.new(filters, contexts) + end + + def reference_pipeline_result(body, contexts = {}) + reference_pipeline(contexts).call(body) + end + + def reference_filter(html, contexts = {}) + reference_pipeline(contexts).to_document(html) end # Modify a String reference to make it invalid -- cgit v1.2.1 From 6dad2bc6e67f47149e8981730cf3f08938794ceb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:15:53 +0100 Subject: Fix referenced_mentionables method. --- app/models/concerns/mentionable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 0d6cd86aade..634a8d0f274 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -67,7 +67,7 @@ module Mentionable # We're using this method instead of Array diffing because that requires # both of the object's `hash` values to be the same, which may not be the # case for otherwise identical Commit objects. - refs.reject! { |ref| ref == local_reference } + refs.reject { |ref| ref == local_reference } end # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. -- cgit v1.2.1 From b0ef603f71e3a55981f15944f16595470091c344 Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Tue, 1 Dec 2015 16:21:53 +0100 Subject: up for grabs label --- CONTRIBUTING.md | 10 ++++++++++ PROCESS.md | 1 + 2 files changed, 11 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 446eb1189ad..7f5da063fd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,16 @@ The channels people will reach out on can be found on the [getting help page](ht Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. +## I want to contribute! + +If you want to contribute to GitLab, but are not sure where to start, +look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs) +with the label `up-for-grabs`. +These issues will be of reasonable size and challenge, for anyone to start +contributing to GitLab. + +This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4). + ## Issue tracker To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/). diff --git a/PROCESS.md b/PROCESS.md index 482ad5fe9e1..72fc3481447 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -44,6 +44,7 @@ Workflow labels are purposely not very detailed since that would be hard to keep - *UX* needs needs help from a UX designer - *Frontend* needs help from a Front-end engineer - *Graphics* needs help from a Graphics designer +- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. -- cgit v1.2.1 From 1d6d757dbd563500671f57f45faa808510a612d1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:25:56 +0100 Subject: Allow reference format as link href --- lib/gitlab/markdown.rb | 3 ++- lib/gitlab/markdown/abstract_reference_filter.rb | 22 ++++++++++------ .../markdown/external_issue_reference_filter.rb | 10 ++++++-- lib/gitlab/markdown/label_reference_filter.rb | 10 ++++++-- lib/gitlab/markdown/reference_filter.rb | 29 +++++++++++++++++++++- lib/gitlab/markdown/relative_link_filter.rb | 3 +++ lib/gitlab/markdown/user_reference_filter.rb | 28 ++++++++++++--------- 7 files changed, 80 insertions(+), 25 deletions(-) diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b082bfc434b..886a09f52af 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -178,7 +178,6 @@ module Gitlab Gitlab::Markdown::SanitizationFilter, Gitlab::Markdown::UploadLinkFilter, - Gitlab::Markdown::RelativeLinkFilter, Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::TableOfContentsFilter, Gitlab::Markdown::AutolinkFilter, @@ -193,6 +192,8 @@ module Gitlab Gitlab::Markdown::CommitReferenceFilter, Gitlab::Markdown::LabelReferenceFilter, + Gitlab::Markdown::RelativeLinkFilter, + Gitlab::Markdown::TaskListFilter ] end diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 37ed423eeda..b044a73ed17 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -2,7 +2,7 @@ require 'gitlab/markdown' module Gitlab module Markdown - # Issues, Snippets, Merge Requests, Commits and Commit Ranges share + # Issues, Merge Requests, Snippets, Commits and Commit Ranges share # similar functionality in refernce filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -64,8 +64,13 @@ module Gitlab object_link_filter(content, object_class.reference_pattern) end - replace_link_nodes_matching(object_class.link_reference_pattern) do |content| - object_link_filter(content, object_class.link_reference_pattern) + replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| + object_link_filter(link, object_class.reference_pattern, link_text: text) + end + + replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| + object_link_filter(text, object_class.link_reference_pattern) + end end end @@ -76,7 +81,7 @@ module Gitlab # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. - def object_link_filter(text, pattern) + def object_link_filter(text, pattern, link_text: nil) references_in(text, pattern) do |match, id, project_ref, matches| project = project_from_ref(project_ref) @@ -88,10 +93,13 @@ module Gitlab url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) - text = object.reference_link_text(context[:project]) + text = link_text + unless text + text = object.reference_link_text(context[:project]) - extras = object_link_text_extras(object, matches) - text += " (#{extras.join(", ")})" if extras.any? + extras = object_link_text_extras(object, matches) + text += " (#{extras.join(", ")})" if extras.any? + end %(#{match}) + class="#{klass}">#{text}) end end diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 618acb7a578..4d0507b607d 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -30,6 +30,10 @@ module Gitlab replace_text_nodes_matching(Label.reference_pattern) do |content| label_link_filter(content) end + + replace_link_nodes_with_href(Label.reference_pattern) do |link, text| + label_link_filter(link, link_text: text) + end end # Replace label references in text with links to the label specified. @@ -38,7 +42,7 @@ module Gitlab # # Returns a String with label references replaced with links. All links # have `gfm` and `gfm-label` class names attached for styling. - def label_link_filter(text) + def label_link_filter(text, link_text: nil) project = context[:project] self.class.references_in(text) do |match, id, name| @@ -49,8 +53,10 @@ module Gitlab klass = reference_class(:label) data = data_attribute(project: project.id, label: label.id) + text = link_text || render_colored_label(label) + %(#{render_colored_label(label)}) + class="#{klass}">#{text}) else match end diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index e52633ee74c..2597784c7ae 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,7 +122,7 @@ module Gitlab doc end - def replace_link_nodes_matching(pattern) + def replace_link_nodes_with_text(pattern) return doc if project.nil? doc.search('a').each do |node| @@ -132,6 +132,9 @@ module Gitlab link = node.attr('href') text = node.text + next unless link && text + + link = URI.decode(link) # Ignore ending punctionation like periods or commas next unless link == text && text =~ /\A#{pattern}/ @@ -145,6 +148,30 @@ module Gitlab doc end + def replace_link_nodes_with_href(pattern) + return doc if project.nil? + + doc.search('a').each do |node| + klass = node.attr('class') + next if klass && klass.include?('gfm') + + link = node.attr('href') + text = node.text + + next unless link && text + link = URI.decode(link) + next unless link && link =~ /\A#{pattern}\z/ + + html = yield link, text + + next if html == link + + node.replace(html) + end + + doc + end + # Ensure that a :project key exists in context # # Note that while the key might exist, its value could be nil! diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb index 632be4d7542..692c51fd324 100644 --- a/lib/gitlab/markdown/relative_link_filter.rb +++ b/lib/gitlab/markdown/relative_link_filter.rb @@ -17,6 +17,9 @@ module Gitlab return doc unless linkable_files? doc.search('a').each do |el| + klass = el.attr('class') + next if klass && klass.include?('gfm') + process_link_attr el.attribute('href') end diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb index ab5e1f6fe9e..0a20d9c0347 100644 --- a/lib/gitlab/markdown/user_reference_filter.rb +++ b/lib/gitlab/markdown/user_reference_filter.rb @@ -52,6 +52,10 @@ module Gitlab replace_text_nodes_matching(User.reference_pattern) do |content| user_link_filter(content) end + + replace_link_nodes_with_href(User.reference_pattern) do |link, text| + user_link_filter(link, link_text: text) + end end # Replace `@user` user references in text with links to the referenced @@ -61,12 +65,12 @@ module Gitlab # # Returns a String with `@user` references replaced with links. All links # have `gfm` and `gfm-project_member` class names attached for styling. - def user_link_filter(text) + def user_link_filter(text, link_text: nil) self.class.references_in(text) do |match, username| if username == 'all' - link_to_all + link_to_all(link_text: link_text) elsif namespace = Namespace.find_by(path: username) - link_to_namespace(namespace) || match + link_to_namespace(namespace, link_text: link_text) || match else match end @@ -83,36 +87,36 @@ module Gitlab reference_class(:project_member) end - def link_to_all + def link_to_all(link_text: nil) project = context[:project] url = urls.namespace_project_url(project.namespace, project, only_path: context[:only_path]) data = data_attribute(project: project.id) - text = User.reference_prefix + 'all' + text = link_text || User.reference_prefix + 'all' link_tag(url, data, text) end - def link_to_namespace(namespace) + def link_to_namespace(namespace, link_text: nil) if namespace.is_a?(Group) - link_to_group(namespace.path, namespace) + link_to_group(namespace.path, namespace, link_text: link_text) else - link_to_user(namespace.path, namespace) + link_to_user(namespace.path, namespace, link_text: link_text) end end - def link_to_group(group, namespace) + def link_to_group(group, namespace, link_text: nil) url = urls.group_url(group, only_path: context[:only_path]) data = data_attribute(group: namespace.id) - text = Group.reference_prefix + group + text = link_text || Group.reference_prefix + group link_tag(url, data, text) end - def link_to_user(user, namespace) + def link_to_user(user, namespace, link_text: nil) url = urls.user_url(user, only_path: context[:only_path]) data = data_attribute(user: namespace.owner_id) - text = User.reference_prefix + user + text = link_text || User.reference_prefix + user link_tag(url, data, text) end -- cgit v1.2.1 From d4030a845eebcb913a7aac1e8fd502706669d0cc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 16:26:05 +0100 Subject: Pick up direct links to issues/MRs as references. --- lib/gitlab/markdown/abstract_reference_filter.rb | 17 +++++- .../markdown/commit_range_reference_filter_spec.rb | 2 +- .../markdown/commit_reference_filter_spec.rb | 2 +- .../gitlab/markdown/issue_reference_filter_spec.rb | 56 ++++++++++++++--- .../gitlab/markdown/label_reference_filter_spec.rb | 71 ++++++++++++++++------ .../merge_request_reference_filter_spec.rb | 2 +- .../markdown/snippet_reference_filter_spec.rb | 2 +- .../gitlab/markdown/user_reference_filter_spec.rb | 51 ++++++++++++---- 8 files changed, 161 insertions(+), 42 deletions(-) diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index b044a73ed17..0ec55c0207e 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -60,17 +60,27 @@ module Gitlab end def call + # `#123` replace_text_nodes_matching(object_class.reference_pattern) do |content| object_link_filter(content, object_class.reference_pattern) end + # `[Issue](#123)`, which is turned into + # `Issue` replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| object_link_filter(link, object_class.reference_pattern, link_text: text) end + # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into + # `http://gitlab.example.com/namespace/project/issues/123` replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| object_link_filter(text, object_class.link_reference_pattern) end + + # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text| + object_link_filter(link, object_class.link_reference_pattern, link_text: text) end end @@ -88,7 +98,12 @@ module Gitlab if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - data = data_attribute(project: project.id, object_sym => object.id, original: match) + + data = data_attribute( + original: link_text || match, + project: project.id, + object_sym => object.id + ) url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) diff --git a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb index 92158382790..9ce63f9af46 100644 --- a/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb @@ -143,7 +143,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:range) { CommitRange.new("#{commit1.id}...master", project) } diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 6fe9b165ff5..78a3603269c 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -132,7 +132,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:commit) { project2.commit } diff --git a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb index 0a741688b82..078ff3ed4b2 100644 --- a/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/issue_reference_filter_spec.rb @@ -136,30 +136,70 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:issue) { create(:issue, project: project2) } let(:reference) { helper.url_for_issue(issue.iid, project2) + "#note_123" } - it 'ignores valid references when cross-reference project uses external tracker' do - expect_any_instance_of(Project).to receive(:get_issue). - with(issue.iid).and_return(nil) + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") - exp = act = "Issue #{reference}" - expect(reference_filter(act).to_html).to match(/#{Regexp.escape(reference)}<\/a>/) + expect(doc.css('a').first.attr('href')). + to eq reference + end + + it 'links with adjacent text' do + doc = reference_filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end + + context 'cross-project reference in link href' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { %Q{Reference} } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')). + to eq helper.url_for_issue(issue.iid, project2) + end + + it 'links with adjacent text' do + doc = reference_filter("Fixed (#{reference}.)") + expect(doc.to_html).to match(/\(Reference<\/a>\.\)/) end + it 'adds to the results hash' do + result = reference_pipeline_result("Fixed #{reference}") + expect(result[:references][:issue]).to eq [issue] + end + end + + context 'cross-project URL in link href' do + let(:namespace) { create(:namespace, name: 'cross-reference') } + let(:project2) { create(:empty_project, :public, namespace: namespace) } + let(:issue) { create(:issue, project: project2) } + let(:reference) { %Q{Reference} } + it 'links to a valid reference' do doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')). - to eq reference + to eq helper.url_for_issue(issue.iid, project2) + "#note_123" end it 'links with adjacent text' do doc = reference_filter("Fixed (#{reference}.)") - expect(doc.to_html).to match(/\(#{Regexp.escape(issue.to_reference(project))} \(comment 123\)<\/a>\.\)/) + expect(doc.to_html).to match(/\(Reference<\/a>\.\)/) end it 'adds to the results hash' do diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index fc21b65a843..ef6dd524aba 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -16,17 +16,17 @@ module Gitlab::Markdown %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Label #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end it 'includes default classes' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-label' end it 'includes a data-project attribute' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-project') @@ -34,7 +34,7 @@ module Gitlab::Markdown end it 'includes a data-label attribute' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-label') @@ -42,7 +42,7 @@ module Gitlab::Markdown end it 'supports an :only_path context' do - doc = filter("Label #{reference}", only_path: true) + doc = reference_filter("Label #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) @@ -56,33 +56,33 @@ module Gitlab::Markdown describe 'label span element' do it 'includes default classes' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a span').first.attr('class')).to eq 'label color-label' end it 'includes a style attribute' do - doc = filter("Label #{reference}") + doc = reference_filter("Label #{reference}") expect(doc.css('a span').first.attr('style')).to match(/\Abackground-color: #\h{6}; color: #\h{6}\z/) end end context 'Integer-based references' do it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label IDs' do exp = act = "Label #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -91,7 +91,7 @@ module Gitlab::Markdown let(:reference) { "#{Label.reference_prefix}#{label.name}" } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) @@ -99,14 +99,14 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label names' do exp = act = "Label #{Label.reference_prefix}#{label.name.reverse}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -115,7 +115,7 @@ module Gitlab::Markdown let(:reference) { label.to_reference(:name) } it 'links to a valid reference' do - doc = filter("See #{reference}") + doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. namespace_project_issues_url(project.namespace, project, label_name: label.name) @@ -123,21 +123,58 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Label (#{reference}.)") + doc = reference_filter("Label (#{reference}.)") expect(doc.to_html).to match(%r(\(#{label.name}\.\))) end it 'ignores invalid label names' do exp = act = %(Label #{Label.reference_prefix}"#{label.name.reverse}") - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end describe 'edge cases' do it 'gracefully handles non-references matching the pattern' do exp = act = '(format nil "~0f" 3.0) ; 3.0' - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp + end + end + + describe 'referencing a label in a link href' do + let(:reference) { %Q{Label} } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_issues_url(project.namespace, project, label_name: label.name) + end + + it 'links with adjacent text' do + doc = reference_filter("Label (#{reference}.)") + expect(doc.to_html).to match(%r(\(Label\.\))) + end + + it 'includes a data-project attribute' do + doc = reference_filter("Label #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-project') + expect(link.attr('data-project')).to eq project.id.to_s + end + + it 'includes a data-label attribute' do + doc = reference_filter("See #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-label') + expect(link.attr('data-label')).to eq label.id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Label #{reference}") + expect(result[:references][:label]).to eq [label] end end end diff --git a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb index cdb3390e793..4a232051127 100644 --- a/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb @@ -117,7 +117,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:project, :public, namespace: namespace) } let(:merge) { create(:merge_request, source_project: project2, target_project: project2) } diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index 73d20957a56..b6f05710c3b 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -115,7 +115,7 @@ module Gitlab::Markdown end end - context 'URL cross-project reference' do + context 'cross-project URL reference' do let(:namespace) { create(:namespace, name: 'cross-reference') } let(:project2) { create(:empty_project, :public, namespace: namespace) } let(:snippet) { create(:project_snippet, project: project2) } diff --git a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb index d9e0d7c42db..25379f0670e 100644 --- a/spec/lib/gitlab/markdown/user_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/user_reference_filter_spec.rb @@ -14,13 +14,13 @@ module Gitlab::Markdown it 'ignores invalid users' do exp = act = "Hey #{invalidate_reference(reference)}" - expect(filter(act).to_html).to eq(exp) + expect(reference_filter(act).to_html).to eq(exp) end %w(pre code a style).each do |elem| it "ignores valid references contained inside '#{elem}' element" do exp = act = "<#{elem}>Hey #{reference}" - expect(filter(act).to_html).to eq exp + expect(reference_filter(act).to_html).to eq exp end end @@ -32,7 +32,7 @@ module Gitlab::Markdown end it 'supports a special @all mention' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').length).to eq 1 expect(doc.css('a').first.attr('href')) .to eq urls.namespace_project_url(project.namespace, project) @@ -46,26 +46,26 @@ module Gitlab::Markdown context 'mentioning a user' do it 'links to a User' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) end it 'links to a User with a period' do user = create(:user, name: 'alphA.Beta') - doc = filter("Hey #{user.to_reference}") + doc = reference_filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end it 'links to a User with an underscore' do user = create(:user, name: 'ping_pong_king') - doc = filter("Hey #{user.to_reference}") + doc = reference_filter("Hey #{user.to_reference}") expect(doc.css('a').length).to eq 1 end it 'includes a data-user attribute' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-user') @@ -83,12 +83,12 @@ module Gitlab::Markdown let(:reference) { group.to_reference } it 'links to the Group' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('href')).to eq urls.group_url(group) end it 'includes a data-group attribute' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") link = doc.css('a').first expect(link).to have_attribute('data-group') @@ -102,21 +102,48 @@ module Gitlab::Markdown end it 'links with adjacent text' do - doc = filter("Mention me (#{reference}.)") + doc = reference_filter("Mention me (#{reference}.)") expect(doc.to_html).to match(/\(#{reference}<\/a>\.\)/) end it 'includes default classes' do - doc = filter("Hey #{reference}") + doc = reference_filter("Hey #{reference}") expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-project_member' end it 'supports an :only_path context' do - doc = filter("Hey #{reference}", only_path: true) + doc = reference_filter("Hey #{reference}", only_path: true) link = doc.css('a').first.attr('href') expect(link).not_to match %r(https?://) expect(link).to eq urls.user_path(user) end + + context 'referencing a user in a link href' do + let(:reference) { %Q{User} } + + it 'links to a User' do + doc = reference_filter("Hey #{reference}") + expect(doc.css('a').first.attr('href')).to eq urls.user_url(user) + end + + it 'links with adjacent text' do + doc = reference_filter("Mention me (#{reference}.)") + expect(doc.to_html).to match(/\(User<\/a>\.\)/) + end + + it 'includes a data-user attribute' do + doc = reference_filter("Hey #{reference}") + link = doc.css('a').first + + expect(link).to have_attribute('data-user') + expect(link.attr('data-user')).to eq user.namespace.owner_id.to_s + end + + it 'adds to the results hash' do + result = reference_pipeline_result("Hey #{reference}") + expect(result[:references][:user]).to eq [user] + end + end end end -- cgit v1.2.1 From c07f0fa735ba0d4cd926e601ebb3a40cfa197e21 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 17:04:32 +0100 Subject: Add new references to markdown feature spec. --- spec/fixtures/markdown.md.erb | 17 +++++++++++++++++ spec/support/matchers/markdown_matchers.rb | 14 +++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 41d12afa9ce..76e733165ca 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -153,6 +153,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Ignores invalid: <%= User.reference_prefix %>fake_user - Ignored in code: `<%= user.to_reference %>` - Ignored in links: [Link to <%= user.to_reference %>](#user-link) +- Link to user by reference: [User](<%= user.to_reference %>) #### IssueReferenceFilter @@ -160,6 +161,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Issue in another project: <%= xissue.to_reference(project) %> - Ignored in code: `<%= issue.to_reference %>` - Ignored in links: [Link to <%= issue.to_reference %>](#issue-link) +- Issue by URL: <%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %> +- Link to issue by reference: [Issue](<%= issue.to_reference %>) +- Link to issue by URL: [Issue](<%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %>) #### MergeRequestReferenceFilter @@ -167,6 +171,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Merge request in another project: <%= xmerge_request.to_reference(project) %> - Ignored in code: `<%= merge_request.to_reference %>` - Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link) +- Merge request by URL: <%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %> +- Link to merge request by reference: [Merge request](<%= merge_request.to_reference %>) +- Link to merge request by URL: [Merge request](<%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %>) #### SnippetReferenceFilter @@ -174,6 +181,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Snippet in another project: <%= xsnippet.to_reference(project) %> - Ignored in code: `<%= snippet.to_reference %>` - Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link) +- Snippet by URL: <%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %> +- Link to snippet by reference: [Snippet](<%= snippet.to_reference %>) +- Link to snippet by URL: [Snippet](<%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %>) #### CommitRangeReferenceFilter @@ -181,6 +191,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Range in another project: <%= xcommit_range.to_reference(project) %> - Ignored in code: `<%= commit_range.to_reference %>` - Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link) +- Range by URL: <%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %> +- Link to range by reference: [Range](<%= commit_range.to_reference %>) +- Link to range by URL: [Range](<%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %>) #### CommitReferenceFilter @@ -188,6 +201,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Commit in another project: <%= xcommit.to_reference(project) %> - Ignored in code: `<%= commit.to_reference %>` - Ignored in links: [Link to <%= commit.to_reference %>](#commit-link) +- Commit by URL: <%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %> +- Link to commit by reference: [Commit](<%= commit.to_reference %>) +- Link to commit by URL: [Commit](<%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %>) #### LabelReferenceFilter @@ -196,6 +212,7 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Label by name in quotes: <%= label.to_reference(:name) %> - Ignored in code: `<%= simple_label.to_reference %>` - Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link) +- Link to label by reference: [Label](<%= label.to_reference %>) ### Task Lists diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 7500d0fdf80..7eadcd58c1f 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -71,7 +71,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-project_member', count: 3) + expect(actual).to have_selector('a.gfm.gfm-project_member', count: 4) end end @@ -80,7 +80,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-issue', count: 3) + expect(actual).to have_selector('a.gfm.gfm-issue', count: 6) end end @@ -89,7 +89,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 3) + expect(actual).to have_selector('a.gfm.gfm-merge_request', count: 6) expect(actual).to have_selector('em a.gfm-merge_request') end end @@ -99,7 +99,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-snippet', count: 2) + expect(actual).to have_selector('a.gfm.gfm-snippet', count: 5) end end @@ -108,7 +108,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 2) + expect(actual).to have_selector('a.gfm.gfm-commit_range', count: 5) end end @@ -117,7 +117,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-commit', count: 2) + expect(actual).to have_selector('a.gfm.gfm-commit', count: 5) end end @@ -126,7 +126,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-label', count: 3) + expect(actual).to have_selector('a.gfm.gfm-label', count: 4) end end -- cgit v1.2.1 From 2f5074dc395c784f91abb3bacd23e75ce080a547 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 1 Dec 2015 17:13:47 +0100 Subject: Expand inline docs. --- lib/gitlab/markdown/abstract_reference_filter.rb | 6 ++++-- lib/gitlab/markdown/reference_filter.rb | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb index 0ec55c0207e..9488e980c08 100644 --- a/lib/gitlab/markdown/abstract_reference_filter.rb +++ b/lib/gitlab/markdown/abstract_reference_filter.rb @@ -3,7 +3,7 @@ require 'gitlab/markdown' module Gitlab module Markdown # Issues, Merge Requests, Snippets, Commits and Commit Ranges share - # similar functionality in refernce filtering. + # similar functionality in reference filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference @@ -88,6 +88,8 @@ module Gitlab # to the referenced object's details page. # # text - String text to replace references in. + # pattern - Reference pattern to match against. + # link_text - Original content of the link being replaced. # # Returns a String with references replaced with links. All links # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. @@ -98,7 +100,7 @@ module Gitlab if project && object = find_object(project, id) title = escape_once(object_link_title(object)) klass = reference_class(object_sym) - + data = data_attribute( original: link_text || match, project: project.id, diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index 2597784c7ae..b6d93e05ec7 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -122,6 +122,18 @@ module Gitlab doc end + # Iterate through the document's link nodes, yielding the current node's + # content if: + # + # * The `project` context value is present AND + # * The node's content matches `pattern` + # + # pattern - Regex pattern against which to match the node's content + # + # Yields the current node's String contents. The result of the block will + # replace the node and update the current document. + # + # Returns the updated Nokogiri::HTML::DocumentFragment object. def replace_link_nodes_with_text(pattern) return doc if project.nil? @@ -148,6 +160,18 @@ module Gitlab doc end + # Iterate through the document's link nodes, yielding the current node's + # content if: + # + # * The `project` context value is present AND + # * The node's HREF matches `pattern` + # + # pattern - Regex pattern against which to match the node's HREF + # + # Yields the current node's String HREF and String content. + # The result of the block will replace the node and update the current document. + # + # Returns the updated Nokogiri::HTML::DocumentFragment object. def replace_link_nodes_with_href(pattern) return doc if project.nil? -- cgit v1.2.1 From 9ab7bdf7739935bf79c2e033212726a4be421a26 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 1 Dec 2015 19:45:58 -0200 Subject: Signed in admin should be able to add/remove himself to a group --- app/models/ability.rb | 6 ++---- features/admin/groups.feature | 16 ++++++++++++++++ features/steps/admin/groups.rb | 29 +++++++++++++++++++++++++++++ features/steps/shared/group.rb | 4 ++++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 07f3a56ec7a..10c41306c55 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -346,12 +346,10 @@ class Ability unless group.last_owner?(target_user) can_manage = group_abilities(user, group).include?(:admin_group_member) - if can_manage && user != target_user + if can_manage rules << :update_group_member rules << :destroy_group_member - end - - if user == target_user + elsif user == target_user rules << :destroy_group_member end end diff --git a/features/admin/groups.feature b/features/admin/groups.feature index 973918086a3..2edb3964f70 100644 --- a/features/admin/groups.feature +++ b/features/admin/groups.feature @@ -33,3 +33,19 @@ Feature: Admin Groups When I visit admin group page When I select user "johndoe@gitlab.com" from user list as "Reporter" Then I should see "johndoe@gitlab.com" in team list in every project as "Reporter" + + @javascript + Scenario: Signed in admin should be able to add himself to a group + Given "John Doe" is owner of group "Owned" + When I visit group "Owned" members page + When I select current user as "Developer" + Then I should see current user as "Developer" + + @javascript + Scenario: Signed in admin should be able to remove himself from group + Given current user is developer of group "Owned" + When I visit group "Owned" members page + Then I should see current user as "Developer" + When I click on the "Remove User From Group" button for current user + When I visit group "Owned" members page + Then I should not see current user as "Developer" diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb index d27634858a2..43fd91d0d4c 100644 --- a/features/steps/admin/groups.rb +++ b/features/steps/admin/groups.rb @@ -1,5 +1,6 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps include SharedAuthentication + include SharedGroup include SharedPaths include SharedUser include SharedActiveTab @@ -88,6 +89,34 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps end end + step 'I select current user as "Developer"' do + page.within ".users-group-form" do + select2(current_user.id, from: "#user_ids", multiple: true) + select "Developer", from: "access_level" + end + + click_button "Add users to group" + end + + step 'I should see current user as "Developer"' do + page.within '.content-list' do + expect(page).to have_content(current_user.name) + expect(page).to have_content('Developer') + end + end + + step 'I click on the "Remove User From Group" button for current user' do + find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I should not see current user as "Developer"' do + page.within '.content-list' do + expect(page).not_to have_content(current_user.name) + expect(page).not_to have_content('Developer') + end + end + protected def current_group diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb index 58581653f28..fe6736dacd4 100644 --- a/features/steps/shared/group.rb +++ b/features/steps/shared/group.rb @@ -1,6 +1,10 @@ module SharedGroup include Spinach::DSL + step 'current user is developer of group "Owned"' do + is_member_of(current_user.name, "Owned", Gitlab::Access::DEVELOPER) + end + step '"John Doe" is owner of group "Owned"' do is_member_of("John Doe", "Owned", Gitlab::Access::OWNER) end -- cgit v1.2.1 From 67cc6b0642573fe443126042dd36b15f05bc539c Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 1 Dec 2015 20:47:26 -0200 Subject: Signed in admin should be able to add/remove himself to a project --- app/models/ability.rb | 6 ++---- features/admin/projects.feature | 16 ++++++++++++++++ features/steps/admin/projects.rb | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/app/models/ability.rb b/app/models/ability.rb index 10c41306c55..cd5ae0fb0fd 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -365,12 +365,10 @@ class Ability unless target_user == project.owner can_manage = project_abilities(user, project).include?(:admin_project_member) - if can_manage && user != target_user + if can_manage rules << :update_project_member rules << :destroy_project_member - end - - if user == target_user + elsif user == target_user rules << :destroy_project_member end end diff --git a/features/admin/projects.feature b/features/admin/projects.feature index f7cec04eb75..c5ee80136c8 100644 --- a/features/admin/projects.feature +++ b/features/admin/projects.feature @@ -27,3 +27,19 @@ Feature: Admin Projects And I visit admin project page When I transfer project to group 'Web' Then I should see project transfered + + @javascript + Scenario: Signed in admin should be able to add himself to a project + Given "John Doe" owns private project "Enterprise" + When I visit project "Enterprise" members page + When I select current user as "Developer" + Then I should see current user as "Developer" + + @javascript + Scenario: Signed in admin should be able to remove himself from a project + Given "John Doe" owns private project "Enterprise" + And current user is developer of project "Enterprise" + When I visit project "Enterprise" members page + Then I should see current user as "Developer" + When I click on the "Remove User From Project" button for current user + Then I should not see current user as "Developer" diff --git a/features/steps/admin/projects.rb b/features/steps/admin/projects.rb index 5a1cc9aa151..a7a28755a6c 100644 --- a/features/steps/admin/projects.rb +++ b/features/steps/admin/projects.rb @@ -3,6 +3,8 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps include SharedPaths include SharedAdmin include SharedProject + include SharedUser + include Select2Helper step 'I should see all non-archived projects' do Project.non_archived.each do |p| @@ -56,6 +58,41 @@ class Spinach::Features::AdminProjects < Spinach::FeatureSteps expect(page).to have_content 'Namespace: Web' end + step 'I visit project "Enterprise" members page' do + project = Project.find_by!(name: "Enterprise") + visit namespace_project_project_members_path(project.namespace, project) + end + + step 'I select current user as "Developer"' do + page.within ".users-project-form" do + select2(current_user.id, from: "#user_ids", multiple: true) + select "Developer", from: "access_level" + end + + click_button "Add users to project" + end + + step 'I should see current user as "Developer"' do + page.within '.content-list' do + expect(page).to have_content(current_user.name) + expect(page).to have_content('Developer') + end + end + + step 'current user is developer of project "Enterprise"' do + project = Project.find_by!(name: "Enterprise") + project.team << [current_user, :developer] + end + + step 'I click on the "Remove User From Project" button for current user' do + find(:css, 'li', text: current_user.name).find(:css, 'a.btn-remove').click + # poltergeist always confirms popups. + end + + step 'I should not see current_user as "Developer"' do + expect(page).not_to have_selector(:css, '.content-list') + end + def project @project ||= Project.first end -- cgit v1.2.1 From 8034b61b52ed9e54366ee9d73d17a4ea1c00de44 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 1 Dec 2015 20:58:31 -0200 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..c088c89d861 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.2.2 - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - Fix: Raw private snippets access workflow - Prevent "413 Request entity too large" errors when pushing large files with LFS + - Fix: As an admin, cannot add oneself as a member to a group/project v 8.2.1 - Forcefully update builds that didn't want to update with state machine -- cgit v1.2.1 From 9f30f5c63515709adce95eef166e96ea91811e36 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 18:19:33 -0500 Subject: Make icon-link image transparent --- app/assets/images/icon-link.png | Bin 726 -> 1128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png index 60021d5ac47..7d89da97e11 100644 Binary files a/app/assets/images/icon-link.png and b/app/assets/images/icon-link.png differ -- cgit v1.2.1 From e6dadea3891358ac7ed34dbb31eac403c193fcdc Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Wed, 2 Dec 2015 08:50:00 +0200 Subject: git rid of deprecated warnings --- app/models/user.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index e1144ca77be..15aab315649 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -794,4 +794,9 @@ class User < ActiveRecord::Base Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id), other.select(:id)]) end + + # Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration + def send_devise_notification(notification, *args) + devise_mailer.send(notification, self, *args).deliver_later + end end -- cgit v1.2.1 From 9aac53bcee8f5fc99ec84036d688801d987959ea Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:54:24 +0100 Subject: Satisfy Rubocop --- app/models/commit_range.rb | 2 +- spec/lib/gitlab/markdown/commit_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb index b8bf36b32ce..14e7971fa06 100644 --- a/app/models/commit_range.rb +++ b/app/models/commit_range.rb @@ -93,7 +93,7 @@ class CommitRange def to_reference(from_project = nil) if cross_project_reference?(from_project) - reference = project.to_reference + self.class.reference_prefix + self.id + project.to_reference + self.class.reference_prefix + self.id else self.id end diff --git a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb index 78a3603269c..462a41b4756 100644 --- a/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/commit_reference_filter_spec.rb @@ -152,7 +152,7 @@ module Gitlab::Markdown end it 'ignores invalid commit IDs on the referenced project' do - exp = act = "Committed #{invalidate_reference(reference)}" + act = "Committed #{invalidate_reference(reference)}" expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end diff --git a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb index b6f05710c3b..3a9acc9d6d4 100644 --- a/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb @@ -134,7 +134,7 @@ module Gitlab::Markdown end it 'ignores invalid snippet IDs on the referenced project' do - exp = act = "See #{invalidate_reference(reference)}" + act = "See #{invalidate_reference(reference)}" expect(reference_filter(act).to_html).to match(/#{Regexp.escape(invalidate_reference(reference))}<\/a>/) end -- cgit v1.2.1 From a8e463c8aca571ede3691c98f7f3990d3d880d0b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:56:05 +0100 Subject: Don't show project fork event as imported --- CHANGELOG | 1 + app/models/event.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..8fbe1e6ff14 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Don't show project fork event as "imported" v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/models/event.rb b/app/models/event.rb index 9afd223bce5..01d008035a5 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -201,7 +201,7 @@ class Event < ActiveRecord::Base elsif commented? "commented on" elsif created_project? - if project.import? + if project.external_import? "imported" else "created" -- cgit v1.2.1 From edc37c25204d7a4446d15eac94e6e1d92d613ed9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 10:56:39 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 090b54f41a4..01e7e8d20bd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Fix 500 error when update group member permission - Fix: Raw private snippets access workflow - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) + - Recognize issue/MR/snippet/commit links as references v 8.2.1 - Forcefully update builds that didn't want to update with state machine -- cgit v1.2.1 From 927a4576c6e0bff2c9a41e538efcd2c7691a6a74 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 2 Dec 2015 13:26:49 +0100 Subject: The Procfile is for development only --- Procfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Procfile b/Procfile index fd5f7ecb94b..2e41485677c 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,7 @@ +# For DEVELOPMENT only. Production uses Runit in +# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in +# lib/support/init.d, which call scripts in bin/ . +# web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml -- cgit v1.2.1 From a7682f8775a4609ac8c70151ffe8f3ccf3b767b6 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Tue, 24 Nov 2015 14:59:02 +0100 Subject: Specs for 'Merge When Build Succeeds' --- .gitignore | 1 + app/models/merge_request.rb | 13 ++-- .../merge_when_build_succeeds_service.rb | 6 +- app/services/system_note_service.rb | 2 +- .../merge_requests/widget/_merged.html.haml | 4 +- .../merge_requests/widget/open/_accept.html.haml | 4 +- .../open/_merge_when_build_succeeds.html.haml | 21 +++--- lib/api/entities.rb | 1 + lib/api/merge_requests.rb | 22 +++--- spec/factories/ci/commits.rb | 23 ++++--- spec/factories/commit_statuses.rb | 2 +- .../merge_when_build_succeeds_spec.rb | 80 ++++++++++++++++++++++ spec/models/merge_request_spec.rb | 24 +++++++ spec/requests/api/merge_requests_spec.rb | 17 ++++- spec/services/merge_requests/merge_service_spec.rb | 8 +-- .../merge_when_build_succeeds_service_spec.rb | 78 +++++++++++++++++++++ .../merge_requests/refresh_service_spec.rb | 5 +- spec/services/system_note_service_spec.rb | 22 ++++-- 18 files changed, 268 insertions(+), 65 deletions(-) create mode 100644 spec/features/merge_requests/merge_when_build_succeeds_spec.rb create mode 100644 spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb diff --git a/.gitignore b/.gitignore index f5b6427ca03..881b3fb81ac 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ public/assets/ public/uploads.* public/uploads/ shared/artifacts/ +TODO rails_best_practices_output.html /tags tmp/ diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 89f9e8fa6a8..131dfda6b5f 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -254,15 +254,14 @@ class MergeRequest < ActiveRecord::Base end end - def can_cancel_merge_when_build_succeeds?(user) - can_be_merged_by?(user) || self.author == user + def can_cancel_merge_when_build_succeeds?(current_user) + can_be_merged_by?(current_user) || self.author == current_user end - def can_remove_source_branch? - for_fork? && - !project.protected_branch(source_branch) && - !project.repository.root_ref(source_branch) && - can?(current_user, :push_code, project) + def can_remove_source_branch?(current_user) + !source_project.protected_branch?(source_branch) && + !source_project.root_ref?(source_branch) && + Ability.abilities.allowed?(current_user, :push_code, source_project) end def mr_and_commit_notes diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb index 15dcace5dfb..2f101e53a3f 100644 --- a/app/services/merge_requests/merge_when_build_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb @@ -11,13 +11,11 @@ module MergeRequests unless already_approved merge_request.merge_when_build_succeeds = true merge_request.merge_user = @current_user - end - - merge_request.save - unless already_approved SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.ci_commit) end + + merge_request.save end # Triggers the automatic merge of merge_request once the build succeeds diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7de4221c4c4..ed557fef814 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -132,7 +132,7 @@ class SystemNoteService # Called when 'merge when build succeeds' is executed def self.merge_when_build_succeeds(noteable, project, author, ci_commit) - body = "Approved an automatic merge when the build for #{ci_commit.sha} succeeds" + body = "Enabled an automatic merge when the build for #{ci_commit.sha} succeeds" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index a788fcea23f..52e34ee617e 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -13,7 +13,7 @@ %span.label-branch= @merge_request.target_branch The source branch has been removed. - - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) + - elsif @merge_request.can_remove_source_branch?(current_user) .remove_source_branch_widget %p = succeed '.' do @@ -48,5 +48,3 @@ $('.remove_source_branch_in_progress').hide(); $('.remove_source_branch_widget.failed').show(); }); - - diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 5ec623b472c..279e2ec91f8 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -7,13 +7,13 @@ - ci_commit = @merge_request.ci_commit - if ci_commit && ci_commit.active? = f.button class: "btn btn-create btn-grouped merge_when_build_succeeds", name: "merge_when_build_succeeds" do - Merge when Build Succeeds + Merge When Build Succeeds = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do Accept Merge Request Now - else = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do Accept Merge Request - - if @merge_request.can_remove_source_branch? + - if @merge_request.can_remove_source_branch?(current_user) .accept-control.checkbox = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do = check_box_tag :should_remove_source_branch diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index 51e18f84424..43ba49c5a5e 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -3,22 +3,25 @@ to be merged automatically when #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds. %div - - if @merge_request.merge_params["should_remove_source_branch"].present? + - source_branch_removed = @merge_request.merge_params["should_remove_source_branch"].present? + - if source_branch_removed = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch The source branch will be removed. + - else %p = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch The source branch will not be removed. - .clearfix.prepend-top-10 - - if @merge_request.can_remove_source_branch? && !@merge_request.merge_params["should_remove_source_branch"].present? - = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do - = icon('times') - Remove Source Branch When Merged - - if @merge_request.merge_when_build_succeeds - = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do - Cancel Automatic Merge + - if (@merge_request.can_remove_source_branch?(current_user) && !source_branch_removed) || @merge_request.can_cancel_merge_when_build_succeeds?(current_user) + .clearfix.prepend-top-10 + - if @merge_request.can_remove_source_branch?(current_user) && !source_branch_removed + = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do + = icon('times') + Remove Source Branch When Merged + - if @merge_request.can_cancel_merge_when_build_succeeds?(current_user) + = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do + Cancel Automatic Merge diff --git a/lib/api/entities.rb b/lib/api/entities.rb index d6aec03d7f5..6f9f71b0945 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -169,6 +169,7 @@ module API expose :description expose :work_in_progress?, as: :work_in_progress expose :milestone, using: Entities::Milestone + expose :merge_when_build_succeeds end class MergeRequestChanges < MergeRequest diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index b72f816932b..32cb1137ef7 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -179,11 +179,11 @@ module API # Merge MR # # Parameters: - # id (required) - The ID of a project - # merge_request_id (required) - ID of MR - # merge_commit_message (optional) - Custom merge commit message - # should_remove_source_branch - When true, the source branch will be deleted if possible - # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds + # id (required) - The ID of a project + # merge_request_id (required) - ID of MR + # merge_commit_message (optional) - Custom merge commit message + # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible + # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds # Example: # PUT /projects/:id/merge_request/:merge_request_id/merge # @@ -193,12 +193,11 @@ module API # Merge request can not be merged # because user dont have permissions to push into target branch unauthorized! unless merge_request.can_be_merged_by?(current_user) - - not_allowed! unless merge_request.open? && !merge_request.work_in_progress? + not_allowed! if !merge_request.open? || merge_request.work_in_progress? merge_request.check_if_can_be_merged if merge_request.unchecked? - render_api_error!('Branch cannot be merged', 406) if merge_request.can_be_merged? + render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged? merge_params = { commit_message: params[:merge_commit_message], @@ -206,7 +205,7 @@ module API } if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active? - ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). + ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params). execute(merge_request) else ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params). @@ -224,11 +223,6 @@ module API post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do merge_request = user_project.merge_requests.find(params[:merge_request_id]) - allowed = ::Gitlab::GitAccess.new(current_user, user_project). - can_push_to_branch?(merge_request.target_branch) - - # Merge request can not be merged - # because user dont have permissions to push into target branch unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user) ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request) diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index 79e000b7ccb..70e3fa319c6 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -2,17 +2,18 @@ # # Table name: commits # -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# gl_project_id :integer # # Read about factories at https://github.com/thoughtbot/factory_girl diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index 52de437052d..8898b71e2a3 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -5,7 +5,7 @@ FactoryGirl.define do name 'default' status 'success' description 'commit status' - commit factory: :ci_commit + commit factory: :ci_commit_with_one_job factory :generic_commit_status, class: GenericCommitStatus do name 'generic' diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb new file mode 100644 index 00000000000..b25a3f05e29 --- /dev/null +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +feature 'Merge When Build Succeeds', feature: true, js: true do + let(:user) { create(:user) } + + let(:project) { create(:project, :public) } + let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user, title: "Bug NS-04") } + + before do + project.team << [user, :master] + project.enable_ci + end + + context "Active build for Merge Request" do + before do + ci_commit = create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) + ci_build = create(:ci_build, commit: ci_commit) + + login_as user + visit_merge_request(merge_request) + end + + it 'displays the Merge When Build Succeeds button' do + expect(page).to have_button "Merge When Build Succeeds" + end + + context "Merge When Build succeeds enabled" do + before do + click_button "Merge When Build Succeeds" + end + + it 'activates Merge When Build Succeeds feature' do + expect(page).to have_link "Cancel Automatic Merge" + + expect(page).to have_content "Approved by #{user.name} to be merged automatically when the build succeeds." + expect(page).to have_content "The source branch will not be removed." + end + end + end + + context 'When it is enabled' do + # No clue how, but push a new commit to the branch + let(:merge_request) { create(:merge_request_with_diffs, source_project: project, # source_branch: "mepmep", + author: user, title: "Bug NS-04", merge_when_build_succeeds: true) } + + before do + merge_request.source_project.team << [user, :master] + merge_request.source_branch = "feature" + merge_request.target_branch = "master" + merge_request.save! + + ci_commit = create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) + ci_build = create(:ci_build, commit: ci_commit) + + login_as user + visit_merge_request(merge_request) + end + + it 'cancels the automatic merge' do + click_link "Cancel Automatic Merge" + + expect(page).to have_button "Merge When Build Succeeds" + end + + it "allows the user to remove the source branch" do + expect(page).to have_link "Remove Source Branch When Merged" + end + end + + context 'Build is not active' do + it "should not allow for enabling" do + visit_merge_request(merge_request) + expect(page).not_to have_button "Merge When Build Succeeds" + end + end + + def visit_merge_request(merge_request) + visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) + end +end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 1bd09a1b0fb..c7a9765825e 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -174,6 +174,30 @@ describe MergeRequest do end end + describe '#can_remove_source_branch' do + let(:user) { build(:user)} + + before do + subject.source_project.team << [user, :master] + end + + it "cant be merged when its a a protected branch" do + subject.source_project.protected_branches = []; + + expect(subject.can_remove_source_branch?(user)).to be_falsey + end + + it "cant remove a root ref" do + subject.source_branch = "master"; + + expect(subject.can_remove_source_branch?(user)).to be_falsey + end + + it "is truthy in all other cases" do + expect(subject.can_remove_source_branch?(user)) + end + end + describe "#reset_merge_when_build_succeeds" do let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true } it "sets the item to false" do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index a68c7b1e461..91ae2059e95 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -303,19 +303,21 @@ describe API::API, api: true do end describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do + let (:ci_commit) { create(:ci_commit_without_jobs) } + it "should return merge_request in case of success" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) expect(response.status).to eq(200) end - it "should return 405 if branch can't be merged" do + it "should return 406 if branch can't be merged" do allow_any_instance_of(MergeRequest). to receive(:can_be_merged?).and_return(false) put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) - expect(response.status).to eq(405) + expect(response.status).to eq(406) expect(json_response['message']).to eq('Branch cannot be merged') end @@ -340,6 +342,17 @@ describe API::API, api: true do expect(response.status).to eq(401) expect(json_response['message']).to eq('401 Unauthorized') end + + it "enables merge when build succeeds if the ci is active" do + allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit) + allow(ci_commit).to receive(:active?).and_return(true) + + put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user), merge_when_build_succeeds: true + + expect(response.status).to eq(200) + expect(json_response['title']).to eq('Test') + expect(json_response['merge_when_build_succeeds']).to eq(true) + end end describe "PUT /projects/:id/merge_request/:merge_request_id" do diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index 7483f51de03..242524286fa 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -13,12 +13,12 @@ describe MergeRequests::MergeService do describe :execute do context 'valid params' do - let(:service) { MergeRequests::MergeService.new(project, user, {}) } + let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } before do allow(service).to receive(:execute_hooks) - service.execute(merge_request, 'Awesome message') + service.execute(merge_request) end it { expect(merge_request).to be_valid } @@ -37,14 +37,14 @@ describe MergeRequests::MergeService do end context "error handling" do - let(:service) { MergeRequests::MergeService.new(project, user, {}) } + let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') } it 'saves error if there is an exception' do allow(service).to receive(:repository).and_raise("error") allow(service).to receive(:execute_hooks) - service.execute(merge_request, 'Awesome message') + service.execute(merge_request) expect(merge_request.merge_error).to eq("Something went wrong during merge") end diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb new file mode 100644 index 00000000000..8638539173b --- /dev/null +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -0,0 +1,78 @@ +require 'spec_helper' + +describe MergeRequests::MergeWhenBuildSucceedsService do + let(:user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:mr_merge_if_green_enabled) { create(:merge_request, merge_when_build_succeeds: true, + source_branch: "source_branch", target_branch: project.default_branch, + source_project: project, target_project: project, state: "opened") } + let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch) } + let(:project) { create(:project) } + let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') } + + before do + project.ci_commits = [ci_commit] + project.save! + end + describe "#execute" do + context 'first time enabling' do + before do + allow(merge_request).to receive(:ci_commit).and_return(ci_commit) + end + + it 'sets the params, merge_user, and flag' do + service.execute(merge_request) + + expect(merge_request).to be_valid + expect(merge_request.merge_when_build_succeeds).to be_truthy + expect(merge_request.merge_params).to eq commit_message: 'Awesome message' + expect(merge_request.merge_user).to be user + + note = merge_request.notes.last + expect(note.note).to include 'Enabled an automatic merge when the build for' + end + end + + context 'allready approved' do + let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, new_key: true) } + let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) } + + before do + allow(mr_merge_if_green_enabled).to receive(:ci_commit).and_return(ci_commit) + allow(mr_merge_if_green_enabled).to receive(:mergeable?).and_return(true) + allow(ci_commit).to receive(:success?).and_return(true) + end + + it 'updates the merge params' do + expect(SystemNoteService).not_to receive(:merge_when_build_succeeds) + + service.execute(mr_merge_if_green_enabled) + expect(mr_merge_if_green_enabled.merge_params).to have_key(:new_key) + end + end + end + + describe "#trigger" do + let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch, status: "success") } + + it "merges all merge requests with merge when build succeeds enabled" do + allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit) + allow(ci_commit).to receive(:success?).and_return(true) + + expect(MergeWorker).to receive(:perform_async) + service.trigger(build) + end + end + + describe "#cancel" do + before do + service.cancel(mr_merge_if_green_enabled) + end + + it "resets all the merge_when_build_succeeds params" do + expect(mr_merge_if_green_enabled.merge_when_build_succeeds).to be_falsey + expect(mr_merge_if_green_enabled.merge_params).to eq({}) + expect(mr_merge_if_green_enabled.merge_user).to be nil + end + end +end diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 7ee4488521d..18b2659c1f6 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -17,7 +17,8 @@ describe MergeRequests::RefreshService do source_project: @project, source_branch: 'master', target_branch: 'feature', - target_project: @project) + target_project: @project, + merge_when_build_succeeds: true) @fork_merge_request = create(:merge_request, source_project: @fork_project, @@ -46,6 +47,7 @@ describe MergeRequests::RefreshService do it { expect(@merge_request.notes).not_to be_empty } it { expect(@merge_request).to be_open } + it { expect(@merge_request.merge_when_build_succeeds).to be_falsey} it { expect(@fork_merge_request).to be_open } it { expect(@fork_merge_request.notes).to be_empty } end @@ -146,6 +148,7 @@ describe MergeRequests::RefreshService do end end + def reload_mrs @merge_request.reload @fork_merge_request.reload diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 35912ece644..5d41a5cdc69 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -208,18 +208,28 @@ describe SystemNoteService do end describe '.merge_when_build_succeeds' do - let(:ci_commit) { create :ci_commit, gl_project: project } - let(:merge_request) { create :merge_request, project: project } + let(:ci_commit) { create :ci_commit_without_jobs } + let(:noteable) { create :merge_request } - subject { described_class.merge_when_build_succeeds(merge_request, project, author) } + subject { described_class.merge_when_build_succeeds(noteable, project, author, ci_commit) } it_behaves_like 'a system note' it "posts the Merge When Build Succeeds system note" do - allow(merge_request).to receive(:ci_commit).and_return(ci_commit) - allow(ci_commit).to receive(:short_sha).and_return('12345678') + expect(subject.note).to eq "Enabled an automatic merge when the build for 97de212e80737a608d939f648d959671fb0a0142 succeeds" + end + end + + describe '.cancel_merge_when_build_succeeds' do + let(:ci_commit) { create :ci_commit_without_jobs } + let(:noteable) { create :merge_request } + + subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) } - expect(subject.note).to eq "This merge request will be automatically merged when the build for 12345678 succeeds" + it_behaves_like 'a system note' + + it "posts the Merge When Build Succeeds system note" do + expect(subject.note).to eq "Canceled the automatic merge" end end -- cgit v1.2.1 From 25907ebe476a24bfdd2c451f18227d4fcf314b07 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 2 Dec 2015 13:28:21 +0100 Subject: Doc feature Merge When Build Succeeds --- .../disable_merge_when_build_succeeds.png | Bin 0 -> 20551 bytes .../enable_merge_when_build_succeeds.png | Bin 0 -> 13150 bytes doc/workflow/merge_when_build_succeeds.md | 20 ++++++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 doc/workflow/merge_requests/disable_merge_when_build_succeeds.png create mode 100644 doc/workflow/merge_requests/enable_merge_when_build_succeeds.png create mode 100644 doc/workflow/merge_when_build_succeeds.md diff --git a/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png b/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png new file mode 100644 index 00000000000..a45a4890b62 Binary files /dev/null and b/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png differ diff --git a/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png b/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png new file mode 100644 index 00000000000..62a46c9508b Binary files /dev/null and b/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png differ diff --git a/doc/workflow/merge_when_build_succeeds.md b/doc/workflow/merge_when_build_succeeds.md new file mode 100644 index 00000000000..9bf6ddcc569 --- /dev/null +++ b/doc/workflow/merge_when_build_succeeds.md @@ -0,0 +1,20 @@ +# Merge When Build Succeeds + +Select a Merge Request to be merged if the build succeeds so the user does not have to wait for the build to finish and revisit the Merge Request to merge it after the build is done. + +## Enabling for a Merge Request + +Given an active build for a Merge Request, thus pending or running, a `Merge When Build Succeeds` button will appear to any user which can merge it. Once clicked, it ensures this merge request is merged when the build is successful. +When clicking the button, the merge parameters are also saved to allow the merge user to edit the commit message and remove the source branch if he can remove that branch. + +When this feature is enabled, a message will appear to notify other users. Also a note is posted on the thread. + +![Enable Merge When Build Succceeds](merge_requests/enable_merge_when_build_succeeds.png) + +## Canceling + +The automatic merge can be disabled by clicking the `Cancel Automatic Merge` button, or when a new commit is added to the Merge Request. In the former case a note is posted. In the latter case a user able to merge can enable the feature again. + +![Disable the automatic merge](merge_requests/disable_merge_when_build_succeeds.png) + +A failed build does not reset the automatic build so a build can be retried. -- cgit v1.2.1 From 1519553b9cfbf3054c624ff86ef1c07571152cb9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 2 Dec 2015 13:40:05 +0100 Subject: Add cover-block to UI framework Signed-off-by: Dmitriy Zaporozhets --- app/views/help/ui.html.haml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 2169a821fb2..8a60d6852be 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -34,8 +34,6 @@ %h3 %code .gray-content-block - - .gray-content-block.middle-block %h4 Normal block inside content = lorem @@ -45,6 +43,25 @@ = lorem + %h3 + %code .cover-block + + .cover-block + .avatar-holder + = image_tag avatar_icon('admin@example.com', 90), class: "avatar s90", alt: '' + .cover-title + John Smith + + .cover-desc + = lorem + + .cover-controls + = link_to '#', class: 'btn btn-gray' do + = icon('pencil') +   + = link_to '#', class: 'btn btn-gray' do + = icon('rss') + %h2#lists Lists %h3 @@ -258,7 +275,6 @@ .file-contenta.code = render 'shared/file_highlight', blob: blob - %h2#markdown Markdown %h3 %code .md or .wiki and others -- cgit v1.2.1 From 4d67a2909f46d807c3586a74938d6f7619429808 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:15 +0100 Subject: Use pointer cursor in award emoji selector --- app/assets/stylesheets/pages/issuable.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3a08ee70bc7..848ad7dac84 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -157,6 +157,7 @@ min-width: 214px; > li { + cursor: pointer; margin: 5px; } } -- cgit v1.2.1 From b7cac5a8dae03a1c870f58d22f51e156e62b0965 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:21 +0100 Subject: Use full names in emoji tooltip --- app/helpers/issues_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..25befd654d4 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -96,7 +96,7 @@ module IssuesHelper def emoji_author_list(notes, current_user) list = notes.map do |note| - note.author == current_user ? "me" : note.author.username + note.author == current_user ? "me" : note.author.name end list.join(", ") -- cgit v1.2.1 From 275c2a3161ac4d5638b8fa39a7355bbd9fc3cf46 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 13:59:42 +0100 Subject: Use new style for wiki --- app/assets/stylesheets/pages/wiki.scss | 5 +++++ app/views/projects/wikis/_form.html.haml | 16 +++++++-------- app/views/projects/wikis/_main_links.html.haml | 11 ++++------- app/views/projects/wikis/_nav.html.haml | 25 ++++++++++++++++-------- app/views/projects/wikis/_new.html.haml | 4 ++-- app/views/projects/wikis/edit.html.haml | 24 +++++++++++------------ app/views/projects/wikis/git_access.html.haml | 2 +- app/views/projects/wikis/pages.html.haml | 7 +++---- app/views/projects/wikis/show.html.haml | 12 ++++-------- features/steps/project/source/markdown_render.rb | 2 +- features/steps/project/wiki.rb | 12 ++++++------ 11 files changed, 62 insertions(+), 58 deletions(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index dfaeba41cf6..cdf514197cb 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -4,3 +4,8 @@ margin-right: auto; padding-right: 7px; } + +.wiki-last-edit-by { + font-size: 80%; + font-weight: normal; +} diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml index 9c94c43e747..1d257818dcd 100644 --- a/app/views/projects/wikis/_form.html.haml +++ b/app/views/projects/wikis/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f| += form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default' } do |f| -if @page.errors.any? #error_explanation .alert.alert-danger @@ -11,14 +11,7 @@ .col-sm-10 = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control" - .row - .col-sm-offset-2.col-sm-10 - %p.cgray - To link to a (new) page you can just type - %code [Link Title](page-slug) - \. - - .form-group.wiki-content + .form-group = f.label :content, class: 'control-label' .col-sm-10 = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do @@ -27,6 +20,11 @@ .clearfix .error-alert + + .help-block + To link to a (new) page, simply type + %code [Link Title](page-slug) + \. .form-group = f.label :commit_message, class: 'control-label' .col-sm-10= f.text_field :message, class: 'form-control', rows: 18 diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml index 14f25822259..29bf5d62abe 100644 --- a/app/views/projects/wikis/_main_links.html.haml +++ b/app/views/projects/wikis/_main_links.html.haml @@ -1,9 +1,4 @@ %span.pull-right - - if can?(current_user, :create_wiki, @project) - = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new btn-grouped", "data-toggle" => "modal" do - %i.fa.fa-plus - New Page - - if (@page && @page.persisted?) = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do Page History @@ -11,5 +6,7 @@ = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o Edit - -= render 'projects/wikis/new' + - if can?(current_user, :admin_wiki, @project) + = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do + = icon('trash') + Delete diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml index fffb4eb31ab..e6e6ad5bc4b 100644 --- a/app/views/projects/wikis/_nav.html.haml +++ b/app/views/projects/wikis/_nav.html.haml @@ -1,10 +1,19 @@ -%ul.center-top-menu - = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do - = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) +.project-issuable-filter + .controls + - if can?(current_user, :create_wiki, @project) + = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do + %i.fa.fa-plus + New Page - = nav_link(path: 'wikis#pages') do - = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project) + = render 'projects/wikis/new' - = nav_link(path: 'wikis#git_access') do - = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do - Git Access + %ul.center-top-menu + = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do + = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home) + + = nav_link(path: 'wikis#pages') do + = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project) + + = nav_link(path: 'wikis#git_access') do + = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do + Git Access diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index dace172438c..f0547e9c057 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -12,5 +12,5 @@ The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and / %p.hint Please don't use spaces. - .modal-footer - = link_to 'Build', '#', class: 'build-new-wiki btn btn-create' + .form-actions + = link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create' diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 0b709c3695b..23f64fbbd10 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,16 +1,16 @@ -- page_title "Edit", @page.title, "Wiki" +- page_title "Edit", @page.title.capitalize, "Wiki" = render "header_title" = render 'nav' -.pull-right - = render 'main_links' -%h3.page-title - Editing - - %span.light #{@page.title} -%hr -= render 'form' +.gray-content-block + .pull-right + = render 'main_links' + + %h3.page-title.oneline + %span.light Edit Page + - if @page.persisted? + = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page) + - else + = @page.title -.pull-right - - if @page.persisted? && can?(current_user, :admin_wiki, @project) - = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-sm btn-remove" do - Delete this page += render 'form' diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index 6417ef4a38b..11c8c4f0eba 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -5,7 +5,7 @@ .gray-content-block .row .col-sm-6 - %h3.page-title + %h3.page-title.oneline Git access for %strong= @project_wiki.path_with_namespace diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index d179a1abec1..aae1ad69ad9 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -1,11 +1,10 @@ -- page_title "All Pages", "Wiki" +- page_title "Pages", "Wiki" = render "header_title" = render 'nav' .gray-content-block - = render 'main_links' - %h3.page-title - All Pages + All pages in this wiki are listed below. + %ul.content-list - @wiki_pages.each do |wiki_page| %li diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 55fbf5a8b6e..309d40f52bc 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -5,11 +5,12 @@ .gray-content-block = render 'main_links' - %h3.page-title + %h3.page-title.oneline = @page.title.capitalize - .wiki-last-edit-by - Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} + %span.wiki-last-edit-by + · + last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} - if @page.historical? .warning_message @@ -21,8 +22,3 @@ .wiki = preserve do = render_wiki_content(@page) - -.gray-content-block.footer-block - .wiki-last-edit-by - Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} - diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb index c78e86fa1a7..ec88c8c20c8 100644 --- a/features/steps/project/source/markdown_render.rb +++ b/features/steps/project/source/markdown_render.rb @@ -238,7 +238,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see new wiki page named test' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "test") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page test" end When 'I go back to wiki page home' do diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 02207dbffa6..935c1ca8a2e 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -5,7 +5,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps include SharedPaths step 'I click on the Cancel button' do - page.within(:css, ".form-actions") do + page.within(:css, ".wiki-form .form-actions") do click_on "Cancel" end end @@ -24,7 +24,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps expect(page).to have_content "link test" click_link "link test" - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page" end step 'I have an existing Wiki page' do @@ -68,7 +68,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I click on the "Delete this page" button' do - click_on "Delete this page" + click_on "Delete" end step 'The page should be deleted' do @@ -120,13 +120,13 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps step 'I should see the new wiki page form' do expect(current_path).to match('wikis/image.jpg') expect(page).to have_content('New Wiki Page') - expect(page).to have_content('Editing - image.jpg') + expect(page).to have_content('Edit Page image.jpg') end step 'I create a New page with paths' do click_on 'New Page' fill_in 'Page slug', with: 'one/two/three' - click_on 'Build' + click_on 'Create Page' fill_in "wiki_content", with: 'wiki content' click_on "Create page" expect(current_path).to include 'one/two/three' @@ -135,7 +135,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps step 'I create a New page with an invalid name' do click_on 'New Page' fill_in 'Page slug', with: 'invalid name' - click_on 'Build' + click_on 'Create Page' end step 'I should see an error message' do -- cgit v1.2.1 From b66694d236f71474054c63b1e8a683abacf7fdc3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:00:54 +0100 Subject: Add "New X" link to dashboard/group milestone project-specific issue/MR panels --- app/views/shared/_issues.html.haml | 7 ++++--- app/views/shared/_merge_requests.html.haml | 7 +++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml index 0dbb6a04393..4b4c9e9eabe 100644 --- a/app/views/shared/_issues.html.haml +++ b/app/views/shared/_issues.html.haml @@ -3,8 +3,10 @@ .panel.panel-default.panel-small - project = group[0] .panel-heading - = link_to_project project - = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right' + = link_to project.name_with_namespace, namespace_project_issues_path(project.namespace, project) + - if can?(current_user, :create_issue, project) + .pull-right + = link_to 'New issue', new_namespace_project_issue_path(project.namespace, project) %ul.well-list.issues-list - group[1].each do |issue| @@ -12,4 +14,3 @@ = paginate @issues, theme: "gitlab" - else .nothing-here-block No issues to show - diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml index c02c5af008a..be17a511b26 100644 --- a/app/views/shared/_merge_requests.html.haml +++ b/app/views/shared/_merge_requests.html.haml @@ -3,8 +3,11 @@ .panel.panel-default.panel-small - project = group[0] .panel-heading - = link_to_project project - = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right' + = link_to project.name_with_namespace, namespace_project_merge_requests_path(project.namespace, project) + - if can?(current_user, :create_merge_request, project) + .pull-right + = link_to 'New merge request', new_namespace_project_merge_request_path(project.namespace, project) + %ul.well-list.mr-list - group[1].each do |merge_request| = render 'projects/merge_requests/merge_request', merge_request: merge_request -- cgit v1.2.1 From 0833057494dcf84c789fee8a1dd2c3f3f541d036 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:01:20 +0100 Subject: Use new style for milestone detail page --- app/views/dashboard/milestones/show.html.haml | 52 ++++++++---- app/views/groups/milestones/show.html.haml | 61 +++++++++----- app/views/projects/milestones/show.html.haml | 113 +++++++++++++++----------- 3 files changed, 140 insertions(+), 86 deletions(-) diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 83077a398bd..3536bbeaf4b 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -1,19 +1,23 @@ - page_title @milestone.title, "Milestones" -%h4.page-title - .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - Milestone #{@milestone.title} +- header_title "Milestones", dashboard_milestones_path + +.issuable-details + .page-title + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? + Closed + - else + Open + Milestone #{@milestone.title} + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) -%hr - if @milestone.complete? && @milestone.active? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close the milestone now. -.description - .table-holder %table.table %thead @@ -44,7 +48,7 @@ #{@milestone.open_items_count} open = milestone_progress_bar(@milestone) -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -58,25 +62,39 @@ Participants %span.badge= @milestone.participants.count - .pull-right - = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-6 = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-6 = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone %ul.bordered-list - @milestone.participants.each do |user| %li diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index d161259e4aa..3c1d8815013 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,27 +1,29 @@ - page_title @milestone.title, "Milestones" = render "header_title" -%h4.page-title - .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } - - if @milestone.closed? - Closed - - else - Open - Milestone #{@milestone.title} - .pull-right - - if can?(current_user, :admin_milestones, @group) - - if @milestone.active? - = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close" +.issuable-details + .page-title + .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" } + - if @milestone.closed? + Closed - else - = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen" + Open + Milestone #{@milestone.title} + .pull-right + - if can?(current_user, :admin_milestones, @group) + - if @milestone.active? + = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close" + - else + = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen" + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) -%hr - if @milestone.complete? && @milestone.active? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close the milestone now. -.description - .table-holder %table.table %thead @@ -52,7 +54,7 @@ #{@milestone.open_items_count} open = milestone_progress_bar(@milestone) -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -66,25 +68,40 @@ Participants %span.badge= @milestone.participants.count - .pull-right - = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-6 = render 'issues', title: "Open", issues: @milestone.opened_issues .col-md-6 = render 'issues', title: "Closed", issues: @milestone.closed_issues .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-6 = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests .col-md-6 = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone + %ul.bordered-list - @milestone.participants.each do |user| %li diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 3a898dfbcfd..c3bda794c65 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,46 +1,50 @@ - page_title @milestone.title, "Milestones" = render "header_title" -%h4.page-title - .issue-box{ class: issue_box_class(@milestone) } - - if @milestone.closed? - Closed - - elsif @milestone.expired? - Expired - - else - Open - Milestone ##{@milestone.iid} - %small.creator - = @milestone.expires_at - .pull-right - - if can?(current_user, :admin_milestone, @project) - = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do - %i.fa.fa-pencil-square-o - Edit - - if @milestone.active? - = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" +.issuable-details + .page-title + .issue-box{ class: issue_box_class(@milestone) } + - if @milestone.closed? + Closed + - elsif @milestone.expired? + Expired - else - = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" - = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do - %i.fa.fa-trash-o - Remove + Open + Milestone ##{@milestone.iid} + - if @milestone.expires_at + %span.creator + · + = @milestone.expires_at + .pull-right + - if can?(current_user, :admin_milestone, @project) + = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do + %i.fa.fa-pencil-square-o + Edit + + - if @milestone.active? + = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" + - else + = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" + + = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do + %i.fa.fa-trash-o + Delete + + .gray-content-block.middle-block + %h2.issue-title + = gfm escape_once(@milestone.title) + %div + - if @milestone.description.present? + .description + .wiki + = preserve do + = markdown @milestone.description -%hr - if @milestone.issues.any? && @milestone.can_be_closed? - .alert.alert-success + .alert.alert-success.prepend-top-default %span All issues for this milestone are closed. You may close milestone now. -%h3.issue-title - = gfm escape_once(@milestone.title) -%div - - if @milestone.description.present? - .description - .wiki - = preserve do - = markdown @milestone.description - -%hr -.context +.context.prepend-top-default %p.lead Progress: #{@milestone.closed_items_count} closed @@ -51,8 +55,7 @@ %span.pull-right= @milestone.expires_at = milestone_progress_bar(@milestone) - -%ul.nav.nav-tabs +%ul.center-top-menu.no-top.no-bottom %li.active = link_to '#tab-issues', 'data-toggle' => 'tab' do Issues @@ -66,17 +69,21 @@ Participants %span.badge= @users.count - .pull-right - - if can?(current_user, :create_issue, @project) - = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do - %i.fa.fa-plus - New Issue - - if can?(current_user, :read_issue, @project) - = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn edit-milestone-link btn-grouped" - .tab-content .tab-pane.active#tab-issues - .row + .gray-content-block.middle-block + .pull-right + - if can?(current_user, :create_issue, @project) + = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do + %i.fa.fa-plus + New Issue + - if can?(current_user, :read_issue, @project) + = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All issues in this milestone + + .row.prepend-top-default .col-md-4 = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned') .col-md-4 @@ -85,7 +92,15 @@ = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') .tab-pane#tab-merge-requests - .row + .gray-content-block.middle-block + .pull-right + - if can?(current_user, :read_merge_request, @project) + = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" + + .oneline + All merge requests in this milestone + + .row.prepend-top-default .col-md-3 = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned') .col-md-3 @@ -100,6 +115,10 @@ = render 'merge_request', merge_request: merge_request .tab-pane#tab-participants + .gray-content-block.middle-block + .oneline + All participants to this milestone + %ul.bordered-list - @users.each do |user| %li -- cgit v1.2.1 From 5a59712b8ac29450dfef0af0dd381529f0d9d7ae Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:02:12 +0100 Subject: Add "New X" buttons to dashboard and group issue, MR and milestone indexes --- app/views/dashboard/issues.html.haml | 31 ++++++++++++++++----- app/views/dashboard/merge_requests.html.haml | 23 ++++++++++++++-- app/views/dashboard/milestones/index.html.haml | 21 +++++++++++--- app/views/groups/issues.html.haml | 38 ++++++++++++++++++-------- app/views/groups/merge_requests.html.haml | 30 +++++++++++++++----- app/views/groups/milestones/index.html.haml | 24 +++++++++------- app/views/projects/milestones/index.html.haml | 15 ++++++---- 7 files changed, 134 insertions(+), 48 deletions(-) diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index cd602e897b7..829c3c83769 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -4,14 +4,31 @@ - if current_user = auto_discovery_link_tag(:atom, issues_dashboard_url(format: :atom, private_token: current_user.private_token), title: "#{current_user.name} issues") +.project-issuable-filter + .controls + .pull-left + - if current_user + .hidden-xs.pull-left + = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do + %i.fa.fa-rss -.append-bottom-20 - .pull-right - - if current_user - .hidden-xs.pull-left.prepend-top-20 - = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: '' do - %i.fa.fa-rss + - if @projects.any? { |project| can?(current_user, :create_issue, project) } + .dropdown.inline.prepend-left-10 + %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} + %i.fa.fa-plus + New Issue + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + - @projects.each do |project| + - if can?(current_user, :create_issue, project) + %li + = link_to new_namespace_project_issue_path(project.namespace, project) do + = project.name_with_namespace = render 'shared/issuable/filter', type: :issues -= render 'shared/issues' +.gray-content-block.second-block + List all issues from all projects you have access to. + +.prepend-top-default + = render 'shared/issues' diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index d1f332fa0d3..2e91c8dec8a 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -1,6 +1,25 @@ - page_title "Merge Requests" - header_title "Merge Requests", merge_requests_dashboard_path(assignee_id: current_user.id) -.append-bottom-20 +.project-issuable-filter + .controls + - if @projects.any? { |project| can?(current_user, :create_merge_request, project) } + .dropdown.inline + %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} + %i.fa.fa-plus + New Merge Request + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + - @projects.each do |project| + - if can?(current_user, :create_merge_request, project) + %li + = link_to new_namespace_project_merge_request_path(project.namespace, project) do + = project.name_with_namespace + = render 'shared/issuable/filter', type: :merge_requests -= render 'shared/merge_requests' + +.gray-content-block.second-block + List all merge requests from all projects you have access to. + +.prepend-top-default + = render 'shared/merge_requests' diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index 635251e2374..9aea75e50db 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -1,12 +1,25 @@ - page_title "Milestones" -- header_title "Milestones", dashboard_milestones_path +- header_title "Milestones", dashboard_milestones_path +.project-issuable-filter + .controls + - if @projects.any? { |project| can?(current_user, :admin_milestone, project) } + .dropdown.inline + %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} + %i.fa.fa-plus + New Milestone + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + - @projects.each do |project| + - if can?(current_user, :admin_milestone, project) + %li + = link_to new_namespace_project_milestone_path(project.namespace, project) do + = project.name_with_namespace -= render 'shared/milestones_filter' + = render 'shared/milestones_filter' .gray-content-block - .oneline - List all milestones from all projects you have access to. + List all milestones from all projects you have access to. .milestones %ul.content-list diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 08d97e418a3..5a9739a0cda 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -4,21 +4,35 @@ - if current_user = auto_discovery_link_tag(:atom, issues_group_url(@group, format: :atom, private_token: current_user.private_token), title: "#{@group.name} issues") +.project-issuable-filter + .controls + .pull-left + - if current_user + .hidden-xs.pull-left + = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do + %i.fa.fa-rss + - if @projects.any? { |project| can?(current_user, :create_issue, project) } + .dropdown.inline.prepend-left-10 + %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} + %i.fa.fa-plus + New Issue + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + - @projects.each do |project| + - if can?(current_user, :create_issue, project) + %li + = link_to new_namespace_project_issue_path(project.namespace, project) do + = project.name_with_namespace + + = render 'shared/issuable/filter', type: :issues -= render 'shared/issuable/filter', type: :issues .gray-content-block.second-block - .pull-right - - if current_user - .hidden-xs.pull-left - = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token) do - %i.fa.fa-rss - %div - Only issues from - %strong #{@group.name} - group are listed here. - - if current_user - To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page. + Only issues from + %strong #{@group.name} + group are listed here. + - if current_user + To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page. .prepend-top-default = render 'shared/issues' diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 425ad8331bf..95c503a3afa 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,13 +1,29 @@ - page_title "Merge Requests" - header_title group_title(@group, "Merge Requests", merge_requests_group_path(@group)) -= render 'shared/issuable/filter', type: :merge_requests +.project-issuable-filter + .controls + - if @projects.any? { |project| can?(current_user, :create_merge_request, project) } + .dropdown.inline + %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} + %i.fa.fa-plus + New Merge Request + %b.caret + %ul.dropdown-menu.dropdown-menu-align-right + - @projects.each do |project| + - if can?(current_user, :create_merge_request, project) + %li + = link_to new_namespace_project_merge_request_path(project.namespace, project) do + = project.name_with_namespace + + = render 'shared/issuable/filter', type: :merge_requests + .gray-content-block.second-block - %div - Only merge requests from - %strong #{@group.name} - group are listed here. - - if current_user - To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. + Only merge requests from + %strong #{@group.name} + group are listed here. + - if current_user + To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. + .prepend-top-default = render 'shared/merge_requests' diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml index 84ec77c6188..b221d3a89a4 100644 --- a/app/views/groups/milestones/index.html.haml +++ b/app/views/groups/milestones/index.html.haml @@ -1,18 +1,22 @@ - page_title "Milestones" - header_title group_title(@group, "Milestones", group_milestones_path(@group)) -= render 'shared/milestones_filter' +.project-issuable-filter + .controls + - if can?(current_user, :admin_milestones, @group) + .pull-right + %span.pull-right.hidden-xs + = link_to new_group_milestone_path(@group), class: "btn btn-new" do + = icon('plus') + New Milestone + + = render 'shared/milestones_filter' + .gray-content-block - - if can?(current_user, :admin_milestones, @group) - .pull-right - %span.pull-right.hidden-xs - = link_to new_group_milestone_path(@group), class: "btn btn-new" do - New Milestone + Only milestones from + %strong #{@group.name} + group are listed here. - .oneline - Only milestones from - %strong #{@group.name} - group are listed here. .milestones %ul.content-list - if @milestones.blank? diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml index a207385bd43..114b06457a5 100644 --- a/app/views/projects/milestones/index.html.haml +++ b/app/views/projects/milestones/index.html.haml @@ -1,15 +1,18 @@ - page_title "Milestones" = render "header_title" -= render 'shared/milestones_filter' -.gray-content-block - .pull-right - - if can? current_user, :admin_milestone, @project + +.project-issuable-filter + .controls + - if can?(current_user, :admin_milestone, @project) = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do %i.fa.fa-plus New Milestone - .oneline - Milestone allows you to group issues and set due date for it + + = render 'shared/milestones_filter' + +.gray-content-block + Milestone allows you to group issues and set due date for it .milestones %ul.content-list -- cgit v1.2.1 From 05224ae64ac769e3b9b06679d6e44fb120c879ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:10 +0100 Subject: Restore sidebar tooltips and fix logo tooltip location --- app/assets/javascripts/application.js.coffee | 26 +-- app/assets/javascripts/sidebar.js.coffee | 1 + app/assets/stylesheets/framework/sidebar.scss | 185 +++++++++++----------- app/helpers/nav_helper.rb | 8 + app/views/layouts/_page.html.haml | 10 +- app/views/layouts/ci/_page.html.haml | 10 +- app/views/layouts/nav/_admin.html.haml | 28 ++-- app/views/layouts/nav/_dashboard.html.haml | 18 +-- app/views/layouts/nav/_explore.html.haml | 8 +- app/views/layouts/nav/_group.html.haml | 14 +- app/views/layouts/nav/_group_settings.html.haml | 6 +- app/views/layouts/nav/_profile.html.haml | 20 +-- app/views/layouts/nav/_project.html.haml | 34 ++-- app/views/layouts/nav/_project_settings.html.haml | 24 +-- 14 files changed, 207 insertions(+), 185 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 945ffb660e6..1539eba0faa 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -135,17 +135,25 @@ $ -> ), 1 # Initialize tooltips - $('body').tooltip({ - selector: '.has_tooltip, [data-toggle="tooltip"], .page-sidebar-collapsed .nav-sidebar a' + $('body').tooltip( + selector: '.has_tooltip, [data-toggle="tooltip"]' placement: (_, el) -> $el = $(el) - if $el.attr('id') == 'js-shortcuts-home' - # Place the logo tooltip on the right when collapsed, bottom when expanded - $el.parents('header').hasClass('header-collapsed') and 'right' or 'bottom' - else - # Otherwise use the data-placement attribute, or 'bottom' if undefined - $el.data('placement') or 'bottom' - }) + $el.data('placement') || 'bottom' + ) + + $('.header-logo .home').tooltip( + placement: (_, el) -> + $el = $(el) + if $('.page-with-sidebar').hasClass('page-sidebar-collapsed') then 'right' else 'bottom' + container: 'body' + ) + + $('.page-with-sidebar').tooltip( + selector: '.sidebar-collapsed .nav-sidebar a, .sidebar-collapsed a.sidebar-user' + placement: 'right' + container: 'body' + ) # Form submitter $('.trigger-submit').on 'change', -> diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee index fb08016fbae..ae59480af9e 100644 --- a/app/assets/javascripts/sidebar.js.coffee +++ b/app/assets/javascripts/sidebar.js.coffee @@ -5,6 +5,7 @@ $(document).on("click", '.toggle-nav-collapse', (e) -> $('.page-with-sidebar').toggleClass("#{collapsed} #{expanded}") $('header').toggleClass("header-collapsed header-expanded") + $('.sidebar-wrapper').toggleClass("sidebar-collapsed sidebar-expanded") $('.toggle-nav-collapse i').toggleClass("fa-angle-right fa-angle-left") $.cookie("collapsed_nav", $('.page-with-sidebar').hasClass(collapsed), { path: '/' }) ) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c1b0129c866..017f4d2657a 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,4 +1,7 @@ .page-with-sidebar { + padding-top: $header-height; + transition-duration: .3s; + .sidebar-wrapper { position: fixed; top: 0; @@ -14,19 +17,15 @@ .sidebar-wrapper { z-index: 99; background: $background-color; - transition-duration: .3s; } .content-wrapper { - min-height: 100vh; width: 100%; padding: 20px; - background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; - min-height: 90vh; &.container-blank { background: none; @@ -36,6 +35,83 @@ } } +.sidebar-wrapper { + .header-logo { + border-bottom: 1px solid transparent; + float: left; + height: $header-height; + width: $sidebar_width; + position: fixed; + z-index: 999; + overflow: hidden; + transition-duration: .3s; + + a { + float: left; + height: $header-height; + width: 100%; + padding: 11px 0 11px 22px; + overflow: hidden; + outline: none; + transition-duration: .3s; + + img { + width: 36px; + height: 36px; + } + + #tanuki-logo, img { + float: left; + } + + .gitlab-text-container { + width: 230px; + + h3 { + width: 158px; + float: left; + margin: 0; + margin-left: 14px; + font-size: 19px; + line-height: 41px; + font-weight: normal; + } + } + } + + &:hover { + background-color: #EEE; + } + } + + .sidebar-user { + padding: 9px 22px; + position: fixed; + bottom: 40px; + width: $sidebar_width; + overflow: hidden; + transition-duration: .3s; + + .username { + margin-left: 10px; + width: $sidebar_width - 2 * 10px; + font-size: 16px; + line-height: 34px; + } + } +} + + +.tanuki-shape { + transition: all 0.8s; + + &:hover { + fill: rgb(255, 255, 255); + transition: all 0.1s; + } +} + + .nav-sidebar { margin-top: 14 + $header-height; margin-bottom: 100px; @@ -62,7 +138,7 @@ color: $gray; display: block; text-decoration: none; - padding-left: 22px; + padding-left: 23px; font-weight: normal; outline: none; @@ -101,7 +177,6 @@ @mixin expanded-sidebar { padding-left: $sidebar_width; - transition-duration: .3s; .sidebar-wrapper { width: $sidebar_width; @@ -122,9 +197,8 @@ } } -@mixin folded-sidebar { - padding-left: 60px; - transition-duration: .3s; +@mixin collapsed-sidebar { + padding-left: $sidebar_collapsed_width; .sidebar-wrapper { width: $sidebar_collapsed_width; @@ -133,7 +207,7 @@ width: $sidebar_collapsed_width; a { - padding-left: 12px; + padding-left: ($sidebar_collapsed_width - 36) / 2; .gitlab-text-container { display: none; @@ -144,9 +218,13 @@ .nav-sidebar { width: $sidebar_collapsed_width; - li a { - span { - display: none; + li { + width: auto; + + a { + span { + display: none; + } } } } @@ -156,7 +234,7 @@ } .sidebar-user { - padding-left: 12px; + padding-left: ($sidebar_collapsed_width - 36) / 2; width: $sidebar_collapsed_width; .username { @@ -187,11 +265,11 @@ @media (max-width: $screen-md-max) { .page-sidebar-collapsed { - @include folded-sidebar; + @include collapsed-sidebar; } .page-sidebar-expanded { - @include folded-sidebar; + @include collapsed-sidebar; } .collapse-nav { @@ -201,83 +279,10 @@ @media(min-width: $screen-md-max) { .page-sidebar-collapsed { - @include folded-sidebar; + @include collapsed-sidebar; } .page-sidebar-expanded { @include expanded-sidebar; } } - -.sidebar-user { - padding: 9px 22px; - position: fixed; - bottom: 40px; - width: $sidebar_width; - overflow: hidden; - transition-duration: .3s; - - .username { - margin-left: 10px; - width: $sidebar_width - 2 * 10px; - font-size: 16px; - line-height: 34px; - } -} - -.sidebar-wrapper { - .header-logo { - border-bottom: 1px solid transparent; - float: left; - height: $header-height; - width: $sidebar_width; - overflow: hidden; - transition-duration: .3s; - - a { - float: left; - height: $header-height; - width: 100%; - padding: 10px 22px; - overflow: hidden; - outline: none; - - img { - width: 36px; - height: 36px; - } - - #tanuki-logo, img { - float: left; - } - - .gitlab-text-container { - width: 230px; - - h3 { - width: 158px; - float: left; - margin: 0; - margin-left: 14px; - font-size: 19px; - line-height: 41px; - font-weight: normal; - } - } - } - - &:hover { - background-color: #EEE; - } - } -} - - -.tanuki-shape { - transition: all 0.8s; - - &:hover { - fill: rgb(255, 255, 255); - transition: all 0.1s; - } -} diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 9b1dd8b8e54..e6fb8670e57 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -4,6 +4,14 @@ module NavHelper end def nav_sidebar_class + if nav_menu_collapsed? + "sidebar-collapsed" + else + "sidebar-expanded" + end + end + + def page_sidebar_class if nav_menu_collapsed? "page-sidebar-collapsed" else diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 352b8040cf4..ec7cd79bc54 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,8 +1,8 @@ -.page-with-sidebar{ class: nav_sidebar_class } +.page-with-sidebar{ class: page_sidebar_class } = render "layouts/broadcast" - .sidebar-wrapper.nicescroll + .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .header-logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = brand_header_logo .gitlab-text-container %h3 GitLab @@ -17,8 +17,8 @@ .collapse-nav = render partial: 'layouts/collapse_button' - if current_user - = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' + = link_to current_user, class: 'sidebar-user', title: "Profile" do + = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml index ab3e29c3f42..7e90af21b21 100644 --- a/app/views/layouts/ci/_page.html.haml +++ b/app/views/layouts/ci/_page.html.haml @@ -1,8 +1,8 @@ -.page-with-sidebar{ class: nav_sidebar_class } +.page-with-sidebar{ class: page_sidebar_class } = render "layouts/broadcast" - .sidebar-wrapper.nicescroll + .sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .header-logo - = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to root_path, class: 'home', title: 'Dashboard', id: 'js-shortcuts-home' do = brand_header_logo .gitlab-text-container %h3 GitLab @@ -14,8 +14,8 @@ .collapse-nav = render partial: 'layouts/collapse_button' - if current_user - = link_to current_user, class: 'sidebar-user' do - = image_tag avatar_icon(current_user, 60), alt: 'User activity', class: 'avatar avatar s36' + = link_to current_user, class: 'sidebar-user', title: "Profile" do + = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' .username = current_user.username .content-wrapper diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 2079feeeab6..d04a3d1f227 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -5,78 +5,78 @@ %span Overview = nav_link(controller: [:admin, :projects]) do - = link_to admin_namespaces_projects_path, title: 'Projects', data: {placement: 'right'} do + = link_to admin_namespaces_projects_path, title: 'Projects' do = icon('cube fw') %span Projects = nav_link(controller: :users) do - = link_to admin_users_path, title: 'Users', data: {placement: 'right'} do + = link_to admin_users_path, title: 'Users' do = icon('user fw') %span Users = nav_link(controller: :groups) do - = link_to admin_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to admin_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :deploy_keys) do - = link_to admin_deploy_keys_path, title: 'Deploy Keys', data: {placement: 'right'} do + = link_to admin_deploy_keys_path, title: 'Deploy Keys' do = icon('key fw') %span Deploy Keys = nav_link do - = link_to ci_admin_projects_path, title: 'Continuous Integration', data: {placement: 'right'} do + = link_to ci_admin_projects_path, title: 'Continuous Integration' do = icon('building fw') %span Continuous Integration = nav_link(controller: :logs) do - = link_to admin_logs_path, title: 'Logs', data: {placement: 'right'} do + = link_to admin_logs_path, title: 'Logs' do = icon('file-text fw') %span Logs = nav_link(controller: :broadcast_messages) do - = link_to admin_broadcast_messages_path, title: 'Broadcast Messages', data: {placement: 'right'} do + = link_to admin_broadcast_messages_path, title: 'Messages' do = icon('bullhorn fw') %span Messages = nav_link(controller: :hooks) do - = link_to admin_hooks_path, title: 'Hooks', data: {placement: 'right'} do + = link_to admin_hooks_path, title: 'Hooks' do = icon('external-link fw') %span Hooks = nav_link(controller: :background_jobs) do - = link_to admin_background_jobs_path, title: 'Background Jobs', data: {placement: 'right'} do + = link_to admin_background_jobs_path, title: 'Background Jobs' do = icon('cog fw') %span Background Jobs = nav_link(controller: :applications) do - = link_to admin_applications_path, title: 'Applications', data: {placement: 'right'} do + = link_to admin_applications_path, title: 'Applications' do = icon('cloud fw') %span Applications = nav_link(controller: :services) do - = link_to admin_application_settings_services_path, title: 'Service Templates', data: {placement: 'right'} do + = link_to admin_application_settings_services_path, title: 'Service Templates' do = icon('copy fw') %span Service Templates = nav_link(controller: :labels) do - = link_to admin_labels_path, title: 'Labels', data: {placement: 'right'} do + = link_to admin_labels_path, title: 'Labels' do = icon('tags fw') %span Labels = nav_link(controller: :abuse_reports) do - = link_to admin_abuse_reports_path, title: "Abuse reports" do + = link_to admin_abuse_reports_path, title: "Abuse Reports" do = icon('exclamation-circle fw') %span Abuse Reports %span.count= AbuseReport.count(:all) = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do - = link_to admin_application_settings_path, title: 'Settings', data: {placement: 'right'} do + = link_to admin_application_settings_path, title: 'Settings' do = icon('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index b1a1d531846..da698831300 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,50 +1,50 @@ %ul.nav.nav-sidebar = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do - = link_to dashboard_projects_path, title: 'Projects', data: {placement: 'right'} do + = link_to dashboard_projects_path, title: 'Projects' do = icon('home fw') %span Projects = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity', data: {placement: 'right'} do + = link_to activity_dashboard_path, class: 'shortcuts-activity', title: 'Activity' do = icon('dashboard fw') %span Activity = nav_link(controller: :groups) do - = link_to dashboard_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to dashboard_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :milestones) do - = link_to dashboard_milestones_path, title: 'Milestones', data: {placement: 'right'} do + = link_to dashboard_milestones_path, title: 'Milestones' do = icon('clock-o fw') %span Milestones = nav_link(path: 'dashboard#issues') do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do = icon('exclamation-circle fw') %span Issues %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do - = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do + = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do = icon('tasks fw') %span Merge Requests %span.count= current_user.assigned_merge_requests.opened.count = nav_link(controller: :snippets) do - = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do + = link_to dashboard_snippets_path, title: 'Snippets' do = icon('clipboard fw') %span Snippets = nav_link(controller: :help) do - = link_to help_path, title: 'Help', data: {placement: 'right'} do + = link_to help_path, title: 'Help' do = icon('question-circle fw') %span Help %li.separate-item = nav_link(controller: :profile) do - = link_to profile_path, title: 'Profile settings', data: {placement: 'bottom'} do + = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do = icon('user fw') %span Profile Settings diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index 21e565972a7..48039ca2918 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,21 +1,21 @@ %ul.nav.nav-sidebar = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do - = link_to explore_root_path, title: 'Projects', data: {placement: 'right'} do + = link_to explore_root_path, title: 'Projects' do = icon('home fw') %span Projects = nav_link(controller: :groups) do - = link_to explore_groups_path, title: 'Groups', data: {placement: 'right'} do + = link_to explore_groups_path, title: 'Groups' do = icon('group fw') %span Groups = nav_link(controller: :snippets) do - = link_to explore_snippets_path, title: 'Snippets', data: {placement: 'right'} do + = link_to explore_snippets_path, title: 'Snippets' do = icon('clipboard fw') %span Snippets = nav_link(controller: :help) do - = link_to help_path, title: 'Help', data: {placement: 'right'} do + = link_to help_path, title: 'Help' do = icon('question-circle fw') %span Help diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 319352876b4..68da8d5de2a 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -8,39 +8,39 @@ %li.separate-item = nav_link(path: 'groups#show', html_options: {class: 'home'}) do - = link_to group_path(@group), title: 'Home', data: {placement: 'right'} do + = link_to group_path(@group), title: 'Home' do = icon('dashboard fw') %span Group - if can?(current_user, :read_group, @group) - if current_user = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group), title: 'Milestones', data: {placement: 'right'} do + = link_to group_milestones_path(@group), title: 'Milestones' do = icon('clock-o fw') %span Milestones = nav_link(path: 'groups#issues') do - = link_to issues_group_path(@group), title: 'Issues', data: {placement: 'right'} do + = link_to issues_group_path(@group), title: 'Issues' do = icon('exclamation-circle fw') %span Issues - if current_user %span.count= Issue.opened.of_group(@group).count = nav_link(path: 'groups#merge_requests') do - = link_to merge_requests_group_path(@group), title: 'Merge Requests', data: {placement: 'right'} do + = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - if current_user %span.count= MergeRequest.opened.of_group(@group).count = nav_link(controller: [:group_members]) do - = link_to group_group_members_path(@group), title: 'Members', data: {placement: 'right'} do + = link_to group_group_members_path(@group), title: 'Members' do = icon('users fw') %span Members - if can?(current_user, :admin_group, @group) = nav_link(html_options: { class: "separate-item" }) do - = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do + = link_to edit_group_path(@group), title: 'Settings' do = icon ('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml index c8411521f36..56a92fe9103 100644 --- a/app/views/layouts/nav/_group_settings.html.haml +++ b/app/views/layouts/nav/_group_settings.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@group), title: 'Go to group', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to group @@ -9,12 +9,12 @@ %ul.sidebar-subnav = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), title: 'Group Settings', data: {placement: 'right'} do + = link_to edit_group_path(@group), title: 'Group Settings' do = icon ('pencil-square-o fw') %span Group Settings = nav_link(path: 'groups#projects') do - = link_to projects_group_path(@group), title: 'Projects', data: {placement: 'right'} do + = link_to projects_group_path(@group), title: 'Projects' do = icon('folder fw') %span Projects diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 0f3a793e30b..64b30783c05 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -8,52 +8,52 @@ %li.separate-item = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do - = link_to profile_path, title: 'Profile', data: {placement: 'right'} do + = link_to profile_path, title: 'Profile Settings' do = icon('user fw') %span Profile Settings = nav_link(controller: [:accounts, :two_factor_auths]) do - = link_to profile_account_path, title: 'Account', data: {placement: 'right'} do + = link_to profile_account_path, title: 'Account' do = icon('gear fw') %span Account = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new', 'applications#create']) do - = link_to applications_profile_path, title: 'Applications', data: {placement: 'right'} do + = link_to applications_profile_path, title: 'Applications' do = icon('cloud fw') %span Applications = nav_link(controller: :emails) do - = link_to profile_emails_path, title: 'Emails', data: {placement: 'right'} do + = link_to profile_emails_path, title: 'Emails' do = icon('envelope-o fw') %span Emails %span.count= current_user.emails.count + 1 - unless current_user.ldap_user? = nav_link(controller: :passwords) do - = link_to edit_profile_password_path, title: 'Password', data: {placement: 'right'} do + = link_to edit_profile_password_path, title: 'Password' do = icon('lock fw') %span Password = nav_link(controller: :notifications) do - = link_to profile_notifications_path, title: 'Notifications', data: {placement: 'right'} do + = link_to profile_notifications_path, title: 'Notifications' do = icon('inbox fw') %span Notifications = nav_link(controller: :keys) do - = link_to profile_keys_path, title: 'SSH Keys', data: {placement: 'right'} do + = link_to profile_keys_path, title: 'SSH Keys' do = icon('key fw') %span SSH Keys %span.count= current_user.keys.count = nav_link(controller: :preferences) do - = link_to profile_preferences_path, title: 'Preferences', data: {placement: 'right'} do + = link_to profile_preferences_path, title: 'Preferences' do -# TODO (rspeicher): Better icon? = icon('image fw') %span Preferences = nav_link(path: 'profiles#audit_log') do - = link_to audit_log_profile_path, title: 'Audit Log', data: {placement: 'right'} do + = link_to audit_log_profile_path, title: 'Audit Log' do = icon('history fw') %span Audit Log diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2b91d7721f9..87a7707b095 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,13 +1,13 @@ %ul.nav.nav-sidebar - if @project.group = nav_link do - = link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do + = link_to group_path(@project.group), title: 'Go to group', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to group - else = nav_link do - = link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do + = link_to root_path, title: 'Go to dashboard', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to dashboard @@ -15,32 +15,32 @@ %li.separate-item = nav_link(path: 'projects#show', html_options: {class: 'home'}) do - = link_to project_path(@project), title: 'Project', class: 'shortcuts-project', data: {placement: 'right'} do + = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do = icon('home fw') %span Project = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: 'Project Activity', class: 'shortcuts-project-activity', data: {placement: 'right'} do + = link_to activity_project_path(@project), title: 'Activity', class: 'shortcuts-project-activity' do = icon('dashboard fw') %span Activity - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree', data: {placement: 'right'} do + = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do = icon('files-o fw') %span Files - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches releases)) do - = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do + = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = icon('history fw') %span Commits - if project_nav_tab? :builds = nav_link(controller: %w(builds)) do - = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds', data: {placement: 'right'} do + = link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do = icon('cubes fw') %span Builds @@ -48,28 +48,28 @@ - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network', data: {placement: 'right'} do + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do = icon('code-fork fw') %span Network - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs', data: {placement: 'right'} do + = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do = icon('area-chart fw') %span Graphs - if project_nav_tab? :milestones = nav_link(controller: :milestones) do - = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones', data: {placement: 'right'} do + = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do = icon('clock-o fw') %span Milestones - if project_nav_tab? :issues = nav_link(controller: :issues) do - = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues', data: {placement: 'right'} do + = link_to url_for_project_issues(@project, only_path: true), title: 'Issues', class: 'shortcuts-issues' do = icon('exclamation-circle fw') %span Issues @@ -78,7 +78,7 @@ - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do - = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests', data: {placement: 'right'} do + = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do = icon('tasks fw') %span Merge Requests @@ -86,35 +86,35 @@ - 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 + = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = icon('users fw') %span Members - if project_nav_tab? :labels = nav_link(controller: :labels) do - = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels', data: {placement: 'right'} do + = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do = icon('tags fw') %span Labels - if project_nav_tab? :wiki = nav_link(controller: :wikis) do - = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki', data: {placement: 'right'} do + = link_to get_project_wiki_path(@project), title: 'Wiki', class: 'shortcuts-wiki' do = icon('book fw') %span Wiki - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets', data: {placement: 'right'} do + = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do = icon('clipboard fw') %span Snippets - if project_nav_tab? :settings = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do - = link_to edit_project_path(@project), title: 'Settings', data: {placement: 'right'} do + = link_to edit_project_path(@project), title: 'Settings' do = icon('cogs fw') %span Settings diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 377a99e719a..f0b3f27b626 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -1,6 +1,6 @@ %ul.nav.nav-sidebar = nav_link do - = link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do + = link_to project_path(@project), title: 'Go to project', class: 'back-link' do = icon('caret-square-o-left fw') %span Go to project @@ -9,59 +9,59 @@ %ul.sidebar-subnav = nav_link(path: 'projects#edit') do - = link_to edit_project_path(@project), title: 'Project Settings', data: {placement: 'right'} do + = link_to edit_project_path(@project), title: 'Project Settings' do = icon('pencil-square-o fw') %span Project Settings = nav_link(controller: :deploy_keys) do - = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys', data: {placement: 'right'} do + = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do = icon('key fw') %span Deploy Keys = nav_link(controller: :hooks) do - = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks', data: {placement: 'right'} do + = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do = icon('link fw') %span Web Hooks = nav_link(controller: :services) do - = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services', data: {placement: 'right'} do + = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do = icon('cogs fw') %span Services = nav_link(controller: :protected_branches) do - = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches', data: {placement: 'right'} do + = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do = icon('lock fw') %span Protected Branches - if @project.builds_enabled? = nav_link(controller: :runners) do - = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do + = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners' do = icon('cog fw') %span Runners = nav_link(controller: :variables) do - = link_to namespace_project_variables_path(@project.namespace, @project) do + = link_to namespace_project_variables_path(@project.namespace, @project), title: 'Variables' do = icon('code fw') %span Variables = nav_link path: 'triggers#index' do - = link_to namespace_project_triggers_path(@project.namespace, @project) do + = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do = icon('retweet fw') %span Triggers = nav_link path: 'ci_web_hooks#index' do - = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project) do + = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do = icon('link fw') %span CI Web Hooks = nav_link path: 'ci_settings#edit' do - = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project) do + = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do = icon('building fw') %span CI Settings = nav_link controller: 'ci_services' do - = link_to namespace_project_ci_services_path(@project.namespace, @project) do + = link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do = icon('share fw') %span CI Services -- cgit v1.2.1 From 5c771cca3312dfd447ebe5e0b92ebe279ca1dd67 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:24 +0100 Subject: Fix logo height in signed-out header bar --- app/assets/stylesheets/framework/header.scss | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 02ea91602e8..4dbbb56104b 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -6,15 +6,17 @@ header { transition-duration: .3s; &.navbar-empty { + height: 58px; background: #FFF; border-bottom: 1px solid #EEE; .center-logo { - margin: 8px 0; + margin: 11px 0; text-align: center; - img { - height: 32px; + #tanuki-logo, img { + width: 36px; + height: 36px; } } } -- cgit v1.2.1 From 4f7380f94eb7d118b7be51c09e878dfcca84a942 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:05:58 +0100 Subject: Fix header tooltip alignment --- app/views/layouts/header/_default.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 3ca30d3baab..3892ef8eefa 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -11,27 +11,27 @@ %li.hidden-sm.hidden-xs = render 'layouts/search' %li.visible-sm.visible-xs - = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('search') - if session[:impersonator_id] %li.impersonation - = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do + = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('user-secret fw') - if current_user.is_admin? %li - = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to admin_root_path, title: 'Admin Area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('wrench fw') - if current_user.can_create_project? %li - = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to new_project_path, title: 'New project', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('plus fw') - if Gitlab::Sherlock.enabled? %li = link_to sherlock_transactions_path, title: 'Sherlock Transactions', - data: {toggle: 'tooltip', placement: 'bottom'} do + data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('tachometer fw') %li - = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom'} do + = link_to destroy_user_session_path, class: 'logout', method: :delete, title: 'Sign out', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = icon('sign-out') %h1.title= title -- cgit v1.2.1 From 1ef01e4f6a25743f2034da222407a41b63920df0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:06:11 +0100 Subject: Fade in/out Back icon when sidebar is toggled --- app/assets/stylesheets/framework/sidebar.scss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 017f4d2657a..458af76cb75 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -162,6 +162,10 @@ padding: 0px 8px; @include border-radius(6px); } + + &.back-link i { + transition-duration: .3s; + } } } } @@ -190,7 +194,7 @@ &.back-link { i { - visibility: hidden; + opacity: 0; } } } -- cgit v1.2.1 From 00a4be759e091bf3e07bde48650e1402da908896 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:06:25 +0100 Subject: Page titles are title case. --- app/views/layouts/admin.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 1c738719bd8..6591c52bdbd 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,5 +1,5 @@ -- page_title "Admin area" -- header_title "Admin area", admin_root_path +- page_title "Admin Area" +- header_title "Admin Area", admin_root_path - sidebar "admin" = render template: "layouts/application" -- cgit v1.2.1 From 112cca872bd7982bd2452f2f0903d21342d5ea5f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:07:21 +0100 Subject: Move project visibility status to the left --- app/assets/stylesheets/framework/blocks.scss | 5 +++++ app/views/projects/_home_panel.html.haml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 1635df9c97b..9e30d3b9c8b 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -112,5 +112,10 @@ position: absolute; top: 10px; right: 10px; + + &.left { + left: 10px; + right: auto; + } } } diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index b30036966a7..d94a279eafd 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -12,9 +12,9 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - .cover-controls .visibility-level-label = visibility_level_icon(@project.visibility_level) + .cover-controls.left = visibility_level_label(@project.visibility_level) .project-repo-buttons -- cgit v1.2.1 From daca985a6e75d6f43c5cc5b487a0942d5bf93f68 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Tue, 1 Dec 2015 23:40:24 -0500 Subject: Prevent impersonation if blocked --- app/controllers/admin/impersonation_controller.rb | 16 +++++++++++----- app/views/admin/users/_head.html.haml | 2 +- .../admin/impersonation_controller_spec.rb | 19 +++++++++++++++++++ spec/features/admin/admin_users_spec.rb | 10 ++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 spec/controllers/admin/impersonation_controller_spec.rb diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb index 0382402afa6..102dd437402 100644 --- a/app/controllers/admin/impersonation_controller.rb +++ b/app/controllers/admin/impersonation_controller.rb @@ -5,14 +5,20 @@ class Admin::ImpersonationController < Admin::ApplicationController before_action :authorize_impersonator! def create - session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = request.env['HTTP_REFERER'] + if @user.blocked? + flash[:alert] = "You cannot impersonate a blocked user" - warden.set_user(user, scope: 'user') + redirect_to admin_user_path(@user) + else + session[:impersonator_id] = current_user.username + session[:impersonator_return_to] = request.env['HTTP_REFERER'] + + warden.set_user(user, scope: 'user') - flash[:alert] = "You are impersonating #{user.username}." + flash[:alert] = "You are impersonating #{user.username}." - redirect_to root_path + redirect_to root_path + end end def destroy diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 8d1cab4137c..5e17b018163 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -6,7 +6,7 @@ %span.cred (Admin) .pull-right - - unless @user == current_user + - unless @user == current_user || @user.blocked? = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do %i.fa.fa-pencil-square-o diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb new file mode 100644 index 00000000000..d7a7ba1c5b6 --- /dev/null +++ b/spec/controllers/admin/impersonation_controller_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Admin::ImpersonationController do + let(:admin) { create(:admin) } + + before do + sign_in(admin) + end + + describe 'CREATE #impersonation when blocked' do + let(:blocked_user) { create(:user, state: :blocked) } + + it 'does not allow impersonation' do + post :create, id: blocked_user.username + + expect(flash[:alert]).to eq 'You cannot impersonate a blocked user' + end + end +end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 86f01faffb4..4570e409128 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -128,6 +128,16 @@ describe "Admin::Users", feature: true do expect(page).not_to have_content('Impersonate') end + + it 'should not show impersonate button for blocked user' do + another_user.block + + visit admin_user_path(another_user) + + expect(page).not_to have_content('Impersonate') + + another_user.activate + end end context 'when impersonating' do -- cgit v1.2.1 From 085e45a81cbdac73f41702764a1b53a56a298653 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:08:25 +0100 Subject: Add edit and RSS buttons to project home panel --- app/views/projects/_home_panel.html.haml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index d94a279eafd..695da7f07d1 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -17,6 +17,15 @@ .cover-controls.left = visibility_level_label(@project.visibility_level) + .cover-controls + - if can?(current_user, :admin_project, @project) + = link_to edit_project_path(@project), class: 'btn btn-gray' do + = icon('pencil') + - if current_user +   + = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do + = icon('rss') + .project-repo-buttons .split-one = render 'projects/buttons/star' -- cgit v1.2.1 From d65647e90c25a1cf28353b3d0476aec0a4c6ebb7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:08:59 +0100 Subject: Add visibility description tooltip to snippet and project visibility labels --- app/helpers/icons_helper.rb | 22 +++++++++------ app/helpers/visibility_level_helper.rb | 44 +++++++---------------------- app/views/projects/_home_panel.html.haml | 4 +-- app/views/shared/snippets/_header.html.haml | 4 +-- 4 files changed, 27 insertions(+), 47 deletions(-) diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb index 1cf5b96481a..5724d3aabec 100644 --- a/app/helpers/icons_helper.rb +++ b/app/helpers/icons_helper.rb @@ -27,16 +27,20 @@ module IconsHelper end end - def public_icon - icon('globe fw') - end - - def internal_icon - icon('shield fw') - end + def visibility_level_icon(level, fw: true) + name = + case level + when Gitlab::VisibilityLevel::PRIVATE + 'lock' + when Gitlab::VisibilityLevel::INTERNAL + 'shield' + else # Gitlab::VisibilityLevel::PUBLIC + 'globe' + end + + name << " fw" if fw - def private_icon - icon('lock fw') + icon(name) end def file_type_icon_class(type, mode, name) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index b52cd23aba2..72c65030f94 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -25,48 +25,24 @@ module VisibilityLevelHelper end def project_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "Project access must be granted explicitly for each user." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The project can be cloned by" - haml_concat "any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The project can be cloned" - haml_concat "without any" - haml_concat "authentication." - end - end + case level + when Gitlab::VisibilityLevel::PRIVATE + "Project access must be granted explicitly for each user." + when Gitlab::VisibilityLevel::INTERNAL + "The project can be cloned by any logged in user." + when Gitlab::VisibilityLevel::PUBLIC + "The project can be cloned without any authentication." end end def snippet_visibility_level_description(level) - capture_haml do - haml_tag :span do - case level - when Gitlab::VisibilityLevel::PRIVATE - haml_concat "The snippet is visible only for me." - when Gitlab::VisibilityLevel::INTERNAL - haml_concat "The snippet is visible for any logged in user." - when Gitlab::VisibilityLevel::PUBLIC - haml_concat "The snippet can be accessed" - haml_concat "without any" - haml_concat "authentication." - end - end - end - end - - def visibility_level_icon(level) case level when Gitlab::VisibilityLevel::PRIVATE - private_icon + "The snippet is visible only for me." when Gitlab::VisibilityLevel::INTERNAL - internal_icon + "The snippet is visible for any logged in user." when Gitlab::VisibilityLevel::PUBLIC - public_icon + "The snippet can be accessed without any authentication." end end diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 695da7f07d1..c1669ac046b 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -12,9 +12,9 @@ Forked from = link_to project_path(forked_from_project) do = forked_from_project.namespace.try(:name) - .visibility-level-label - = visibility_level_icon(@project.visibility_level) .cover-controls.left + .visibility-level-label.has_tooltip{title: project_visibility_level_description(@project.visibility_level), data: { container: 'body' } } + = visibility_level_icon(@project.visibility_level, fw: false) = visibility_level_label(@project.visibility_level) .cover-controls diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 0a4a790ec5e..b6c9872f083 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,7 +1,7 @@ .snippet-details .page-title - .snippet-box{class: visibility_level_color(@snippet.visibility_level)} - = visibility_level_icon(@snippet.visibility_level) + .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }} + = visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_label(@snippet.visibility_level) %span.snippet-id Snippet ##{@snippet.id} %span.creator -- cgit v1.2.1 From de5e28cdcfe8554f2f45f3247d8304aa94f5c3cb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:13 +0100 Subject: Use default cursor for project visibility label. --- app/assets/stylesheets/pages/projects.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 4a0fe546844..68a3617512e 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -94,7 +94,7 @@ @extend .btn-gray; color: $gray; - cursor: auto; + cursor: default; i { color: inherit; -- cgit v1.2.1 From 762e759bb3546db7f058d8055ed7b62590d80217 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:18 +0100 Subject: Remove duplicate styles. --- app/assets/stylesheets/pages/projects.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 68a3617512e..25d4d0a2c43 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -30,12 +30,6 @@ } .project-home-panel { - text-align: center; - background: #f7f8fa; - margin: -$gl-padding; - padding: $gl-padding; - padding: 44px 0 17px 0; - .project-identicon-holder { margin-bottom: 16px; @@ -105,7 +99,6 @@ display: inline-table; position: relative; top: 17px; - margin-bottom: 44px; } .project-repo-buttons { -- cgit v1.2.1 From 6252fe4510376c917a690e655f8f4257132dde3e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:09:50 +0100 Subject: Unify new project namespace select and path input --- app/assets/stylesheets/framework/forms.scss | 8 ++++++++ app/helpers/namespaces_helper.rb | 6 +++--- app/views/projects/new.html.haml | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 0edfe24f195..95674c5812b 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -88,7 +88,15 @@ label { } .input-group { + .select2-container { + display: table-cell; + width: 200px !important; + } .input-group-addon { background-color: #f7f8fa; } + .input-group-addon:not(:first-child):not(:last-child) { + border-left: 0; + border-right: 0; + } } diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index e7f3cb21038..faba418c4db 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -1,10 +1,10 @@ module NamespacesHelper - def namespaces_options(selected = :current_user, scope = :default) + def namespaces_options(selected = :current_user, display_path: false) groups = current_user.owned_groups + current_user.masters_groups users = [current_user.namespace] - group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ] - users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ] + group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [display_path ? g.path : g.human_name, g.id]} ] + users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [display_path ? u.path : u.human_name, u.id]} ] options = [] options << group_opts diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index c9d1fc3da21..f6355ea6703 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -11,16 +11,16 @@ Project path .col-sm-10 .input-group - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true - .input-group-addon - \.git - - - if current_user.can_select_namespace? - .form-group - = f.label :namespace_id, class: 'control-label' do - %span Namespace - .col-sm-10 - = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2} + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2', tabindex: 1} + .input-group-addon + \/ + - else + .input-group-addon + #{root_url}#{current_user.username}/ + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - if import_sources_enabled? .project-import.js-toggle-container -- cgit v1.2.1 From 37ab1120c8a252aa29702ef141ff4ea1163e0e75 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:03 +0100 Subject: Move "Add Group" button higher up in new project form --- app/views/projects/new.html.haml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index f6355ea6703..a4bd8d54af2 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -22,6 +22,11 @@ #{root_url}#{current_user.username}/ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + - if import_sources_enabled? .project-import.js-toggle-container .form-group @@ -96,14 +101,6 @@ .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 - - if current_user.can_create_group? - .pull-right - .light.inline - .space-right - Need a group for several dependent projects? - = link_to new_group_path, class: "btn" do - Create a group - .save-project-loader.hide .center %h2 -- cgit v1.2.1 From a895d5d3b2252cd962b8ea19d2c1092cbee7fca6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:32 +0100 Subject: Use select2 for bulk issue edit status --- app/assets/javascripts/issues.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 40bb9e9cb0c..ac9e022e727 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -29,7 +29,7 @@ $('#filter_issue_search').val($('#issue_search').val()) initSelects: -> - $("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true) + $("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true) $("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true) $("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true) $("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true) -- cgit v1.2.1 From f385988d1648e54218d68e02a25eaad048b04d0b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:10:43 +0100 Subject: Use "Any Label" and "Any Milestone" in selects rather than the ambiguous "Any" option --- app/models/label.rb | 2 +- app/models/milestone.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/label.rb b/app/models/label.rb index b306aecbac1..bef6063fe88 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -17,7 +17,7 @@ class Label < ActiveRecord::Base # Requests that have no label assigned. LabelStruct = Struct.new(:title, :name) None = LabelStruct.new('No Label', 'No Label') - Any = LabelStruct.new('Any', '') + Any = LabelStruct.new('Any Label', '') DEFAULT_COLOR = '#428BCA' diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c2642b75b8a..d8c7536cd31 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,9 +16,9 @@ class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. - MilestoneStruct = Struct.new(:title, :name) - None = MilestoneStruct.new('No Milestone', 'No Milestone') - Any = MilestoneStruct.new('Any', '') + MilestoneStruct = Struct.new(:title, :name, :id) + None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) + Any = MilestoneStruct.new('Any Milestone', '', -1) include InternalId include Sortable -- cgit v1.2.1 From af541515fa49756d4d23719029648276ed6c2f5b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:11:32 +0100 Subject: Remove "none" username for "Unassigned" and "Any User" select options --- app/assets/javascripts/users_select.js.coffee | 13 +++++-------- app/assets/stylesheets/framework/selects.scss | 8 +++++++- app/helpers/issues_helper.rb | 7 ++++--- app/helpers/selects_helper.rb | 14 ++++++++------ app/views/shared/issuable/_filter.html.haml | 4 ++-- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index f5db74d84e7..12abf806bfa 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -32,17 +32,15 @@ class @UsersSelect if showNullUser nullUser = { name: 'Unassigned', - avatar: null, - username: 'none', id: 0 } data.results.unshift(nullUser) if showAnyUser + name = showAnyUser + name = 'Any User' if name == true anyUser = { - name: 'Any', - avatar: null, - username: 'none', + name: name, id: null } data.results.unshift(anyUser) @@ -50,7 +48,6 @@ class @UsersSelect if showEmailUser && data.results.length == 0 && query.term.match(/^[^@]+@[^@]+$/) emailUser = { name: "Invite \"#{query.term}\"", - avatar: null, username: query.term, id: query.term } @@ -82,10 +79,10 @@ class @UsersSelect else avatar = gon.default_avatar_url - "
    + "
    #{user.name}
    -
    #{user.username}
    +
    #{user.username || ""}
    " formatSelection: (user) -> diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 78fff58d232..5781983c48f 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -123,10 +123,16 @@ } .user-result { + min-height: 24px; + .user-image { float: left; } - .user-name { + + &.no-username { + .user-name { + line-height: 24px; + } } } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..bfaae223b15 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -44,9 +44,10 @@ module IssuesHelper end def bulk_update_milestone_options - options_for_select([['None (backlog)', -1]]) + - options_from_collection_for_select(project_active_milestones, 'id', - 'title', params[:milestone_id]) + milestones = project_active_milestones.to_a + milestones.unshift(Milestone::None) + + options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id]) end def milestone_options(object) diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 7e54d4d1b5b..7e175d0de8a 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -15,12 +15,14 @@ module SelectsHelper html = { class: css_class, - 'data-placeholder' => placeholder, - 'data-null-user' => null_user, - 'data-any-user' => any_user, - 'data-email-user' => email_user, - 'data-first-user' => first_user, - 'data-current-user' => current_user + data: { + placeholder: placeholder, + null_user: null_user, + any_user: any_user, + email_user: email_user, + first_user: first_user, + current_user: current_user + } } unless opts[:scope] == :all diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index d1231438ee4..c159e85ef21 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -31,11 +31,11 @@ .issues-other-filters .filter-item.inline = users_select_tag(:assignee_id, selected: params[:assignee_id], - placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true, first_user: true, current_user: true) + placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true) .filter-item.inline = users_select_tag(:author_id, selected: params[:author_id], - placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true, current_user: true) + placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true) .filter-item.inline.milestone-filter = select_tag('milestone_title', projects_milestones_options, -- cgit v1.2.1 From 5d0ab2b9e9854733ba174cd3d403600f45c7dbd1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:12:16 +0100 Subject: Make style of select2 box more consistent with controls --- app/assets/stylesheets/framework/selects.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 5781983c48f..5e5ae3d0af8 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -15,6 +15,16 @@ border-left: none; padding-top: 5px; } + + .select2-chosen { + color: $gl-text-color; + } + + &.select2-default { + .select2-chosen { + color: #999; + } + } } } @@ -23,6 +33,7 @@ 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); -- cgit v1.2.1 From 68ee01cfa0de7656b09eb743aabccf7ca8746d57 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:12:46 +0100 Subject: Use placeholders rather than blank options in bulk issue edit selects. --- app/views/shared/issuable/_filter.html.haml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index c159e85ef21..ac6c248ccf1 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -53,12 +53,16 @@ - if controller.controller_name == 'issues' .issues_bulk_update.hide = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do - = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control') - = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true) - = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") + .filter-item.inline + = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), include_blank: true, data: { placeholder: "Status" }) + .filter-item.inline + = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true) + .filter-item.inline + = select_tag('update[milestone_id]', bulk_update_milestone_options, include_blank: true, data: { placeholder: "Milestone" }) = hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag :state_event, params[:state_event] - = button_tag "Update issues", class: "btn update_selected_issues btn-save" + .filter-item.inline + = button_tag "Update issues", class: "btn update_selected_issues btn-save" :javascript new UsersSelect(); -- cgit v1.2.1 From d399ffa244511b0bfe0b4de746b6775a3a456465 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:18:46 +0100 Subject: Use "Assigned to USER" tooltip in issue list item --- app/helpers/projects_helper.rb | 5 +++-- app/views/projects/issues/_issue.html.haml | 2 +- app/views/projects/merge_requests/_merge_request.html.haml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index c0c51aae039..48729e5260e 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -21,7 +21,7 @@ module ProjectsHelper end def link_to_member(project, author, opts = {}) - default_opts = { avatar: true, name: true, size: 16, author_class: 'author' } + default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } opts = default_opts.merge(opts) return "(deleted)" unless author @@ -39,7 +39,8 @@ module ProjectsHelper if opts[:name] link_to(author_html, user_path(author), class: "author_link").html_safe else - link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe + title = opts[:title].sub(":name", sanitize(author.name)) + link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => title, container: 'body' } ).html_safe end end diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index d7657ee7e40..1d452dc601d 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -14,7 +14,7 @@ %span CLOSED - if issue.assignee - = link_to_member(@project, issue.assignee, name: false) + = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") - note_count = issue.notes.user.count - if note_count > 0   diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 83e8ad11989..5842d7ff163 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -20,7 +20,7 @@ - note_count = merge_request.mr_and_commit_notes.user.count - if merge_request.assignee   - = link_to_member(merge_request.source_project, merge_request.assignee, name: false) + = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") - if note_count > 0   %span -- cgit v1.2.1 From dc809983a4d5c9f61af9ca4392010d243271f7d3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:18:59 +0100 Subject: Link issue/MR list item comments counter to comments --- app/views/projects/issues/_issue.html.haml | 8 ++++---- app/views/projects/merge_requests/_merge_request.html.haml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 1d452dc601d..4f6ff20caf4 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -18,13 +18,13 @@ - note_count = issue.notes.user.count - if note_count > 0   - %span - %i.fa.fa-comments + = link_to issue_path(issue) + "#notes" do + = icon('comments') = note_count - else   - %span.issue-no-comments - %i.fa.fa-comments + = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do + = icon('comments') = 0 .issue-info diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 5842d7ff163..2f2ebc0894a 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -23,13 +23,13 @@ = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") - if note_count > 0   - %span - %i.fa.fa-comments + = link_to merge_request_path(merge_request) + "#notes" do + = icon('comments') = note_count - else   - %span.merge-request-no-comments - %i.fa.fa-comments + = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do + = icon('comments') = 0 .merge-request-info -- cgit v1.2.1 From 08ee38a1c5d55d66070aa6430afa991a395a7da0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:19:25 +0100 Subject: Link milestone item to issues with that milestone --- app/views/projects/issues/_issue.html.haml | 8 +++++--- app/views/projects/merge_requests/_merge_request.html.haml | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 4f6ff20caf4..2e50b603317 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -28,11 +28,13 @@ = 0 .issue-info - = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe + #{issue.to_reference} · + opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} + by #{link_to_member(@project, issue.author, avatar: false)} - if issue.milestone   - %span - %i.fa.fa-clock-o + = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do + = icon('clock-o') = issue.milestone.title - if issue.tasks? %span.task-status diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 2f2ebc0894a..4940bc78435 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -33,11 +33,13 @@ = 0 .merge-request-info - = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe + \##{merge_request.iid} · + opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} + by #{link_to_member(@project, merge_request.author, avatar: false)} - if merge_request.milestone_id?   - %span - %i.fa.fa-clock-o + = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do + = icon('clock-o') = merge_request.milestone.title - if merge_request.target_project.default_branch != merge_request.target_branch   -- cgit v1.2.1 From fbe6432934973021acddcff1d3b0fd3ed5844d2b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:03 +0100 Subject: Move labels to second line of issue/MR list item --- app/views/projects/issues/_issue.html.haml | 8 +++++--- app/views/projects/merge_requests/_merge_request.html.haml | 12 +++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 2e50b603317..1eb71990e55 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,9 +6,6 @@ .issue-title %span.issue-title-text = link_to_gfm issue.title, issue_path(issue), class: "row_title" - .issue-labels - - issue.labels.each do |label| - = link_to_label(label, project: issue.project) .pull-right.light - if issue.closed? %span @@ -36,7 +33,12 @@ = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do = icon('clock-o') = issue.milestone.title + - if issue.labels.any? +   + - issue.labels.each do |label| + = link_to_label(label, project: issue.project) - if issue.tasks? +   %span.task-status = issue.task_status diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 4940bc78435..8246a432c77 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -3,19 +3,16 @@ .merge-request-title %span.merge-request-title-text = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" - .merge-request-labels - - merge_request.labels.each do |label| - = link_to_label(label, project: merge_request.project) .pull-right.light - if ci_commit = render_ci_status(ci_commit) - if merge_request.merged? %span - %i.fa.fa-check + = icon('check') MERGED - elsif merge_request.closed? %span - %i.fa.fa-ban + = icon('ban') CLOSED - note_count = merge_request.mr_and_commit_notes.user.count - if merge_request.assignee @@ -41,12 +38,17 @@ = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') = merge_request.milestone.title + - if merge_request.labels.any? +   + - merge_request.labels.each do |label| + = link_to_label(label, project: merge_request.project) - if merge_request.target_project.default_branch != merge_request.target_branch   %span %i.fa.fa-code-fork = merge_request.target_branch - if merge_request.tasks? +   %span.task-status = merge_request.task_status -- cgit v1.2.1 From ecd9f5ec027a9801bf39a32facf66fe13667b2ca Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:43 +0100 Subject: Add "No Milestone" option to issue milestone select --- app/helpers/issues_helper.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 493f370d9a9..efd6418c708 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -50,8 +50,10 @@ module IssuesHelper end def milestone_options(object) - options_from_collection_for_select(object.project.milestones.active, - 'id', 'title', object.milestone_id) + milestones = object.project.milestones.active.to_a + milestones.unshift(Milestone::None) + + options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) end def issue_box_class(item) -- cgit v1.2.1 From 89488a7cf930c41028a6e2e842cf0b6b8133e78b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:03 +0100 Subject: Move "Please review the contribution guide" next to MR/issue Submit button --- app/views/shared/issuable/_form.html.haml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..2e1656954e1 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -94,15 +94,17 @@ = link_to 'Change branches', mr_change_branches_path(@merge_request) .form-actions - - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted? - %p - Please review the - %strong #{link_to 'guidelines for contribution', guide_url} - to this repository. - if issuable.new_record? = f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else = f.submit 'Save changes', class: 'btn btn-save' + + - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) + .inline.prepend-left-10 + Please review the + %strong #{link_to 'contribution guidelines', guide_url} + for this project. + - if issuable.new_record? - cancel_project = issuable.source_project - else -- cgit v1.2.1 From 6aa43974d4bb674f457833ba6f314bbd7c8a2578 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:23 +0100 Subject: Move subscription info below label info in issue/MR sidebar --- app/views/projects/issues/_discussion.html.haml | 7 ------- app/views/projects/merge_requests/_discussion.html.haml | 7 ------- app/views/shared/issuable/_context.html.haml | 8 ++++++++ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 8f0a1ed9be2..b5f522f2079 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -29,10 +29,3 @@ .issuable-affix .context = render 'shared/issuable/context', issuable: @issue - - - if @issue.labels.any? - .issuable-context-title - %label Labels - .issue-show-labels - - @issue.labels.each do |label| - = link_to_label(label) diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 2b3c3eff5e4..00b72e5a1a1 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -26,10 +26,3 @@ .issuable-affix .context = render 'shared/issuable/context', issuable: @merge_request - - - if @merge_request.labels.any? - .issuable-context-title - %label Labels - .merge-request-show-labels - - @merge_request.labels.each do |label| - = link_to_label(label) diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index be66256c7b0..5d00c871080 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -29,6 +29,14 @@ = hidden_field_tag :issuable_context = f.submit class: 'btn hide' + - if issuable.labels.any? + %div.prepend-top-default.clearfix + .issuable-context-title + %label Labels + .merge-request-show-labels + - issuable.labels.each do |label| + = link_to_label(label) + - if current_user - subscribed = issuable.subscribed?(current_user) %div.prepend-top-20.clearfix -- cgit v1.2.1 From 3922d6c22c7c148e100eac1635a8d3f0565585db Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:21:39 +0100 Subject: Move (Un)subscribe button below subscription status --- app/views/shared/issuable/_context.html.haml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 5d00c871080..0b87f11c4db 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -41,17 +41,16 @@ - subscribed = issuable.subscribed?(current_user) %div.prepend-top-20.clearfix .issuable-context-title - %label - Subscription: - %button.btn.btn-block.subscribe-button{:type => 'button'} - = icon('eye') - %span= subscribed ? 'Unsubscribe' : 'Subscribe' + %label Subscription - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' .subscription-status{data: {status: subscribtion_status}} .description-block.unsubscribed{class: ( 'hidden' if subscribed )} You're not receiving notifications from this thread. .description-block.subscribed{class: ( 'hidden' unless subscribed )} You're receiving notifications because you're subscribed to this thread. + %button.btn.btn-block.subscribe-button{:type => 'button'} + = icon('eye') + %span= subscribed ? 'Unsubscribe' : 'Subscribe' :javascript new Subscription("#{toggle_subscription_path(issuable)}"); -- cgit v1.2.1 From e3233016be0a94b88e94700507760c9f672661f4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:22:11 +0100 Subject: Use "opened by" instead of "created by" in issue/MR header, like in list --- app/views/projects/issues/show.html.haml | 3 ++- app/views/projects/merge_requests/show/_mr_title.html.haml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index f01bf2505da..e2de17cee6d 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -11,7 +11,8 @@ Open %span.issue-id Issue ##{@issue.iid} %span.creator - · created by #{link_to_member(@project, @issue.author, size: 24)} + · + opened by #{link_to_member(@project, @issue.author, size: 24)} · = time_ago_with_tooltip(@issue.created_at, placement: 'bottom', html_class: 'issue_created_ago') - if @issue.updated_at != @issue.created_at diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 2bf9cd597a4..4dfe46e2b86 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -4,7 +4,7 @@ %span.issue-id Merge Request ##{@merge_request.iid} %span.creator · - created by #{link_to_member(@project, @merge_request.author, size: 24)} + opened by #{link_to_member(@project, @merge_request.author, size: 24)} · = time_ago_with_tooltip(@merge_request.created_at) - if @merge_request.updated_at != @merge_request.created_at -- cgit v1.2.1 From 7a65cb3abf63bcd87856c6220e87c7fccfdb8a1f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:22:33 +0100 Subject: Don't reset target branch when choosing to "Change branches" in MR --- app/helpers/merge_requests_helper.rb | 5 +++-- app/views/projects/merge_requests/new.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index b804d4f4e3b..cc4243e1559 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -49,8 +49,9 @@ module MergeRequestsHelper source_project_id: @merge_request.source_project_id, target_project_id: @merge_request.target_project_id, source_branch: @merge_request.source_branch, - target_branch: nil - } + target_branch: @merge_request.target_branch, + }, + change_branches: true ) end diff --git a/app/views/projects/merge_requests/new.html.haml b/app/views/projects/merge_requests/new.html.haml index 9fdde80c6d9..d259968030e 100644 --- a/app/views/projects/merge_requests/new.html.haml +++ b/app/views/projects/merge_requests/new.html.haml @@ -1,7 +1,7 @@ - page_title "New Merge Request" = render "header_title" -- if @merge_request.can_be_created +- if @merge_request.can_be_created && !params[:change_branches] = render 'new_submit' - else = render 'new_compare' diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 2a333222fb2..789befc9df2 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -112,7 +112,6 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I fill out an invalid "Merge Request On Forked Project" merge request' do - select "Select branch", from: "merge_request_target_branch" expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s expect(find(:select, "merge_request_source_branch", {}).value).to eq "" -- cgit v1.2.1 From 1df2908b27bbce4a0c67139dc3358799ffa55550 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:23:36 +0100 Subject: Link to branches from MR "Request to merge X into Y" sentence --- app/views/projects/merge_requests/_show.html.haml | 5 +++-- app/views/projects/merge_requests/widget/_merged.html.haml | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index eeaa72ed21b..c52d870f6e2 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,9 +26,10 @@ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) .normal %span Request to merge - %span.label-branch #{source_branch_with_namespace(@merge_request)} + = link_to source_branch_with_namespace(@merge_request), namespace_project_commits_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), class: "label-branch" %span into - %span.label-branch #{@merge_request.target_branch} + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index a788fcea23f..ac08e0b498a 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -10,7 +10,8 @@ - if !@merge_request.source_branch_exists? = succeed '.' do The changes were merged into - %span.label-branch= @merge_request.target_branch + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch The source branch has been removed. - elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) @@ -18,7 +19,8 @@ %p = succeed '.' do The changes were merged into - %span.label-branch= @merge_request.target_branch + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch You can remove the source branch now. = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), remote: true, method: :delete, class: "btn btn-primary btn-sm remove_source_branch" do %i.fa.fa-times -- cgit v1.2.1 From 6c54823dce9aecf54f78b41180ec75efbc1391f4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:24:29 +0100 Subject: Use select2 placeholder instead of blank option --- app/views/projects/merge_requests/_new_compare.html.haml | 4 ++-- app/views/shared/issuable/_context.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..f5a512d385b 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -10,7 +10,7 @@ .panel-body = f.select(:source_project_id, [[@merge_request.source_project_path,@merge_request.source_project.id]] , {}, { class: 'source_project select2 span3', disabled: @merge_request.persisted?, required: true })   - = f.select(:source_branch, @merge_request.source_branches, { include_blank: "Select branch" }, {class: 'source_branch select2 span2', required: true}) + = f.select(:source_branch, @merge_request.source_branches, { include_blank: true }, { class: 'source_branch select2 span2', required: true, data: { placeholder: "Select source branch" } }) .panel-footer .mr_source_commit @@ -22,7 +22,7 @@ - projects = @project.forked_from_project.nil? ? [@project] : [@project, @project.forked_from_project] = f.select(:target_project_id, options_from_collection_for_select(projects, 'id', 'path_with_namespace', f.object.target_project_id), {}, { class: 'target_project select2 span3', disabled: @merge_request.persisted?, required: true })   - = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, {class: 'target_branch select2 span2', required: true}) + = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', required: true, data: { placeholder: "Select target branch" } }) .panel-footer .mr_target_commit diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 0b87f11c4db..e6d753a8b7f 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -25,7 +25,7 @@ none .issuable-context-selectbox - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) + = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) = hidden_field_tag :issuable_context = f.submit class: 'btn hide' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 2e1656954e1..942551c2682 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -39,9 +39,9 @@ Assign to .col-sm-10 = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", - placeholder: 'Select a user', class: 'custom-form-control', null_user: true, + placeholder: 'Select assignee', class: 'custom-form-control', null_user: true, selected: issuable.assignee_id, project: @target_project || @project, - first_user: true, current_user: true) + first_user: true, current_user: true, include_blank: true)   = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group @@ -52,7 +52,7 @@ .col-sm-10 - if milestone_options(issuable).present? = f.select(:milestone_id, milestone_options(issuable), - { include_blank: 'Select milestone' }, { class: 'select2' }) + { include_blank: true }, { class: 'select2', data: { placeholder: 'Select milestone' } }) - else .prepend-top-10 %span.light No open milestones available. @@ -66,7 +66,7 @@ .col-sm-10 - if issuable.project.labels.any? = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, - { selected: issuable.label_ids }, multiple: true, class: 'select2' + { selected: issuable.label_ids }, multiple: true, class: 'select2', data: { placeholder: "Select labels" } - else .prepend-top-10 %span.light No labels yet. @@ -88,7 +88,7 @@ %i.fa.fa-code-fork Target Branch .col-sm-10 - = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? }) + = f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} }) - if @merge_request.new_record? %p.help-block = link_to 'Change branches', mr_change_branches_path(@merge_request) -- cgit v1.2.1 From 3b03987a27f3b6b720251b02abfadc4e9ba06fe8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:24:44 +0100 Subject: Redesign multiple select2 options --- app/assets/stylesheets/framework/selects.scss | 41 ++++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index 78fff58d232..f541444401c 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -48,17 +48,38 @@ color: #313236; } +.select2-container-multi { + .select2-choices { + @include border-radius(2px); + border-color: $input-border; + background: white; + padding-left: $gl-padding / 2; + + .select2-search-field input { + padding: $gl-padding / 2; + font-size: 13px; + height: auto; + font-family: inherit; + font-size: inherit; + } -.select2-container-multi .select2-choices { - @include border-radius(2px); - border-color: #CCC; -} - -.select2-container-multi .select2-choices .select2-search-field input { - padding: 8px 14px; - font-size: 13px; - line-height: 18px; - height: auto; + .select2-search-choice { + margin: 8px 0 0 8px; + background: white; + box-shadow: none; + border-color: $input-border; + color: $gl-text-color; + line-height: 15px; + + .select2-search-choice-close { + top: 5px; + } + + &.select2-search-choice-focus { + border-color: $gl-text-color; + } + } + } } .select2-drop-active { -- cgit v1.2.1 From 05c9d882166710a26e27a7b79e7855db47ca43c0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:25:03 +0100 Subject: Only show manual merge instructions if current user can merge --- app/views/projects/merge_requests/_show.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index c52d870f6e2..09b228b7c0c 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -34,8 +34,8 @@ = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" - - if @merge_request.open? && @merge_request.can_be_merged? - .light.append-bottom-20 + - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) + .light.prepend-top-default You can also accept this merge request manually using the = succeed '.' do = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" -- cgit v1.2.1 From ed74fa73e227b9666f3f38f17b35a5cf8328fa44 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:27:29 +0100 Subject: Use consistent casing for page titles --- app/views/admin/labels/edit.html.haml | 8 ++------ app/views/admin/labels/new.html.haml | 6 ++---- app/views/admin/users/edit.html.haml | 3 --- app/views/groups/edit.html.haml | 3 +-- app/views/groups/new.html.haml | 7 ++++++- app/views/projects/blob/new.html.haml | 5 ++--- app/views/projects/branches/new.html.haml | 5 +++-- app/views/projects/deploy_keys/new.html.haml | 2 +- app/views/projects/issues/_form.html.haml | 3 --- app/views/projects/issues/edit.html.haml | 6 ++++++ app/views/projects/issues/new.html.haml | 4 ++++ app/views/projects/labels/edit.html.haml | 8 ++------ app/views/projects/labels/new.html.haml | 6 ++---- app/views/projects/merge_requests/_new_compare.html.haml | 3 ++- app/views/projects/merge_requests/_new_submit.html.haml | 2 +- app/views/projects/merge_requests/edit.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 7 ------- app/views/projects/milestones/edit.html.haml | 6 ++++++ app/views/projects/milestones/new.html.haml | 6 ++++++ app/views/projects/new.html.haml | 7 ++++++- app/views/projects/services/_form.html.haml | 4 ---- app/views/projects/snippets/edit.html.haml | 2 +- app/views/projects/snippets/new.html.haml | 2 +- app/views/projects/tags/new.html.haml | 2 +- app/views/shared/_confirm_modal.html.haml | 3 ++- app/views/snippets/edit.html.haml | 2 +- app/views/snippets/new.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/source/browse_files.rb | 2 +- 29 files changed, 62 insertions(+), 58 deletions(-) diff --git a/app/views/admin/labels/edit.html.haml b/app/views/admin/labels/edit.html.haml index 45c62a76259..309aedceded 100644 --- a/app/views/admin/labels/edit.html.haml +++ b/app/views/admin/labels/edit.html.haml @@ -1,9 +1,5 @@ - page_title "Edit", @label.name, "Labels" -%h3 - Edit label - %span.light #{@label.name} -.back-link - = link_to admin_labels_path do - ← To labels list +%h3.page-title + Edit Label %hr = render 'form' diff --git a/app/views/admin/labels/new.html.haml b/app/views/admin/labels/new.html.haml index 8d298ad20f7..0135ad0723d 100644 --- a/app/views/admin/labels/new.html.haml +++ b/app/views/admin/labels/new.html.haml @@ -1,7 +1,5 @@ - page_title "New Label" -%h3 New label -.back-link - = link_to admin_labels_path do - ← To labels list +%h3.page-title + New Label %hr = render 'form' diff --git a/app/views/admin/users/edit.html.haml b/app/views/admin/users/edit.html.haml index a8837d74dd9..3b6fd71500d 100644 --- a/app/views/admin/users/edit.html.haml +++ b/app/views/admin/users/edit.html.haml @@ -1,8 +1,5 @@ - page_title "Edit", @user.name, "Users" %h3.page-title Edit user: #{@user.name} -.back-link - = link_to admin_user_path(@user) do - ← Back to user page %hr = render 'form' diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 57308a661c0..b5afb4ae1c2 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -3,8 +3,7 @@ .panel.panel-default .panel-heading - %strong= @group.name - group settings: + Group settings .panel-body = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f| - if @group.errors.any? diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 0665cdf387a..3e602559ae0 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -1,5 +1,10 @@ - page_title 'New Group' -- header_title 'New Group' +- header_title "Groups", dashboard_groups_path + +%h3.page-title + New Group +%hr + = form_for @group, html: { class: 'group-form form-horizontal' } do |f| - if @group.errors.any? .alert.alert-danger diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml index 1ff68005450..167fa615182 100644 --- a/app/views/projects/blob/new.html.haml +++ b/app/views/projects/blob/new.html.haml @@ -1,9 +1,8 @@ - page_title "New File", @path.presence, @ref = render "header_title" -.gray-content-block.top-block - %h3.page-title - Create New File +%h3.page-title + New File .file-editor = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index f5577042ca4..d103a713c54 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -6,8 +6,9 @@ %button{ type: "button", class: "close", "data-dismiss" => "alert"} × = @error %h3.page-title - %i.fa.fa-code-fork - New branch + New Branch +%hr + = form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do .form-group = label_tag :branch_name, 'Name for new branch', class: 'control-label' diff --git a/app/views/projects/deploy_keys/new.html.haml b/app/views/projects/deploy_keys/new.html.haml index 01c810aee18..01fab3008a7 100644 --- a/app/views/projects/deploy_keys/new.html.haml +++ b/app/views/projects/deploy_keys/new.html.haml @@ -1,5 +1,5 @@ - page_title "New Deploy Key" -%h3.page-title New Deploy key +%h3.page-title New Deploy Key %hr = render 'form' diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index f39bb7d2574..e0e26a26dae 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,7 +1,4 @@ %div.issue-form-holder - %h3.page-title= @issue.new_record? ? "Create Issue" : "Edit Issue ##{@issue.iid}" - %hr - = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| = render 'shared/issuable/form', f: f, issuable: @issue diff --git a/app/views/projects/issues/edit.html.haml b/app/views/projects/issues/edit.html.haml index 53b6f0879c9..20216297d25 100644 --- a/app/views/projects/issues/edit.html.haml +++ b/app/views/projects/issues/edit.html.haml @@ -1,2 +1,8 @@ - page_title "Edit", "#{@issue.title} (##{@issue.iid})", "Issues" += render "header_title" + +%h3.page-title + Edit Issue ##{@issue.iid} +%hr + = render "form" diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index 153447baa1b..b317a0c1cf4 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,4 +1,8 @@ - page_title "New Issue" = render "header_title" +%h3.page-title + New Issue +%hr + = render "form" diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml index bc4ab0ca27c..675a805e12f 100644 --- a/app/views/projects/labels/edit.html.haml +++ b/app/views/projects/labels/edit.html.haml @@ -1,11 +1,7 @@ - page_title "Edit", @label.name, "Labels" = render "header_title" -%h3 - Edit label - %span.light #{@label.name} -.back-link - = link_to namespace_project_labels_path(@project.namespace, @project) do - ← To labels list +%h3.page-title + Edit Label %hr = render 'form' diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml index 342ad4f3f95..e20fd7d6891 100644 --- a/app/views/projects/labels/new.html.haml +++ b/app/views/projects/labels/new.html.haml @@ -1,9 +1,7 @@ - page_title "New Label" = render "header_title" -%h3 New label -.back-link - = link_to namespace_project_labels_path(@project.namespace, @project) do - ← To labels list +%h3.page-title + New Label %hr = render 'form' diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..46e72e9dee5 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -1,4 +1,5 @@ -%p.lead Compare branches for new Merge Request +%h3.page-title + New Merge Request = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline js-requires-input" } do |f| .hide.alert.alert-danger.mr-compare-errors diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 6244d3ba0b4..9fa9cc56126 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -1,5 +1,5 @@ %h3.page-title - New merge request + New Merge Request %p.slead - source_title, target_title = format_mr_branch_names(@merge_request) From diff --git a/app/views/projects/merge_requests/edit.html.haml b/app/views/projects/merge_requests/edit.html.haml index 303ca0a880b..fc62bb5bce9 100644 --- a/app/views/projects/merge_requests/edit.html.haml +++ b/app/views/projects/merge_requests/edit.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - = "Edit merge request ##{@merge_request.iid}" + Edit Merge Request ##{@merge_request.iid} %hr = render 'form' diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index 24879b19d2b..cc29970f07f 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -1,10 +1,3 @@ -%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}" -.back-link - = link_to namespace_project_milestones_path(@project.namespace, @project) do - ← To milestones - -%hr - = form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form js-requires-input'} do |f| -if @milestone.errors.any? .alert.alert-danger diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml index e9dc0b77462..43f8863163d 100644 --- a/app/views/projects/milestones/edit.html.haml +++ b/app/views/projects/milestones/edit.html.haml @@ -1,3 +1,9 @@ - page_title "Edit", @milestone.title, "Milestones" = render "header_title" + +%h3.page-title + Edit Milestone ##{@milestone.iid} + +%hr + = render "form" diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml index 9ba9acb6f77..0d016f78313 100644 --- a/app/views/projects/milestones/new.html.haml +++ b/app/views/projects/milestones/new.html.haml @@ -1,3 +1,9 @@ - page_title "New Milestone" = render "header_title" + +%h3.page-title + New Milestone + +%hr + = render "form" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index c9d1fc3da21..fa75a624222 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,5 +1,10 @@ - page_title 'New Project' -- header_title 'New Project' +- header_title "Projects", root_path + +%h3.page-title + New Project +%hr + .project-edit-container .project-edit-errors = render 'projects/errors' diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index e1823b51198..fecd157c6dc 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -4,10 +4,6 @@ %p= @service.description -.back-link - = link_to namespace_project_services_path(@project.namespace, @project) do - ← to services - %hr = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml index e69f2d99709..dc3ea1fcf12 100644 --- a/app/views/projects/snippets/edit.html.haml +++ b/app/views/projects/snippets/edit.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - Edit snippet + Edit Snippet %hr = render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet), visibility_level: @snippet.visibility_level diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml index 67cd69fd215..e57237991b4 100644 --- a/app/views/projects/snippets/new.html.haml +++ b/app/views/projects/snippets/new.html.haml @@ -2,6 +2,6 @@ = render "header_title" %h3.page-title - New snippet + New Snippet %hr = render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet), visibility_level: default_snippet_visibility diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 86aa15dc5b3..97abdb239ed 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -7,7 +7,7 @@ = @error %h3.page-title - New git tag + New Tag %hr = form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 2a44817e05a..9bc2d33c27e 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -3,7 +3,8 @@ .modal-content .modal-header %a.close{href: "#", "data-dismiss" => "modal"} × - %h4 Confirmation required + %h3.page-title + Confirmation required .modal-body %p.cred.lead.js-confirm-text diff --git a/app/views/snippets/edit.html.haml b/app/views/snippets/edit.html.haml index 1a380035661..82f44a9a5c3 100644 --- a/app/views/snippets/edit.html.haml +++ b/app/views/snippets/edit.html.haml @@ -1,5 +1,5 @@ - page_title "Edit", @snippet.title, "Snippets" %h3.page-title - Edit snippet + Edit Snippet %hr = render 'shared/snippets/form', url: snippet_path(@snippet), visibility_level: @snippet.visibility_level diff --git a/app/views/snippets/new.html.haml b/app/views/snippets/new.html.haml index a74d5e792ad..79e2392490d 100644 --- a/app/views/snippets/new.html.haml +++ b/app/views/snippets/new.html.haml @@ -1,5 +1,5 @@ - page_title "New Snippet" %h3.page-title - New snippet + New Snippet %hr = render "shared/snippets/form", url: snippets_path(@snippet), visibility_level: default_snippet_visibility diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 2a333222fb2..155a5ff58ae 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -41,7 +41,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps click_button "Compare branches" - expect(page).to have_content "New merge request" + expect(page).to have_content "New Merge Request" fill_in "merge_request_title", with: "Merge Request On Forked Project" end diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f40e0f0d528..99e1e9b4af6 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I can see new file page' do - expect(page).to have_content "Create New File" + expect(page).to have_content "New File" expect(page).to have_content "Commit message" end -- cgit v1.2.1 From 67119e15c03d4d1e8abd2ce2cfe1b40aba35c709 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:30:12 +0100 Subject: Use consistent casing for form field labels --- app/views/admin/labels/_form.html.haml | 2 +- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/branches/new.html.haml | 2 +- app/views/projects/labels/_form.html.haml | 2 +- app/views/projects/tags/new.html.haml | 4 ++-- app/views/shared/_group_form.html.haml | 2 +- app/views/shared/_new_commit_form.html.haml | 3 +-- app/views/shared/issuable/_form.html.haml | 22 ++++++---------------- features/steps/admin/labels.rb | 8 ++++---- spec/features/issues_spec.rb | 2 +- 10 files changed, 19 insertions(+), 30 deletions(-) diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml index a5ace4e7a3b..eaa94ed9e36 100644 --- a/app/views/admin/labels/_form.html.haml +++ b/app/views/admin/labels/_form.html.haml @@ -12,7 +12,7 @@ .col-sm-10 = f.text_field :title, class: "form-control", required: true .form-group - = f.label :color, "Background Color", class: 'control-label' + = f.label :color, "Background color", class: 'control-label' .col-sm-10 .input-group .input-group-addon.label-color-preview   diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 13b5ffd17ff..377f0fa0129 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -7,7 +7,7 @@ .modal-body = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do .form-group - = label_tag :dir_name, 'Directory Name', class: 'control-label' + = 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' diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index d103a713c54..efb5298a5e5 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -11,7 +11,7 @@ = form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal js-requires-input" do .form-group - = label_tag :branch_name, 'Name for new branch', class: 'control-label' + = label_tag :branch_name, nil, class: 'control-label' .col-sm-10 = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' .form-group diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 4cf13492e99..291703bd60c 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -12,7 +12,7 @@ .col-sm-10 = f.text_field :title, class: "form-control js-quick-submit", required: true .form-group - = f.label :color, "Background Color", class: 'control-label' + = f.label :color, "Background color", class: 'control-label' .col-sm-10 .input-group .input-group-addon.label-color-preview   diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 97abdb239ed..91e0a5493bd 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -12,7 +12,7 @@ = form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do .form-group - = label_tag :tag_name, 'Name for new tag', class: 'control-label' + = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' .form-group @@ -21,7 +21,7 @@ = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' .help-block Branch name or commit SHA .form-group - = label_tag :message, 'Message', class: 'control-label' + = label_tag :message, nil, class: 'control-label' .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' .help-block (Optional) Entering a message will create an annotated tag. diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index c0a9923348e..67072b9fc2a 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -23,7 +23,7 @@ %li It will change the git path to repositories under this group. .form-group.group-description-holder - = f.label :description, 'Details', class: 'control-label' + = f.label :description, class: 'control-label' .col-sm-10 = f.text_area :description, maxlength: 250, class: 'form-control js-gfm-input', rows: 4 diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index 8636341c60d..ce52614e868 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -2,8 +2,7 @@ - unless @project.empty_repo? .form-group.branch - = label_tag 'branch', class: 'control-label' do - Branch + = label_tag 'new_branch', 'Target branch', class: 'control-label' .col-sm-10 = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..2a5fb534a3c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -30,13 +30,11 @@ = render 'projects/notes/hints' .clearfix .error-alert - %hr - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) + %hr .form-group .issue-assignee - = f.label :assignee_id, class: 'control-label' do - %i.fa.fa-user - Assign to + = f.label :assignee_id, "Assignee", class: 'control-label' .col-sm-10 = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", placeholder: 'Select a user', class: 'custom-form-control', null_user: true, @@ -46,9 +44,7 @@ = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' .form-group .issue-milestone - = f.label :milestone_id, class: 'control-label' do - %i.fa.fa-clock-o - Milestone + = f.label :milestone_id, "Milestone", class: 'control-label' .col-sm-10 - if milestone_options(issuable).present? = f.select(:milestone_id, milestone_options(issuable), @@ -60,9 +56,7 @@ - if can? current_user, :admin_milestone, issuable.project = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank .form-group - = f.label :label_ids, class: 'control-label' do - %i.fa.fa-tag - Labels + = f.label :label_ids, "Labels", class: 'control-label' .col-sm-10 - if issuable.project.labels.any? = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, @@ -78,15 +72,11 @@ %hr - if @merge_request.new_record? .form-group - = f.label :source_branch, class: 'control-label' do - %i.fa.fa-code-fork - Source Branch + = f.label :source_branch, class: 'control-label' .col-sm-10 = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) .form-group - = f.label :target_branch, class: 'control-label' do - %i.fa.fa-code-fork - Target Branch + = f.label :target_branch, class: 'control-label' .col-sm-10 = f.select(:target_branch, @merge_request.target_branches, { include_blank: "Select branch" }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record? }) - if @merge_request.new_record? diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index b45d98658bc..cb23b869658 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -45,21 +45,21 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do visit new_admin_label_path fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label \'bug\'' do visit new_admin_label_path fill_in 'Title', with: 'bug' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label with invalid color' do visit new_admin_label_path fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#12' + fill_in 'Background color', with: '#12' click_button 'Save' end @@ -101,7 +101,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' - fill_in 'Background Color', with: '#F15610' + fill_in 'Background color', with: '#F15610' click_button 'Save' end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 32fd4065bb4..0af5e6fc1a6 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -61,7 +61,7 @@ describe 'Issues', feature: true do it 'allows user to select unasigned', js: true do visit edit_namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_content "Assign to #{@user.name}" + expect(page).to have_content "Assignee #{@user.name}" first('#s2id_issue_assignee_id').click sleep 2 # wait for ajax stuff to complete -- cgit v1.2.1 From ca016903054fe53be08b8afeb53c019f44247cf9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:31:15 +0100 Subject: Add cancel button to forms that didn't have one already --- app/views/groups/new.html.haml | 1 + app/views/profiles/show.html.haml | 8 +++----- app/views/projects/new.html.haml | 1 + app/views/projects/services/_form.html.haml | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml index 3e602559ae0..4bc31cabea6 100644 --- a/app/views/groups/new.html.haml +++ b/app/views/groups/new.html.haml @@ -23,3 +23,4 @@ .form-actions = f.submit 'Create group', class: "btn btn-create", tabindex: 3 + = link_to 'Cancel', dashboard_groups_path, class: 'btn btn-cancel' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index ac7355dde1f..faab12ca0b9 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -96,8 +96,6 @@ = link_to 'Remove avatar', profile_avatar_path, data: { confirm: "Avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" - .row - .col-md-7 - .form-group - .col-sm-offset-2.col-sm-10 - = f.submit 'Save changes', class: "btn btn-success" + .form-actions + = f.submit 'Save changes', class: "btn btn-success" + = link_to "Cancel", user_path(current_user), class: "btn btn-cancel" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index fa75a624222..2670b9d9cda 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -100,6 +100,7 @@ .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 + = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' - if current_user.can_create_group? .pull-right diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index fecd157c6dc..ea5a2302a56 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -15,3 +15,4 @@ - if @service.valid? && @service.activated? - disabled = @service.can_test? ? '':'disabled' = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: "btn #{disabled}" + = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" -- cgit v1.2.1 From ffabf1df50744564ac99f358f13ab4b1c5d54284 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:33:14 +0100 Subject: Add cancel button to forms that didn't have one already --- app/views/profiles/preferences/show.html.haml | 2 +- app/views/projects/deploy_keys/_form.html.haml | 2 +- app/views/projects/edit.html.haml | 20 ++++++++++++-------- app/views/projects/labels/_form.html.haml | 6 ++++-- .../projects/merge_requests/_new_compare.html.haml | 4 ++-- app/views/projects/runners/edit.html.haml | 2 +- app/views/projects/services/_form.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 2 +- app/views/shared/snippets/_form.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/issues.rb | 4 ++-- features/steps/project/merge_requests.rb | 2 +- 12 files changed, 28 insertions(+), 22 deletions(-) diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index cc41d7dd813..877589dc390 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -54,4 +54,4 @@ .help-block Choose what content you want to see on a project's home page. .panel-footer - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 91675b3738e..085c9149b11 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -18,6 +18,6 @@ = f.text_area :key, class: "form-control thin_area", rows: 5 .form-actions - = f.submit 'Create', class: "btn-create btn" + = f.submit 'Create Deploy Key', class: "btn-create btn" = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 0c10de1604c..5e7c211a424 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -130,9 +130,11 @@ The project can be committed to. %br %strong Once active this project shows up in the search and on the dashboard. - = link_to 'Unarchive', unarchive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, - method: :post, class: "btn btn-success" + + .form-actions + = link_to 'Unarchive project', unarchive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." }, + method: :post, class: "btn btn-success" - else .panel.panel-warning .panel-heading @@ -144,9 +146,11 @@ It is hidden from the dashboard and doesn't show up in searches. %br %strong Archived projects cannot be committed to! - = link_to 'Archive', archive_namespace_project_path(@project.namespace, @project), - data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, - method: :post, class: "btn btn-warning" + + .form-actions + = link_to 'Archive project', archive_namespace_project_path(@project.namespace, @project), + data: { confirm: "Are you sure that you want to archive this project?\nAn archived project cannot be committed to." }, + method: :post, class: "btn btn-warning" - else .nothing-here-block Only the project owner can archive a project @@ -175,7 +179,7 @@ %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Rename', class: "btn btn-warning" + = f.submit 'Rename project', class: "btn btn-warning" - if can?(current_user, :change_namespace, @project) .panel.panel-default.panel.panel-danger @@ -194,7 +198,7 @@ %li You can only transfer the project to namespaces you manage. %li You will need to update your local repositories to point to the new location. .form-actions - = f.submit 'Transfer', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } + = f.submit 'Transfer project', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => transfer_project_message(@project) } - else .nothing-here-block Only the project owner can transfer a project diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 291703bd60c..2d4311412fd 100644 --- a/app/views/projects/labels/_form.html.haml +++ b/app/views/projects/labels/_form.html.haml @@ -28,6 +28,8 @@   .form-actions - = f.submit 'Save', class: 'btn btn-save js-save-button' + - if @label.persisted? + = f.submit 'Save changes', class: 'btn btn-save js-save-button' + - else + = f.submit 'Create Label', class: 'btn btn-create js-save-button' = link_to "Cancel", namespace_project_labels_path(@project.namespace, @project), class: 'btn btn-cancel' - diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index 46e72e9dee5..cc34f58c54f 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -52,8 +52,8 @@ are the same. - %div - = f.submit 'Compare branches', class: "btn btn-new mr-compare-btn" + .form-actions + = f.submit 'Compare branches and continue', class: "btn btn-new mr-compare-btn" :javascript var source_branch = $("#merge_request_source_branch") diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index a0324701690..eba03028af8 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -26,4 +26,4 @@ = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' .help-block You can setup jobs to only use runners with specific tags .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index ea5a2302a56..1b70880043a 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -10,7 +10,7 @@ = render 'shared/service_settings', form: form .form-actions - = form.submit 'Save', class: 'btn btn-save' + = form.submit 'Save changes', class: 'btn btn-save'   - if @service.valid? && @service.activated? - disabled = @service.can_test? ? '':'disabled' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 2a5fb534a3c..7f32fac6b2c 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -90,7 +90,7 @@ %strong #{link_to 'guidelines for contribution', guide_url} to this repository. - if issuable.new_record? - = f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' + = f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' - else = f.submit 'Save changes', class: 'btn btn-save' - if issuable.new_record? diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 913b6744844..b46ed0dc0ad 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -27,7 +27,7 @@ - if @snippet.new_record? = f.submit 'Create snippet', class: "btn-create btn" - else - = f.submit 'Save', class: "btn-save btn" + = f.submit 'Save changes', class: "btn-save btn" - if @snippet.project_id = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 155a5ff58ae..024dc5e72d3 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -39,7 +39,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps select "fix", from: "merge_request_source_branch" select "master", from: "merge_request_target_branch" - click_button "Compare branches" + click_button "Compare branches and continue" expect(page).to have_content "New Merge Request" fill_in "merge_request_title", with: "Merge Request On Forked Project" diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..202778b180b 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -72,13 +72,13 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps step 'I submit new issue "500 error on profile"' do fill_in "issue_title", with: "500 error on profile" - click_button "Submit new issue" + click_button "Submit issue" end step 'I submit new issue "500 error on profile" with label \'bug\'' do fill_in "issue_title", with: "500 error on profile" select 'bug', from: "Labels" - click_button "Submit new issue" + click_button "Submit issue" end step 'I click link "500 error on profile"' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index d5f2c4209a1..822cf0ffe1c 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -86,7 +86,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps select "feature", from: "merge_request_target_branch" click_button "Compare branches" fill_in "merge_request_title", with: "Wiki Feature" - click_button "Submit new merge request" + click_button "Submit merge request" end step 'project "Shop" have "Bug NS-04" open merge request' do -- cgit v1.2.1 From b04c5b069517aa511cdeebbad27169af8940b22c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:34:20 +0100 Subject: Use form-actions where appropriate --- app/views/groups/edit.html.haml | 3 ++- app/views/profiles/accounts/show.html.haml | 15 ++++++++++----- app/views/projects/blob/_new_dir.html.haml | 7 +++---- app/views/projects/blob/_upload.html.haml | 7 +++---- app/views/projects/edit.html.haml | 7 ++++--- app/views/shared/_confirm_modal.html.haml | 2 +- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index b5afb4ae1c2..8daac585960 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -44,4 +44,5 @@ %br %strong Removed group can not be restored! - = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" + .form-actions + = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index cd7b1b0fe03..2fd65cc9944 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -23,10 +23,13 @@ %p.cgray - if current_user.private_token = text_field_tag "token", current_user.private_token, class: "form-control" - %div - = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" - else %span You don`t have one yet. Click generate to fix it. + + .form-actions + - if current_user.private_token + = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" + - else = f.submit 'Generate', class: "btn btn-default btn-build-token" - unless current_user.ldap_user? @@ -54,7 +57,8 @@ %p Each time you log in you’ll be required to provide your username and password as usual, plus a randomly-generated code from your phone. - %div + + .form-actions = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success' - if button_based_providers.any? @@ -89,7 +93,7 @@ Saving new username %p.light = user_url(@user) - %div + .form-actions = f.submit 'Save username', class: "btn btn-warning" - if signup_enabled? @@ -104,7 +108,8 @@ - rp = current_user.personal_projects.count - unless rp.zero? %li #{pluralize rp, 'personal project'} will be removed and cannot be restored - = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" + .form-actions + = link_to 'Delete account', user_registration_path, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" - else - if @user.solo_owned_groups.present? %p diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 377f0fa0129..40d0b68c6cc 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -13,10 +13,9 @@ = render 'shared/new_commit_form', placeholder: "Add new directory" - .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" + .form-actions + = submit_tag "Create directory", class: 'btn btn-create' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml index 3bb61f0c944..ecc90a30e78 100644 --- a/app/views/projects/blob/_upload.html.haml +++ b/app/views/projects/blob/_upload.html.haml @@ -16,10 +16,9 @@ = render 'shared/new_commit_form', placeholder: placeholder - .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' - = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" + .form-actions + = button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all' + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file'); diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 5e7c211a424..7bf89d4e550 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -213,7 +213,8 @@ #{link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)}. %br %strong Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source. - = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } + .form-actions + = button_to 'Remove fork relationship', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_fork_project_message(@project) } - else .nothing-here-block Only the project owner can remove the fork relationship. @@ -226,8 +227,8 @@ Removing the project will delete its repository and all related resources including issues, merge requests etc. %br %strong Removed projects cannot be restored! - - = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } + .form-actions + = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) } - else .nothing-here-block Only the project owner can remove a project. diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml index 9bc2d33c27e..34241cd8aad 100644 --- a/app/views/shared/_confirm_modal.html.haml +++ b/app/views/shared/_confirm_modal.html.haml @@ -19,5 +19,5 @@ .form-group = text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input' - .form-group + .form-actions = submit_tag 'Confirm', class: "btn btn-danger js-confirm-danger-submit" -- cgit v1.2.1 From da48fdc2a51af0f02ebb22cc13fabf7d1a636690 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:35:19 +0100 Subject: Don't write "Optional" or "Required" unless non-obvious --- app/views/groups/milestones/new.html.haml | 1 - app/views/projects/milestones/_form.html.haml | 1 - app/views/projects/tags/new.html.haml | 4 ++-- app/views/shared/issuable/_form.html.haml | 3 +-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index 800bac4ef02..ccba58a1ac3 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -15,7 +15,6 @@ = f.label :title, "Title", class: "control-label" .col-sm-10 = 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 diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml index cc29970f07f..c8e0baa1d1b 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -10,7 +10,6 @@ = f.label :title, "Title", class: "control-label" .col-sm-10 = 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 diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 91e0a5493bd..450d3309e26 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -24,7 +24,7 @@ = label_tag :message, nil, class: 'control-label' .col-sm-10 = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' - .help-block (Optional) Entering a message will create an annotated tag. + .help-block Optionally, enter a message to create an annotated tag. %hr .form-group = label_tag :release_description, 'Release notes', class: 'control-label' @@ -32,7 +32,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' - .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page + .help-block Optionally, you can add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 7f32fac6b2c..ae9d100d1d5 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -6,8 +6,7 @@ %span= msg %br .form-group - = f.label :title, class: 'control-label' do - %strong= 'Title *' + = f.label :title, class: 'control-label' .col-sm-10 = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', class: 'form-control pad js-gfm-input js-quick-submit', required: true -- cgit v1.2.1 From a70c507882289a42a9d9b359a730e6f166fedd74 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:36:44 +0100 Subject: Only use input placeholders when they add value --- app/views/projects/blob/_new_dir.html.haml | 2 +- app/views/projects/branches/new.html.haml | 5 +++-- app/views/projects/edit.html.haml | 6 +++--- app/views/projects/new.html.haml | 2 +- app/views/projects/tags/new.html.haml | 6 +++--- app/views/shared/snippets/_form.html.haml | 3 ++- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 40d0b68c6cc..7f95b46efc7 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -9,7 +9,7 @@ .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' + = text_field_tag :dir_name, params[:dir_name], required: true, class: 'form-control' = render 'shared/new_commit_form', placeholder: "Add new directory" diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index efb5298a5e5..11567fc4393 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -13,11 +13,12 @@ .form-group = label_tag :branch_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :branch_name, params[:branch_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control' + .help-block Existing branch name, tag, or commit SHA .form-actions = button_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 7bf89d4e550..35581ee1aa5 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -14,7 +14,7 @@ = f.label :name, class: 'control-label' do Project name .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control", id: "project_name_edit" + = f.text_field :name, class: "form-control", id: "project_name_edit" .form-group @@ -22,7 +22,7 @@ Project description %span.light (optional) .col-sm-10 - = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250 + = f.text_area :description, class: "form-control", rows: 3, maxlength: 250 - if @project.repository.exists? && @project.repository.branch_names.any? .form-group @@ -164,7 +164,7 @@ Project name .col-sm-9 .form-group - = f.text_field :name, placeholder: "Example Project", class: "form-control" + = f.text_field :name, class: "form-control" .form-group = f.label :path, class: 'control-label' do %span Path diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 2670b9d9cda..da0267604e7 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -95,7 +95,7 @@ Description %span.light (optional) .col-sm-10 - = f.text_area :description, placeholder: "Awesome project", class: "form-control", rows: 3, maxlength: 250, tabindex: 3 + = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3 = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project .form-actions diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 450d3309e26..e65ce308d65 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -14,16 +14,16 @@ .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 - = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :tag_name, params[:tag_name], required: true, tabindex: 1, autofocus: true, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref] || @project.default_branch, required: true, tabindex: 2, class: 'form-control' .help-block Branch name or commit SHA .form-group = label_tag :message, nil, class: 'control-label' .col-sm-10 - = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' + = text_field_tag :message, nil, required: false, tabindex: 3, class: 'form-control' .help-block Optionally, enter a message to create an annotated tag. %hr .form-group diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index b46ed0dc0ad..99d93659678 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -8,7 +8,8 @@ .form-group = f.label :title, class: 'control-label' - .col-sm-10= f.text_field :title, placeholder: "Example Snippet", class: 'form-control', required: true + .col-sm-10 + = f.text_field :title, class: 'form-control', required: true, autofocus: true = render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: true, form_model: @snippet -- cgit v1.2.1 From 9bad736fb3ccd0a48cd64fc37538d8b021bce205 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:38:14 +0100 Subject: Remove unnecesary wrapper elements --- app/assets/stylesheets/pages/merge_requests.scss | 4 ---- app/views/projects/_commit_button.html.haml | 4 +--- app/views/projects/issues/_form.html.haml | 5 ++--- app/views/projects/merge_requests/_form.html.haml | 3 +-- app/views/projects/merge_requests/_new_submit.html.haml | 11 +++++------ app/views/projects/notes/_edit_form.html.haml | 5 ++--- app/views/projects/notes/_form.html.haml | 9 ++++----- 7 files changed, 15 insertions(+), 26 deletions(-) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 08e4bcdf529..8ff0a0cd54b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -192,10 +192,6 @@ line-height: 1.1; } -.merge-request-form-info { - padding-top: 15px; -} - // hide mr close link for inline diff comment form .diff-file .close-mr-link, .diff-file .reopen-mr-link { diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml index 35f7e7bb34b..2fd3d9e1be4 100644 --- a/app/views/projects/_commit_button.html.haml +++ b/app/views/projects/_commit_button.html.haml @@ -1,6 +1,4 @@ .form-actions - .commit-button-annotation - = button_tag 'Commit Changes', - class: 'btn commit-btn js-commit-button btn-create' + = button_tag 'Commit Changes', class: 'btn commit-btn js-commit-button btn-create' = link_to 'Cancel', cancel_path, class: 'btn btn-cancel', data: {confirm: leave_edit_message} diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index e0e26a26dae..6588d9bdbe1 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -1,6 +1,5 @@ -%div.issue-form-holder - = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f| - = render 'shared/issuable/form', f: f, issuable: @issue += form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form js-requires-input' } do |f| + = render 'shared/issuable/form', f: f, issuable: @issue :javascript $('.assign-to-me-link').on('click', function(e){ diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 9cf389dbe38..3e4ab09c6d4 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -1,6 +1,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| - .merge-request-form-info - = render 'shared/issuable/form', f: f, issuable: @merge_request + = render 'shared/issuable/form', f: f, issuable: @merge_request :javascript $('.assign-to-me-link').on('click', function(e){ diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 9fa9cc56126..8b0579f341a 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -11,12 +11,11 @@ = link_to 'Change branches', mr_change_branches_path(@merge_request) %hr = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form js-requires-input' } do |f| - .merge-request-form-info - = render 'shared/issuable/form', f: f, issuable: @merge_request - = f.hidden_field :source_project_id - = f.hidden_field :source_branch - = f.hidden_field :target_project_id - = f.hidden_field :target_branch + = render 'shared/issuable/form', f: f, issuable: @merge_request + = f.hidden_field :source_project_id + = f.hidden_field :source_branch + = f.hidden_field :target_project_id + = f.hidden_field :target_branch .mr-compare.merge-request %ul.merge-request-tabs diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index a21c019986a..3ccda1b381c 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -6,6 +6,5 @@ = render 'projects/notes/hints' .note-form-actions - .buttons - = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button' - = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel' + = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button' + = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel' diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index 5dd84317e3b..88e711ab534 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -12,8 +12,7 @@ = render 'projects/notes/hints' .error-alert - .note-form-actions - .buttons.clearfix - = 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 + .note-form-actions.clearfix + = f.submit 'Add Comment', class: "btn btn-create comment-btn btn-grouped js-comment-button" + = yield(:note_actions) + %a.btn.btn-cancel.js-close-discussion-note-form Cancel -- cgit v1.2.1 From 2aeb26bd8823fa681413bd0f64b7b068f41ec4e3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:38:33 +0100 Subject: Use select2 placeholder instead of blank option --- app/views/projects/protected_branches/index.html.haml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 52b3a50c1e6..2541105b007 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -22,7 +22,7 @@ .form-group = f.label :name, "Branch", class: 'control-label' .col-sm-10 - = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "select2"}) + = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: true}, {class: "select2", data: {placeholder: "Select branch"}}) .form-group .col-sm-offset-2.col-sm-10 .checkbox @@ -33,4 +33,3 @@ .form-actions = f.submit 'Protect', class: "btn-create btn" = render 'branches_list' - -- cgit v1.2.1 From 72ab3b17d338c15f27e3bb1fbd0e7cefaa10b94d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:39:39 +0100 Subject: Use js-requires-input where appropriate --- app/views/profiles/keys/_form.html.haml | 7 +++---- app/views/projects/blob/_new_dir.html.haml | 3 +-- app/views/projects/deploy_keys/_form.html.haml | 7 +++---- app/views/projects/tags/new.html.haml | 3 +-- app/views/shared/_new_commit_form.html.haml | 2 +- app/views/shared/snippets/_form.html.haml | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index b76a5b636ac..2a8800de60e 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -1,5 +1,5 @@ %div - = form_for [:profile, @key], html: { class: 'form-horizontal' } do |f| + = form_for [:profile, @key], html: { class: 'form-horizontal js-requires-input' } do |f| - if @key.errors.any? .alert.alert-danger %ul @@ -9,12 +9,11 @@ .form-group = f.label :key, class: 'control-label' .col-sm-10 - = f.text_area :key, class: "form-control", rows: 8 + = f.text_area :key, class: "form-control", rows: 8, autofocus: true, required: true .form-group = f.label :title, class: 'control-label' - .col-sm-10= f.text_field :title, class: "form-control" + .col-sm-10= f.text_field :title, class: "form-control", required: true .form-actions = f.submit 'Add key', class: "btn btn-create" = link_to "Cancel", profile_keys_path, class: "btn btn-cancel" - diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml index 7f95b46efc7..fc6c9f5fd09 100644 --- a/app/views/projects/blob/_new_dir.html.haml +++ b/app/views/projects/blob/_new_dir.html.haml @@ -5,7 +5,7 @@ %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, class: 'form-horizontal js-create-dir-form' do + = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form js-requires-input' do .form-group = label_tag :dir_name, 'Directory name', class: 'control-label' .col-sm-10 @@ -18,5 +18,4 @@ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" :javascript - disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create"); new NewCommitForm($('.js-create-dir-form')) diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 085c9149b11..5e182af2669 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -1,5 +1,5 @@ %div - = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f| -if @key.errors.any? .alert.alert-danger %ul @@ -8,16 +8,15 @@ .form-group = f.label :title, class: "control-label" - .col-sm-10= f.text_field :title, class: 'form-control' + .col-sm-10= f.text_field :title, class: 'form-control', autofocus: true, required: true .form-group = f.label :key, class: "control-label" .col-sm-10 %p.light Paste a machine public key here. Read more about how to generate it = link_to "here", help_page_path("ssh", "README") - = f.text_area :key, class: "form-control thin_area", rows: 5 + = f.text_area :key, class: "form-control thin_area", rows: 5, required: true .form-actions = f.submit 'Create Deploy Key', class: "btn-create btn" = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel" - diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index e65ce308d65..58d2e5c7136 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -10,7 +10,7 @@ New Tag %hr -= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do += form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form js-requires-input" do .form-group = label_tag :tag_name, nil, class: 'control-label' .col-sm-10 @@ -38,7 +38,6 @@ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create"); var availableTags = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index ce52614e868..31b02ed93d0 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -4,7 +4,7 @@ .form-group.branch = label_tag 'new_branch', 'Target branch', class: 'control-label' .col-sm-10 - = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" + = text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch" .form-group.js-create-merge-request-form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml index 99d93659678..1041eccd1df 100644 --- a/app/views/shared/snippets/_form.html.haml +++ b/app/views/shared/snippets/_form.html.haml @@ -1,5 +1,5 @@ .snippet-form-holder - = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form" } do |f| + = form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input" } do |f| - if @snippet.errors.any? .alert.alert-danger %ul -- cgit v1.2.1 From dccc22775472a7053b85f496011808c35fd6d62c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:01 +0100 Subject: Use autofocus where appropriate --- app/views/groups/milestones/new.html.haml | 2 +- app/views/projects/labels/_form.html.haml | 2 +- app/views/projects/milestones/_form.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml index ccba58a1ac3..3894a0ece74 100644 --- a/app/views/groups/milestones/new.html.haml +++ b/app/views/groups/milestones/new.html.haml @@ -14,7 +14,7 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true + = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml index 2d4311412fd..5ce2a7b985d 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 js-quick-submit", required: true + = f.text_field :title, class: "form-control js-quick-submit", required: true, autofocus: 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 c8e0baa1d1b..39aa2437e18 100644 --- a/app/views/projects/milestones/_form.html.haml +++ b/app/views/projects/milestones/_form.html.haml @@ -9,7 +9,7 @@ .form-group = f.label :title, "Title", class: "control-label" .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true + = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true, autofocus: true .form-group.milestone-description = f.label :description, "Description", class: "control-label" .col-sm-10 -- cgit v1.2.1 From cdd4c331b36e75b2b01a1648f1e5563c4afc5a49 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:18 +0100 Subject: Use select2 instead of regular selectbox for profile public email --- app/views/profiles/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index faab12ca0b9..9459d8a6295 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -43,7 +43,7 @@ .form-group = f.label :public_email, class: "control-label" .col-sm-10 - = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show in profile'}, class: "form-control" + = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email), {include_blank: 'Do not show on profile'}, class: "select2" %span.help-block This email will be displayed on your public profile. .form-group = f.label :skype, class: "control-label" -- cgit v1.2.1 From 8d98245783832a4d6617fb7076343469918ed273 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:36 +0100 Subject: Remove `.git` suffix to project path field. --- app/views/projects/edit.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 35581ee1aa5..b28ada909b7 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -174,7 +174,6 @@ .input-group-addon #{URI.join(root_url, @project.namespace.path)}/ = f.text_field :path, class: 'form-control' - %span.input-group-addon .git %ul %li Be careful. Renaming a project's repository can have unintended side effects. %li You will need to update your local repositories to point to the new location. -- cgit v1.2.1 From d0166334ba9997b5eed89d6f54c99f7bfbe9c816 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:40:52 +0100 Subject: Add `http://gitlab.example.com/u/` prefix to "Change username" field. --- app/views/profiles/accounts/show.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 2fd65cc9944..319bdd57c39 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -85,14 +85,15 @@ %p Changing your username will change path to all personal projects! %div - = f.text_field :username, required: true, class: 'form-control' + .input-group + .input-group-addon + = "#{root_url}u/" + = f.text_field :username, required: true, class: 'form-control'   .loading-gif.hide %p = icon('spinner spin') Saving new username - %p.light - = user_url(@user) .form-actions = f.submit 'Save username', class: "btn btn-warning" -- cgit v1.2.1 From c0e614d1c46cd273f83385ef61c76200d492d0a4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:41:02 +0100 Subject: Rename variable --- app/views/projects/branches/new.html.haml | 4 ++-- app/views/projects/tags/new.html.haml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 11567fc4393..31943a2407a 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -24,9 +24,9 @@ = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - var availableTags = #{@project.repository.ref_names.to_json}; + var availableRefs = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ - source: availableTags, + source: availableRefs, minLength: 1 }); diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 58d2e5c7136..9c9bfa3f55f 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -38,9 +38,9 @@ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' :javascript - var availableTags = #{@project.repository.ref_names.to_json}; + var availableRefs = #{@project.repository.ref_names.to_json}; $("#ref").autocomplete({ - source: availableTags, + source: availableRefs, minLength: 1 }); -- cgit v1.2.1 From 092e093b2c77c88707060e0cd95eec338641d6b1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 2 Dec 2015 14:42:43 +0100 Subject: Small css cleanup and ui dev kit improvements Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/files.scss | 1 - app/assets/stylesheets/framework/lists.scss | 7 ------- app/assets/stylesheets/pages/ui_dev_kit.scss | 5 +---- app/views/help/ui.html.haml | 30 ++++++++++++++-------------- 4 files changed, 16 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 35db00281e5..53be2f2ce0b 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -22,7 +22,6 @@ position: relative; background: $background-color; border-bottom: 1px solid $border-color; - text-shadow: 0 1px 1px #fff; margin: 0; text-align: left; padding: 10px 15px; diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index a798ae812e3..927641216e4 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -72,13 +72,6 @@ } } -ol, ul { - &.styled { - li { - padding: 2px; - } - } -} /** light list with border-bottom between li **/ ul.bordered-list { diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss index 277afa1db9e..185f3622e64 100644 --- a/app/assets/stylesheets/pages/ui_dev_kit.scss +++ b/app/assets/stylesheets/pages/ui_dev_kit.scss @@ -1,9 +1,6 @@ .gitlab-ui-dev-kit { > h2 { - font-size: 27px; - border-bottom: 1px solid #CCC; - color: #666; - margin: 30px 0; + margin: 35px 0 20px; font-weight: bold; } } diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 8a60d6852be..d9ffda884c8 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -31,7 +31,7 @@ %h2#blocks Blocks - %h3 + %h4 %code .gray-content-block .gray-content-block.middle-block @@ -43,9 +43,9 @@ = lorem - %h3 + %h4 %code .cover-block - + %br .cover-block .avatar-holder = image_tag avatar_icon('admin@example.com', 90), class: "avatar s90", alt: '' @@ -64,7 +64,7 @@ %h2#lists Lists - %h3 + %h4 %code .content-list %ul.content-list %li @@ -74,7 +74,7 @@ %li One item - %h3 + %h4 %code .well-list %ul.well-list %li @@ -84,7 +84,7 @@ %li One item - %h3 + %h4 %code .panel .well-list .panel.panel-default @@ -97,7 +97,7 @@ %li One item - %h3 + %h4 %code .bordered-list %ul.bordered-list %li @@ -138,7 +138,7 @@ %h2#navs Navigation - %h3 + %h4 %code .center-top-menu .example %ul.center-top-menu @@ -147,7 +147,7 @@ %li %a Closed - %h3 + %h4 %code .btn-group.btn-group-next .example %div.btn-group.btn-group-next @@ -155,7 +155,7 @@ %a.btn Closed - %h3 + %h4 %code .nav.nav-tabs .example %ul.nav.nav-tabs @@ -221,7 +221,7 @@ %h2#forms Forms - %h3 + %h4 %code form.horizontal-form %form.form-horizontal @@ -243,7 +243,7 @@ .col-sm-offset-2.col-sm-10 %button.btn.btn-default{:type => "submit"} Sign in - %h3 + %h4 %code form %form @@ -260,7 +260,7 @@ %button.btn.btn-default{:type => "submit"} Sign in %h2#file File - %h3 + %h4 %code .file-holder - blob = Snippet.new(content: "Wow\nSuch\nFile") @@ -271,12 +271,12 @@ .file-actions .btn-group %a.btn Edit - %a.btn Remove + %a.btn.btn-danger Remove .file-contenta.code = render 'shared/file_highlight', blob: blob %h2#markdown Markdown - %h3 + %h4 %code .md or .wiki and others Markdown rendering has a bit different css and presented in next UI elements: -- cgit v1.2.1 From 58dad2a9dadea647b5665e1de6a6e997484b96b1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:43:23 +0100 Subject: Remove bottom margin from page-titles --- app/assets/stylesheets/framework/typography.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 2c4a58c8db1..aef338cfa56 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -181,6 +181,10 @@ body { line-height: 1.3; font-size: 1.25em; font-weight: 600; + + &:last-child { + margin-bottom: 0; + } } .page-title-empty { -- cgit v1.2.1 From 3ae7dba931b880b8090edffb247ebfe32d26648e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:46:15 +0100 Subject: Use gl-padding instead of 15px/20px where appropriate --- app/assets/stylesheets/framework.scss | 3 ++- app/assets/stylesheets/framework/common.scss | 10 +++++++++- app/assets/stylesheets/framework/files.scss | 4 +--- app/assets/stylesheets/framework/panels.scss | 15 +++++++++++++++ app/assets/stylesheets/framework/tables.scss | 2 ++ app/assets/stylesheets/pages/merge_requests.scss | 2 +- app/assets/stylesheets/pages/note_form.scss | 7 +++---- app/assets/stylesheets/pages/projects.scss | 2 +- app/views/profiles/notifications/show.html.haml | 4 +--- app/views/projects/compare/show.html.haml | 4 ++-- app/views/projects/merge_requests/_new_compare.html.haml | 3 +-- app/views/projects/merge_requests/_show.html.haml | 2 +- app/views/shared/issuable/_context.html.haml | 6 +++--- 13 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 app/assets/stylesheets/framework/panels.scss diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 1ec9d2fd84f..48a4971c8fc 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -1,9 +1,9 @@ @import "framework/fonts"; @import "framework/variables"; @import "framework/mixins"; -@import "framework/layout"; @import 'framework/tw_bootstrap_variables'; @import 'framework/tw_bootstrap'; +@import "framework/layout"; @import "framework/avatar.scss"; @import "framework/blocks.scss"; @@ -25,6 +25,7 @@ @import "framework/markdown_area.scss"; @import "framework/mobile.scss"; @import "framework/pagination.scss"; +@import "framework/panels.scss"; @import "framework/selects.scss"; @import "framework/sidebar.scss"; @import "framework/tables.scss"; diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61689aff57e..61ecd58e6c5 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -7,7 +7,7 @@ /** COMMON CLASSES **/ .prepend-top-10 { margin-top:10px } -.prepend-top-default { margin-top: $gl-padding; } +.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-20 { margin-top:20px } .prepend-left-10 { margin-left:10px } .prepend-left-20 { margin-left:20px } @@ -52,6 +52,10 @@ pre { } } +hr { + margin: $gl-padding 0; +} + .dropdown-menu > li > a { text-shadow: none; } @@ -433,3 +437,7 @@ table { .space-right { margin-right: 10px; } + +.alert, .progress { + margin-bottom: $gl-padding; +} diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 35db00281e5..6bf2857e83a 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -8,7 +8,6 @@ border: none; border-top: 1px solid #E7E9EE; border-bottom: 1px solid #E7E9EE; - margin-bottom: 1em; &.readme-holder { border-bottom: 0; @@ -25,7 +24,7 @@ text-shadow: 0 1px 1px #fff; margin: 0; text-align: left; - padding: 10px 15px; + padding: 10px $gl-padding; .file-actions { float: right; @@ -171,4 +170,3 @@ } } } - diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss new file mode 100644 index 00000000000..406aff3d72c --- /dev/null +++ b/app/assets/stylesheets/framework/panels.scss @@ -0,0 +1,15 @@ +.panel { + margin-bottom: $gl-padding; + + .panel-heading { + padding: 10px $gl-padding; + } + .panel-body { + padding: $gl-padding; + + .form-actions { + margin: -$gl-padding; + margin-top: $gl-padding; + } + } +} diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss index 66e16e8df75..793ab3d9bb9 100644 --- a/app/assets/stylesheets/framework/tables.scss +++ b/app/assets/stylesheets/framework/tables.scss @@ -6,6 +6,8 @@ table { &.table { + margin-bottom: $gl-padding; + .dropdown-menu a { text-decoration: none; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 08e4bcdf529..177cf6ca45b 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -87,7 +87,7 @@ .mr-widget-body, .ci_widget, .mr-widget-footer { - padding: 15px; + padding: $gl-padding; } .normal { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 268fc995aa7..02db44c8eb0 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -7,6 +7,7 @@ } .reply-btn { @extend .btn-primary; + margin: 10px $gl-padding; } .diff-file .diff-content { tr.line_holder:hover { @@ -38,9 +39,8 @@ } .new_note, .edit_note { - .buttons { - margin-top: 8px; - margin-bottom: 3px; + .note-form-actions { + margin-top: $gl-padding; } .note-preview-holder { @@ -150,7 +150,6 @@ .discussion-reply-holder { background: $background-color; - padding: 10px 15px; border-top: 1px solid $border-color; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 4a0fe546844..b3054e36247 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -376,7 +376,7 @@ table.table.protected-branches-list tr.no-border { .project-stats { text-align: center; - margin-top: 15px; + margin-top: $gl-padding; margin-bottom: 0; padding-top: 10px; padding-bottom: 4px; diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 8eebd96b674..d4d9f246273 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -53,9 +53,7 @@ .form-actions = f.submit 'Save changes', class: "btn btn-create" -.clearfix - %hr -.row.all-notifications +.row.all-notifications.prepend-top-default .col-md-6 %p You can also specify notification level per group or per project. diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index 39755efd2fd..51088a7dea8 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -7,11 +7,11 @@ = render "form" - if @commits.present? - .prepend-top-20 + .prepend-top-default = render "projects/commits/commit_list" = render "projects/diffs/diffs", diffs: @diffs, project: @project - else - .light-well.prepend-top-20 + .light-well.prepend-top-default .center %h4 There isn't anything to compare. diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml index d9eff1f9320..6def9d6266f 100644 --- a/app/views/projects/merge_requests/_new_compare.html.haml +++ b/app/views/projects/merge_requests/_new_compare.html.haml @@ -37,7 +37,7 @@ %h4 Compare failed %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches. - else - .light-well.append-bottom-10 + .light-well.append-bottom-default .center %h4 There isn't anything to merge. @@ -86,4 +86,3 @@ return; } }); - diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index eeaa72ed21b..94bd154aebb 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -8,7 +8,7 @@ .merge-request-details.issuable-details = render "projects/merge_requests/show/mr_title" = render "projects/merge_requests/show/mr_box" - .append-bottom-20.mr-source-target.prepend-top-default + .append-bottom-default.mr-source-target.prepend-top-default - if @merge_request.open? .pull-right - if @merge_request.source_branch_exists? diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index be66256c7b0..f1646b4ce64 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -1,5 +1,5 @@ = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| - %div.prepend-top-20 + %div.prepend-top-default .issuable-context-title %label Assignee: @@ -11,7 +11,7 @@ - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true) - %div.prepend-top-20.clearfix + %div.prepend-top-default.clearfix .issuable-context-title %label Milestone: @@ -31,7 +31,7 @@ - if current_user - subscribed = issuable.subscribed?(current_user) - %div.prepend-top-20.clearfix + %div.prepend-top-default.clearfix .issuable-context-title %label Subscription: -- cgit v1.2.1 From c95b64d1cad8aa8f0861feba0c919ae3d1722df0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:46:55 +0100 Subject: Fix alignment of buttons in issue header --- app/assets/stylesheets/framework/issue_box.scss | 5 +++-- app/assets/stylesheets/pages/issuable.scss | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss index 93377e45e70..f12d68b5a1f 100644 --- a/app/assets/stylesheets/framework/issue_box.scss +++ b/app/assets/stylesheets/framework/issue_box.scss @@ -7,8 +7,9 @@ .issue-box { @include border-radius(2px); - display: inline-block; - padding: 10px $gl-padding; + display: block; + float: left; + padding: 0 $gl-padding; font-weight: normal; margin-right: 10px; font-size: $gl-font-size; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 3a08ee70bc7..51d8e5b4657 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -51,11 +51,12 @@ .issuable-details { .page-title { - margin-top: -15px; - padding: 10px 0; + margin-top: -$gl-padding; + padding: 7px 0; margin-bottom: 0; color: #5c5d5e; font-size: 16px; + line-height: 42px; .author { color: #5c5d5e; -- cgit v1.2.1 From aa01b23937d05d4110cf24683bbe96dcc77c730c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:47:19 +0100 Subject: Fix padding around editor path, input and select box. --- app/assets/stylesheets/pages/editor.scss | 46 ++++++++++++------------------- app/views/projects/blob/_editor.html.haml | 22 +++++++-------- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss index e2c521af91e..39d916cd336 100644 --- a/app/assets/stylesheets/pages/editor.scss +++ b/app/assets/stylesheets/pages/editor.scss @@ -19,48 +19,38 @@ color: #B94A48; } } - .commit-button-annotation { - display: inline-block; - margin: 0; - padding: 2px; - - > * { - float: left; - } - - .message { - display: inline-block; - margin: 5px 8px 0 8px; - } - } .file-title { @extend .monospace; + + line-height: 42px; + padding-top: 7px; + padding-bottom: 7px; } .editor-ref { background: $background-color; - padding: 11px 15px; + padding-right: $gl-padding; border-right: 1px solid $border-color; - display: inline-block; - margin: -5px -5px; + display: block; + float: left; margin-right: 10px; } .editor-file-name { - .new-file-name { - display: inline-block; - width: 450px; - } + @extend .monospace; + + float: left; + margin-right: 10px; + } - .form-control { - margin-top: -3px; - } + .new-file-name { + display: inline-block; + width: 450px; + float: left; } - .form-actions { - margin: -$gl-padding; - margin-top: 0; - padding: $gl-padding + .select2 { + float: right; } } diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index f1ad0c3c403..333f5d470ed 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -1,19 +1,19 @@ -.file-holder.file - .file-title +.file-holder.file.append-bottom-default + .file-title.clearfix .editor-ref - %i.fa.fa-code-fork + = icon('code-fork') = ref %span.editor-file-name - - if @path - %span.monospace - = @path + = @path - - if current_action?(:new) || current_action?(:create) + - if current_action?(:new) || current_action?(:create) + %span.editor-file-name \/ - = text_field_tag 'file_name', params[:file_name], placeholder: "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' + = text_field_tag 'file_name', params[:file_name], placeholder: "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: 'select2' .file-content.code %pre.js-edit-mode-pane#editor -- cgit v1.2.1 From 0159d78a3b7dbc58f74c90c41adf8ef3049ed0aa Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:48:06 +0100 Subject: Remove padding around form-actions --- app/assets/stylesheets/framework/forms.scss | 7 ++++--- app/views/profiles/notifications/show.html.haml | 2 +- app/views/projects/releases/edit.html.haml | 2 +- app/views/shared/issuable/_form.html.haml | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 0edfe24f195..ddb522bb638 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -22,9 +22,10 @@ input[type='text'].danger { } .form-actions { - padding: 17px 20px 18px; - margin-top: 18px; - margin-bottom: 18px; + margin: -$gl-padding; + margin-top: 0; + margin-bottom: -$gl-padding; + padding: $gl-padding; background-color: $background-color; border-top: 1px solid $border-color; } diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index d4d9f246273..0bcadc965fa 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -50,7 +50,7 @@ Watch %p You will receive notifications for any activity - .form-actions + .gray-content-block = f.submit 'Save changes', class: "btn btn-create" .row.all-notifications.prepend-top-default diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml index f516b65ecd0..bc80f2f29ad 100644 --- a/app/views/projects/releases/edit.html.haml +++ b/app/views/projects/releases/edit.html.haml @@ -14,6 +14,6 @@ = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' .error-alert - .prepend-top-default + .form-actions.prepend-top-default = f.submit 'Save changes', class: 'btn btn-save' = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel" diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 0fc74d7d2b1..7558b37f83f 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -93,7 +93,8 @@ %p.help-block = link_to 'Change branches', mr_change_branches_path(@merge_request) -.form-actions +- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) +.gray-content-block{class: (is_footer ? "footer-block" : "middle-block")} - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted? %p Please review the -- cgit v1.2.1 From 3ad1d320ef3f3aa5a0896a9b30a2fba791696f58 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:48:51 +0100 Subject: Remove duplicated styling for center top menus --- app/assets/stylesheets/pages/commit.scss | 13 ------------ app/assets/stylesheets/pages/merge_requests.scss | 23 ++-------------------- app/views/projects/commit/_ci_menu.html.haml | 2 +- app/views/projects/diffs/_diffs.html.haml | 2 +- .../projects/merge_requests/_discussion.html.haml | 2 +- .../projects/merge_requests/_new_submit.html.haml | 5 ++--- app/views/projects/merge_requests/_show.html.haml | 2 +- .../merge_requests/show/_commits.html.haml | 2 +- 8 files changed, 9 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index a0e5f7554ed..74bbe0880f7 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -108,16 +108,3 @@ z-index: 2; } } - -.commit-ci-menu { - padding: 0; - margin: 0; - list-style: none; - margin-top: 5px; - height: 56px; - margin: -16px; - padding: 16px; - text-align: center; - margin-top: 0px; - margin-bottom: 2px; -} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 177cf6ca45b..017a86bcd9a 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -4,7 +4,6 @@ */ .mr-state-widget { background: #F7F8FA; - margin-bottom: 20px; color: $gl-gray; border: 1px solid #dce0e6; @include border-radius(2px); @@ -116,26 +115,8 @@ } } -.merge-request .merge-request-tabs { - @include nav-menu; - margin: -$gl-padding; - padding: $gl-padding; - text-align: center; - margin-bottom: 1px; -} - -// Mobile -@media (max-width: 480px) { - .merge-request .merge-request-tabs { - margin: 0; - padding: 0; - - li { - a { - padding: 0; - } - } - } +.merge-request-details { + margin-bottom: $gl-padding; } .mr_source_commit, diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index c73ba74f5ef..76dc87a8824 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -1,4 +1,4 @@ -%ul.center-top-menu.commit-ci-menu +%ul.center-top-menu.no-top.no-bottom.commit-ci-menu = nav_link(path: 'commit#show') do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do Changes diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 416fb4da071..f9d661d59d2 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -3,7 +3,7 @@ - diff_files = safe_diff_files(diffs) -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block .inline-parallel-buttons .btn-group = inline_diff_btn diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 2b3c3eff5e4..4a192aeb2cd 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -7,7 +7,7 @@ = render 'shared/show_aside' -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block .row .col-md-9 .votes-holder.pull-right diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 6244d3ba0b4..72132344c88 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -19,7 +19,7 @@ = f.hidden_field :target_branch .mr-compare.merge-request - %ul.merge-request-tabs + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.commits-tab = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do Commits @@ -31,7 +31,7 @@ .tab-content #commits.commits.tab-pane - = render "projects/commits/commits", project: @project + = render "projects/merge_requests/show/commits" #diffs.diffs.tab-pane.active - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project @@ -57,4 +57,3 @@ diffs_loaded: true, commits_loaded: true }); - diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 94bd154aebb..e7eb0066594 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -40,7 +40,7 @@ = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - if @commits.present? - %ul.merge-request-tabs + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.notes-tab = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do Discussion diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 478054db517..7f904ec42a0 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.second-block.oneline-block +.gray-content-block.middle-block.oneline-block = icon("sort-amount-desc") Most recent commits displayed first -- cgit v1.2.1 From 1c595681e5b60cfdf402abc6fd43c282897b6260 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:29 +0100 Subject: Remove double border between gray blocks --- app/assets/stylesheets/framework/blocks.scss | 4 ++++ app/views/projects/commit/show.html.haml | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 1635df9c97b..dec50a0e0f6 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -114,3 +114,7 @@ right: 10px; } } + +.block-connector { + margin-top: -1px; +} diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 85e203cbe57..069b8b1f169 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,6 +1,9 @@ - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" = render "projects/commits/header_title" = render "commit_box" -= render "ci_menu" if @ci_commit +- if @ci_commit + = render "ci_menu" +- else + %div.block-connector = render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/notes/notes_with_form" -- cgit v1.2.1 From 9ea72430921efaa4a1a79ea21c54763bda8bba78 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:46 +0100 Subject: Fix padding of no readme and empty repo messages --- app/views/projects/_readme.html.haml | 25 +++++++++++++------------ app/views/projects/empty.html.haml | 13 ++++++------- app/views/projects/show.html.haml | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml index b5ef0aca540..d1191928d4f 100644 --- a/app/views/projects/_readme.html.haml +++ b/app/views/projects/_readme.html.haml @@ -7,15 +7,16 @@ = cache(readme_cache_key) do = render_readme(readme) - else - %h3.page-title - This project does not have README yet - - if can?(current_user, :push_code, @project) - %p.slead - A - %code README - file contains information about other files in a repository and is commonly - distributed with computer software, forming part of its documentation. - %br - We recommend you to - = link_to "add README", new_readme_path, class: 'underlined-link' - file to the repository and GitLab will render it here instead of this message. + .gray-content-block.second-block.center + %h3.page-title + This project does not have README yet + - if can?(current_user, :push_code, @project) + %p + A + %code README + file contains information about other files in a repository and is commonly + distributed with computer software, forming part of its documentation. + %p + We recommend you to + = link_to "add README", new_readme_path, class: 'underlined-link' + file to the repository and GitLab will render it here instead of this message. diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index c3858e78cad..950ab33825e 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -5,17 +5,16 @@ = render "home_panel" -.gray-content-block.center +.gray-content-block.second-block.center %h3.page-title The repository for this project is empty - - if can?(current_user, :download_code, @project) + - if can?(current_user, :push_code, @project) %p If you already have files you can push them using command line instructions below. - %br - - if can?(current_user, :push_code, @project) - Otherwise you can start with - = link_to "adding README", new_readme_path, class: 'underlined-link' - file to this project. + %p + Otherwise you can start with + = link_to "adding README", new_readme_path, class: 'underlined-link' + file to this project. - if can?(current_user, :download_code, @project) .prepend-top-20 diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 585caf674c9..9c7a5584da9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -11,7 +11,7 @@ = render "home_panel" -.project-stats.gray-content-block +.project-stats.gray-content-block.second-block %ul.nav.nav-pills %li = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do -- cgit v1.2.1 From ba4bf27fe486af1790844ac40bc188c6462720b8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:49:59 +0100 Subject: Use table-holder where appropriate --- app/views/profiles/applications.html.haml | 84 ++++++++++++++-------------- app/views/profiles/keys/_key_table.html.haml | 16 +++--- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml index 2342936a5d5..0436c2213da 100644 --- a/app/views/profiles/applications.html.haml +++ b/app/views/profiles/applications.html.haml @@ -15,24 +15,25 @@ .pull-right = link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' - if @applications.any? - %table.table.table-striped - %thead - %tr - %th Name - %th Callback URL - %th Clients - %th - %th - %tbody - - @applications.each do |application| - %tr{:id => "application_#{application.id}"} - %td= link_to application.name, oauth_application_path(application) - %td - - application.redirect_uri.split.each do |uri| - %div= uri - %td= application.access_tokens.count - %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm' - %td= render 'doorkeeper/applications/delete_form', application: application + .table-holder + %table.table.table-striped + %thead + %tr + %th Name + %th Callback URL + %th Clients + %th + %th + %tbody + - @applications.each do |application| + %tr{:id => "application_#{application.id}"} + %td= link_to application.name, oauth_application_path(application) + %td + - application.redirect_uri.split.each do |uri| + %div= uri + %td= application.access_tokens.count + %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm' + %td= render 'doorkeeper/applications/delete_form', application: application .oauth-authorized-applications.prepend-top-20 - if user_oauth_applications? @@ -40,29 +41,30 @@ Authorized applications - if @authorized_tokens.any? - %table.table.table-striped - %thead - %tr - %th Name - %th Authorized At - %th Scope - %th - %tbody - - @authorized_apps.each do |app| - - token = app.authorized_tokens.order('created_at desc').first - %tr{:id => "application_#{app.id}"} - %td= app.name - %td= token.created_at - %td= token.scopes - %td= render 'doorkeeper/authorized_applications/delete_form', application: app - - @authorized_anonymous_tokens.each do |token| + .table-holder + %table.table.table-striped + %thead %tr - %td - Anonymous - %div.help-block - %em Authorization was granted by entering your username and password in the application. - %td= token.created_at - %td= token.scopes - %td= render 'doorkeeper/authorized_applications/delete_form', token: token + %th Name + %th Authorized At + %th Scope + %th + %tbody + - @authorized_apps.each do |app| + - token = app.authorized_tokens.order('created_at desc').first + %tr{:id => "application_#{app.id}"} + %td= app.name + %td= token.created_at + %td= token.scopes + %td= render 'doorkeeper/authorized_applications/delete_form', application: app + - @authorized_anonymous_tokens.each do |token| + %tr + %td + Anonymous + %div.help-block + %em Authorization was granted by entering your username and password in the application. + %td= token.created_at + %td= token.scopes + %td= render 'doorkeeper/authorized_applications/delete_form', token: token - else %p.light You don't have any authorized applications diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml index ef0075aad3b..8c9d546af4c 100644 --- a/app/views/profiles/keys/_key_table.html.haml +++ b/app/views/profiles/keys/_key_table.html.haml @@ -1,6 +1,6 @@ - is_admin = defined?(admin) ? true : false -.panel.panel-default - - if @keys.any? +- if @keys.any? + .table-holder %table.table %thead.panel-heading %tr @@ -11,9 +11,9 @@ %tbody - @keys.each do |key| = render 'profiles/keys/key', key: key, is_admin: is_admin - - else - .nothing-here-block - - if is_admin - User has no ssh keys - - else - There are no SSH keys with access to your account. +- else + .nothing-here-block + - if is_admin + User has no ssh keys + - else + There are no SSH keys with access to your account. -- cgit v1.2.1 From d5fd9b26b13678a9e09a56890af10babe46362f0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:12 +0100 Subject: Remove style duplication between snippet and issue detail pages --- app/assets/stylesheets/pages/snippets.scss | 44 ++++------------------------- app/views/shared/snippets/_header.html.haml | 7 +++-- 2 files changed, 9 insertions(+), 42 deletions(-) diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index 242783a7b7e..bb74e50151d 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -27,56 +27,22 @@ } .snippet-holder { - .snippet-details { - .page-title { - margin-top: -15px; - padding: 10px 0; - margin-bottom: 0; - color: #5c5d5e; - font-size: 16px; - - .author { - color: #5c5d5e; - } - - .snippet-id { - color: #5c5d5e; - } - } - - .snippet-title { - margin: 0; - font-size: 23px; - color: #313236; - } - - @media (max-width: $screen-md-max) { - .new-snippet-link { - display: none; - } - } - - @media (max-width: $screen-sm-max) { - .creator, - .page-title .btn-close { - display: none; - } - } - } + margin-bottom: -$gl-padding; .file-holder { border-top: 0; } } - .snippet-box { @include border-radius(2px); - display: inline-block; - padding: 10px $gl-padding; + display: block; + float: left; + padding: 0 $gl-padding; font-weight: normal; margin-right: 10px; font-size: $gl-font-size; border: 1px solid; + line-height: 40px; } diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 0a4a790ec5e..35241029288 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,9 +1,9 @@ -.snippet-details +.issuable-details .page-title .snippet-box{class: visibility_level_color(@snippet.visibility_level)} = visibility_level_icon(@snippet.visibility_level) = visibility_level_label(@snippet.visibility_level) - %span.snippet-id Snippet ##{@snippet.id} + Snippet ##{@snippet.id} %span.creator · created by #{link_to_member(@project, @snippet.author, size: 24)} · @@ -19,6 +19,7 @@ = render "projects/snippets/actions" - else = render "snippets/actions" + .gray-content-block.middle-block - %h2.snippet-title + %h2.issue-title = gfm escape_once(@snippet.title) -- cgit v1.2.1 From 09396b2eaa529a7c9049e482142c3885c4510b90 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:41 +0100 Subject: Render all form controls at the same height --- app/assets/stylesheets/framework/forms.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index ddb522bb638..80283a56ec3 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -74,6 +74,8 @@ label { .form-control { @include box-shadow(none); + height: 42px; + padding: 8px $gl-padding; } .wiki-content { -- cgit v1.2.1 From 8196d269ebcc15925bf532a9edcccf75248bfffb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:50:50 +0100 Subject: No bottom margin for form help blocks --- app/assets/stylesheets/framework/forms.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index 80283a56ec3..cc92966c458 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -95,3 +95,7 @@ label { background-color: #f7f8fa; } } + +.help-block { + margin-bottom: 0; +} -- cgit v1.2.1 From ac389c6a3ec7f54174edb243b54b279a4916c30a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:10 +0100 Subject: Consistent styling for dashboard groups gray block --- app/views/dashboard/groups/index.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index f3f3f58111e..d5b7e729e7b 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -8,8 +8,8 @@ = link_to new_group_path, class: "btn btn-new" do %i.fa.fa-plus New Group - .title Welcome to the groups! - Group members have access to all group projects. + .oneline + Group members have access to all group projects. %ul.content-list - @group_members.each do |group_member| -- cgit v1.2.1 From 6ec9999a8e49e5374fe9e10dc60dc98a7ede1075 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:25 +0100 Subject: Use consistent border color --- app/assets/stylesheets/pages/note_form.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 02db44c8eb0..e1a72af0013 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -79,8 +79,8 @@ padding: $gl-padding; margin-left: -$gl-padding; margin-right: -$gl-padding; - border-right: 1px solid #ECEEF1; - border-top: 1px solid #ECEEF1; + border-right: 1px solid $border-color; + border-top: 1px solid $border-color; margin-bottom: -$gl-padding; } -- cgit v1.2.1 From aec4b290fd154ac382b49aefed4e34386785630f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:51:37 +0100 Subject: Remove unused style --- app/assets/stylesheets/pages/projects.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index b3054e36247..ac2d50f9c18 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -18,10 +18,6 @@ } } -.project-edit-content { - padding: 7px; -} - .project-name-holder { .help-inline { vertical-align: top; -- cgit v1.2.1 From 9517b6211782619ba4d4f9f1322230bd833c0a2c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:20:11 +0100 Subject: Link MR list item branch name to branch --- app/assets/stylesheets/framework/common.scss | 4 ---- app/views/projects/merge_requests/_merge_request.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61689aff57e..1c7c31b02a9 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -337,10 +337,6 @@ table { text-align: center; } -.task-status { - margin-left: 10px; -} - #nprogress .spinner { top: 15px !important; right: 10px !important; diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 8246a432c77..60dd4f4f87c 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -44,8 +44,8 @@ = link_to_label(label, project: merge_request.project) - if merge_request.target_project.default_branch != merge_request.target_branch   - %span - %i.fa.fa-code-fork + = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do + = icon('code-fork') = merge_request.target_branch - if merge_request.tasks?   -- cgit v1.2.1 From 080a0c026bbd2f651eab68c816991e31e57e11ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:52:36 +0100 Subject: Use same text color for commit box as issue title --- app/assets/stylesheets/pages/commit.scss | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index a0e5f7554ed..39cde517c16 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -2,10 +2,6 @@ display: block; } -.commit-title{ - margin-bottom: 10px; -} - .commit-author, .commit-committer{ display: block; color: #999; @@ -41,6 +37,8 @@ .commit-box { .commit-title { margin: 0; + font-size: 23px; + color: #313236; } .commit-description { -- cgit v1.2.1 From 67ff47b39cfa47a6904bd5e4e84499a098a158cf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:11 +0100 Subject: Capitalize tab titles --- app/helpers/blob_helper.rb | 2 +- app/views/projects/blob/edit.html.haml | 6 +++--- features/steps/project/source/browse_files.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 77d99140c43..df5f5fae23c 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -60,7 +60,7 @@ module BlobHelper if Gitlab::MarkupHelper.previewable?(filename) 'Preview' else - 'Preview changes' + 'Preview Changes' end end diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml index 56745165251..a47fe7ede80 100644 --- a/app/views/projects/blob/edit.html.haml +++ b/app/views/projects/blob/edit.html.haml @@ -5,12 +5,12 @@ %ul.center-top-menu.no-bottom.js-edit-mode %li.active = link_to '#editor' do - %i.fa.fa-edit - Edit file + = icon('edit') + Edit File %li = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do - %i.fa.fa-eye + = icon('eye') = editing_preview_title(@blob.name) = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f40e0f0d528..bee31506779 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -87,7 +87,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I click link "Diff"' do - click_link 'Preview changes' + click_link 'Preview Changes' end step 'I click on "Commit Changes"' do -- cgit v1.2.1 From e9e9f537e84c1b3a1df7b4920d3b696029ba6dcc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:20 +0100 Subject: Truncate submodule commit SHAs to 7 characters --- app/helpers/diff_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index bfd3622a6a9..24134310fc5 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -146,9 +146,9 @@ module DiffHelper def submodule_link(blob, ref, repository = @repository) tree, commit = submodule_links(blob, ref, repository) commit_id = if commit.nil? - blob.id[0..10] + Commit.truncate_sha(blob.id) else - link_to "#{blob.id[0..10]}", commit + link_to Commit.truncate_sha(blob.id), commit end [ -- cgit v1.2.1 From b8a926fab0acc85dd22456366390a93f4c4b6ad9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:28 +0100 Subject: Don't use success state for explore projects search button --- app/views/explore/projects/_filter.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 2761272aa8a..28b12c8dca8 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -3,7 +3,7 @@ .form-group = 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" + = button_tag 'Search', class: "btn" .pull-right.hidden-sm.hidden-xs - if current_user -- cgit v1.2.1 From 12dd4c7c8a393cea2f264832e38df4286591f48c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:53:56 +0100 Subject: Use "Delete" in milestone and label delete buttons instead of "Remove" --- app/views/projects/labels/_label.html.haml | 2 +- app/views/projects/milestones/_milestone.html.haml | 2 +- features/steps/admin/labels.rb | 2 +- features/steps/project/issues/labels.rb | 2 +- features/steps/project/issues/milestones.rb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index c6ebfa281a1..b70a9fc9fe5 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -7,4 +7,4 @@ - if can? current_user, :admin_label, @project = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm' - = link_to 'Remove', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} + = link_to 'Delete', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 5e93d55b1fb..334172b976f 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -31,4 +31,4 @@ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close" = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do %i.fa.fa-trash-o - Remove + Delete diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index b45d98658bc..2d5db8f739e 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -17,7 +17,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I remove label \'bug\'' do page.within "#label_#{bug_label.id}" do - click_link 'Remove' + click_link 'Delete' end end diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index d656acf4220..047cf701bb0 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -9,7 +9,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I remove label \'bug\'' do page.within "#label_#{bug_label.id}" do - click_link 'Remove' + click_link 'Delete' end end diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index c8708572ec6..e2eda511497 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -63,7 +63,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps end step 'I click link to remove milestone' do - click_link 'Remove' + click_link 'Delete' end step 'I should see no milestones' do -- cgit v1.2.1 From f8e577591b29b4cde211f275db6f870f98f58eb3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:12 +0100 Subject: Don't use success state for "Download zip" button --- app/views/projects/repositories/_download_archive.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml index 07c24950ee2..b9486a9b492 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 btn-success col-xs-10', rel: 'nofollow' do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do %i.fa.fa-download %span Download zip - %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' } %span.caret %span.sr-only Select Archive Format -- cgit v1.2.1 From 40760d3f7bc1b5fbed17bff2f185e92538590484 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:24 +0100 Subject: Add header title to import pages --- app/views/import/bitbucket/status.html.haml | 1 + app/views/import/fogbugz/new.html.haml | 1 + app/views/import/fogbugz/new_user_map.html.haml | 1 + app/views/import/fogbugz/status.html.haml | 1 + app/views/import/github/status.html.haml | 1 + app/views/import/gitlab/status.html.haml | 1 + app/views/import/gitorious/status.html.haml | 1 + app/views/import/google_code/new.html.haml | 3 ++- app/views/import/google_code/new_user_map.html.haml | 17 +++++++++-------- app/views/import/google_code/status.html.haml | 1 + 10 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml index 1f09a27e2d6..aec2e836c9f 100644 --- a/app/views/import/bitbucket/status.html.haml +++ b/app/views/import/bitbucket/status.html.haml @@ -1,4 +1,5 @@ - page_title "Bitbucket import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bitbucket Import projects from Bitbucket diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index e1bb88ca4ed..5515fad6f48 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -1,4 +1,5 @@ - page_title "FogBugz Import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml index bc3c90294e3..07338736bac 100644 --- a/app/views/import/fogbugz/new_user_map.html.haml +++ b/app/views/import/fogbugz/new_user_map.html.haml @@ -1,4 +1,5 @@ - page_title 'User map', 'FogBugz import' +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml index b902006597b..6ee16c8be4b 100644 --- a/app/views/import/fogbugz/status.html.haml +++ b/app/views/import/fogbugz/status.html.haml @@ -1,4 +1,5 @@ - page_title "FogBugz import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-bug Import projects from FogBugz diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 0699321c8c0..1416ee5bd5a 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,4 +1,5 @@ - page_title "GitHub import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-github Import projects from GitHub diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml index f4a2b33af21..911a55eb85d 100644 --- a/app/views/import/gitlab/status.html.haml +++ b/app/views/import/gitlab/status.html.haml @@ -1,4 +1,5 @@ - page_title "GitLab.com import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-heart Import projects from GitLab.com diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml index 71752d21efa..6b0fa1edf8c 100644 --- a/app/views/import/gitorious/status.html.haml +++ b/app/views/import/gitorious/status.html.haml @@ -1,4 +1,5 @@ - page_title "Gitorious import" +- header_title "Projects", root_path %h3.page-title %i.icon-gitorious.icon-gitorious-big Import projects from Gitorious.org diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml index 9c64e0a009f..5d2f149cd5f 100644 --- a/app/views/import/google_code/new.html.haml +++ b/app/views/import/google_code/new.html.haml @@ -1,4 +1,5 @@ - page_title "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code @@ -6,7 +7,7 @@ = form_tag callback_import_google_code_path, class: 'form-horizontal', multipart: true do %p - Follow the steps below to export your Google Code project data. + Follow the steps below to export your Google Code project data. In the next step, you'll be able to select the projects you want to import. %ol %li diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml index e53ebda7dc1..0738b3db1eb 100644 --- a/app/views/import/google_code/new_user_map.html.haml +++ b/app/views/import/google_code/new_user_map.html.haml @@ -1,4 +1,5 @@ - page_title "User map", "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code @@ -8,31 +9,31 @@ %p Customize how Google Code email addresses and usernames are imported into GitLab. In the next step, you'll be able to select the projects you want to import. - %p + %p The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of :. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side. %ul %li %strong Default: Directly import the Google Code email address or username %p - "johnsmith@example.com": "johnsm...@example.com" - will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. + "johnsmith@example.com": "johnsm...@example.com" + will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. The email address or username is masked to ensure the user's privacy. %li %strong Map a Google Code user to a GitLab user %p - "johnsmith@example.com": "@johnsmith" - will add "By @johnsmith" to all issues and comments originally created by johnsmith@example.com, + "johnsmith@example.com": "@johnsmith" + will add "By @johnsmith" to all issues and comments originally created by johnsmith@example.com, and will set @johnsmith as the assignee on all issues originally assigned to johnsmith@example.com. %li %strong Map a Google Code user to a full name %p - "johnsmith@example.com": "John Smith" + "johnsmith@example.com": "John Smith" will add "By John Smith" to all issues and comments originally created by johnsmith@example.com. %li %strong Map a Google Code user to a full email address %p - "johnsmith@example.com": "johnsmith@example.com" - will add "By johnsmith@example.com" to all issues and comments originally created by johnsmith@example.com. + "johnsmith@example.com": "johnsmith@example.com" + will add "By johnsmith@example.com" to all issues and comments originally created by johnsmith@example.com. By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address. .form-group diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml index 8c64fd27e60..175ef6921cd 100644 --- a/app/views/import/google_code/status.html.haml +++ b/app/views/import/google_code/status.html.haml @@ -1,4 +1,5 @@ - page_title "Google Code import" +- header_title "Projects", root_path %h3.page-title %i.fa.fa-google Import projects from Google Code -- cgit v1.2.1 From 0ec0c93824096fc6b26de369e170ffc729967bd3 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 2 Dec 2015 15:54:44 +0200 Subject: Also update gitlab-workhorse in patch updates [ci skip] --- doc/update/patch_versions.md | 45 +++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 593722eb01f..957354decb7 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -6,7 +6,8 @@ For example from 7.14.0 to 7.14.3, also see the [semantic versioning specificati ### 0. Backup It's useful to make a backup just in case things go south: -(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version) +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab +user on the database version) ```bash cd /home/git/gitlab @@ -15,19 +16,23 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production ### 1. Stop server - sudo service gitlab stop +```bash +sudo service gitlab stop +``` ### 2. Get latest code for the stable branch +In the commands below, 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`). + ```bash cd /home/git/gitlab sudo -u git -H git fetch --all sudo -u git -H git checkout -- Gemfile.lock db/schema.rb 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 @@ -37,12 +42,20 @@ sudo -u git -H git fetch sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` ``` -### 4. Install libs, migrations, etc. +### 4. Update gitlab-workhorse to the corresponding version + +```bash +cd /home/git/gitlab-workhorse +sudo -u git -H git fetch +sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` +``` + +### 5. Install libs, migrations, etc. ```bash cd /home/git/gitlab -#PostgreSQL +# PostgreSQL sudo -u git -H bundle install --without development test mysql --deployment # MySQL @@ -52,19 +65,25 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` -### 5. Start application +### 6. Start application - sudo service gitlab start - sudo service nginx restart +```bash +sudo service gitlab start +sudo service nginx restart +``` -### 6. Check application status +### 7. Check application status Check if GitLab and its environment are configured correctly: - sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +```bash +sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production +``` To make sure you didn't miss anything run a more thorough check with: - sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +```bash +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production +``` If all items are green, then congratulations upgrade complete! -- cgit v1.2.1 From 90df3701e753257a7b71d97c8eec590cc148f409 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:50 +0100 Subject: Add plus icon to all "Add X" buttons --- app/views/profiles/keys/index.html.haml | 4 +++- app/views/projects/branches/index.html.haml | 2 +- app/views/projects/labels/index.html.haml | 1 + app/views/projects/tags/index.html.haml | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml index 14adba1c797..17a4195030e 100644 --- a/app/views/profiles/keys/index.html.haml +++ b/app/views/profiles/keys/index.html.haml @@ -3,7 +3,9 @@ .gray-content-block.top-block .pull-right - = link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new" + = link_to new_profile_key_path, class: "btn btn-new" do + = icon('plus') + Add SSH Key .oneline Before you can add an SSH key you need to = link_to "generate it.", help_page_path("ssh", "README") diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml index 03ade02a0c8..204def60794 100644 --- a/app/views/projects/branches/index.html.haml +++ b/app/views/projects/branches/index.html.haml @@ -5,7 +5,7 @@ .pull-right - if can? current_user, :push_code, @project = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do - %i.fa.fa-add-sign + = icon('plus') New branch   .dropdown.inline diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index fb784ee5f4f..9081bcfe9b3 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -4,6 +4,7 @@ .gray-content-block.top-block - if can? current_user, :admin_label, @project = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do + = icon('plus') New label .oneline Labels can be applied to issues and merge requests. diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 85d76eae3b5..760347de0a9 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -6,7 +6,7 @@ - if can? current_user, :push_code, @project .pull-right = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do - %i.fa.fa-add-sign + = icon('plus') New tag .oneline Tags give the ability to mark specific points in history as being important -- cgit v1.2.1 From a3da3d8e3b60f8eeca716ce231ab5670a637e3eb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:54:58 +0100 Subject: Use monospace font for commit SHA in branch list --- app/views/projects/branches/_commit.html.haml | 2 +- app/views/projects/tree/_tree_content.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index 22d77dda938..9fe65cbb104 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,5 +1,5 @@ .branch-commit - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id" + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id monospace" · %span.str-truncated = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml index c64e684df26..1bc90edd8f0 100644 --- a/app/views/projects/tree/_tree_content.html.haml +++ b/app/views/projects/tree/_tree_content.html.haml @@ -12,7 +12,7 @@ %i.fa.fa-angle-right   %small.light - = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit) + = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" – = truncate(@commit.title, length: 50) = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right' -- cgit v1.2.1 From 59c3ec4e67f598e1a67df233f2d12fbec3d033dc Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:55:36 +0100 Subject: Use file-type specific file icon in blame view and diff item --- app/views/projects/blame/show.html.haml | 3 +-- app/views/projects/diffs/_file.html.haml | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml index 6518c4173e1..8d9ec068a43 100644 --- a/app/views/projects/blame/show.html.haml +++ b/app/views/projects/blame/show.html.haml @@ -6,7 +6,7 @@ #tree-holder.tree-holder .file-holder .file-title - %i.fa.fa-file + = blob_icon @blob.mode, @blob.name %strong = @path %small= number_to_human_size @blob.size @@ -43,4 +43,3 @@ - blame_group[:lines].each do |line| :erb <%= highlight(@blob.name, line, nowrap: true, continue: true).html_safe %> - diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index c745b4e69bf..b3392d00e01 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -2,19 +2,27 @@ .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"} - if diff_file.diff.submodule? %span + = icon('archive fw') - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) - = submodule_link(submodule_item, @commit.id, project.repository) + %strong + = submodule_link(submodule_item, @commit.id, project.repository) - else %span + = blob_icon blob.mode, blob.name + = link_to "#diff-#{i}" do + %strong + = diff_file.new_path + - if diff_file.deleted_file - = "#{diff_file.old_path} deleted" + deleted - elsif diff_file.renamed_file - = "#{diff_file.old_path} renamed to #{diff_file.new_path}" - - else - = diff_file.new_path + renamed from + %strong + = diff_file.old_path - if diff_file.mode_changed? - %span.file-mode= "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" + %small + = "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" .diff-controls - if blob.text? -- cgit v1.2.1 From 854deae4129c002faf97f615c17e742e8eff9166 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:56:17 +0100 Subject: Brefer "Directory" over "Dir", "Files" over "Code --- app/helpers/commits_helper.rb | 4 ++-- app/views/projects/commit/_commit_box.html.haml | 5 +++-- features/steps/project/source/browse_files.rb | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 9df20c9fce5..590d20ac7b3 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -109,7 +109,7 @@ module CommitsHelper ) elsif @path.present? return link_to( - "Browse Dir »", + "Browse Directory »", namespace_project_tree_path(project.namespace, project, tree_join(commit.id, @path)), class: "pull-right" @@ -117,7 +117,7 @@ module CommitsHelper end end link_to( - "Browse Code »", + "Browse Files »", namespace_project_tree_path(project.namespace, project, commit), class: "pull-right" ) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 776768537d0..d8bfe6a07ac 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -13,8 +13,9 @@ - unless @commit.parents.length > 1 %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch) %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff) - = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do - %span Browse Code » + = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped" do + = icon('files-o') + Browse Files %div %p diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index bee31506779..ceab23b9a4d 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -192,7 +192,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I see Browse dir link' do - expect(page).to have_link 'Browse Dir »' + expect(page).to have_link 'Browse Directory »' expect(page).not_to have_link 'Browse Code »' end @@ -204,13 +204,13 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps step 'I see Browse file link' do expect(page).to have_link 'Browse File »' - expect(page).not_to have_link 'Browse Code »' + expect(page).not_to have_link 'Browse Files »' end step 'I see Browse code link' do - expect(page).to have_link 'Browse Code »' + expect(page).to have_link 'Browse Files »' expect(page).not_to have_link 'Browse File »' - expect(page).not_to have_link 'Browse Dir »' + expect(page).not_to have_link 'Browse Directory »' end step 'I click on Permalink' do -- cgit v1.2.1 From 03277af65d111be7832fab060b3de8c42f885847 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:57:11 +0100 Subject: Use standard style for tabs in profile view --- app/views/users/show.html.haml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index d5a92cb816a..e35e69bda80 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -73,7 +73,7 @@ .user-calendar-activities -%ul.center-middle-menu +%ul.center-top-menu.no-top.no-bottom %li.active = link_to "#activity", 'data-toggle' => 'tab' do Activity @@ -92,17 +92,26 @@ .tab-content .tab-pane.active#activity + .gray-content-block.middle-block + Public activity by #{@user.name} + .content_list = spinner - if @groups.any? .tab-pane#groups + .gray-content-block.middle-block + Groups #{@user.name} is a member of + %ul.content-list - @groups.each do |group| = render 'shared/groups/group', group: group - if @contributed_projects.present? .tab-pane#contributed + .gray-content-block.middle-block + Projects #{@user.name} has contributed to + .contributed-projects = render 'shared/projects/list', projects: @contributed_projects.sort_by(&:star_count).reverse, @@ -110,6 +119,9 @@ - if @projects.present? .tab-pane#personal + .gray-content-block.middle-block + Projects owned by #{@user.name} + .personal-projects = render 'shared/projects/list', projects: @projects.sort_by(&:star_count).reverse, -- cgit v1.2.1 From c12e4de50cab653a61e15ef4eec915ae093c32d2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:57:38 +0100 Subject: Move "Start new merge request" checkbox closer to New branch field --- app/assets/javascripts/new_commit_form.js.coffee | 6 +++--- app/views/shared/_new_commit_form.html.haml | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee index 2e561dea3e1..3c7b776155f 100644 --- a/app/assets/javascripts/new_commit_form.js.coffee +++ b/app/assets/javascripts/new_commit_form.js.coffee @@ -3,7 +3,7 @@ class @NewCommitForm @newBranch = form.find('.js-new-branch') @originalBranch = form.find('.js-original-branch') @createMergeRequest = form.find('.js-create-merge-request') - @createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group') + @createMergeRequestContainer = form.find('.js-create-merge-request-container') @renderDestination() @newBranch.keyup @renderDestination @@ -12,10 +12,10 @@ class @NewCommitForm different = @newBranch.val() != @originalBranch.val() if different - @createMergeRequestFormGroup.show() + @createMergeRequestContainer.show() @createMergeRequest.prop('checked', true) unless @wasDifferent else - @createMergeRequestFormGroup.hide() + @createMergeRequestContainer.hide() @createMergeRequest.prop('checked', false) @wasDifferent = different diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index 8636341c60d..2931fb0c2ce 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -7,12 +7,11 @@ .col-sm-10 = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch" - .form-group.js-create-merge-request-form-group - .col-sm-offset-2.col-sm-10 - .checkbox - - nonce = SecureRandom.hex - = label_tag "create_merge_request-#{nonce}" do - = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" - Start a new merge request with this commit + .js-create-merge-request-container + .checkbox + - nonce = SecureRandom.hex + = label_tag "create_merge_request-#{nonce}" do + = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" + Start a new merge request with these changes = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch' -- cgit v1.2.1 From 69709bbdd094dc5e5157fffd8710791ae6efb826 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:57:51 +0100 Subject: Separate page title segments by middot rather than pipe --- app/helpers/page_layout_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 775cf5a3dd4..9bf750124b2 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -4,7 +4,8 @@ module PageLayoutHelper @page_title.push(*titles.compact) if titles.any? - @page_title.join(" | ") + # Segments are seperated by middot + @page_title.join(" \u00b7 ") end def header_title(title = nil, title_url = nil) -- cgit v1.2.1 From d2b30dd2319023bc5d553e0e7eeb1ed6e1d745cd Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:58:04 +0100 Subject: Fix incorrect active state for "Your Projects" tab --- app/views/dashboard/_projects_head.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index ed480b8caf8..efcd7b50b4c 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,5 +1,5 @@ %ul.center-top-menu - = nav_link(path: ['projects#index', 'root#index']) do + = nav_link(page: [dashboard_projects_path, root_path]) do = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do Your Projects = nav_link(page: starred_dashboard_projects_path) do -- cgit v1.2.1 From 7128756d9f68146c51225fec79a7a9173a805911 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:58:12 +0100 Subject: Extract title from copy/pasted SSH key when it ends in a newline --- app/views/profiles/keys/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml index 2bf207a3221..11166dc6d99 100644 --- a/app/views/profiles/keys/new.html.haml +++ b/app/views/profiles/keys/new.html.haml @@ -9,7 +9,7 @@ $('#key_key').on('focusout', function(){ var title = $('#key_title'), val = $('#key_key').val(), - comment = val.match(/^\S+ \S+ (.+)$/); + comment = val.match(/^\S+ \S+ (.+)\n?$/); if( comment && comment.length > 1 && title.val() == '' ){ $('#key_title').val( comment[1] ); -- cgit v1.2.1 From 0827455710d9bd836e2bd771249bfdbd0bf1bdf4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:58:27 +0100 Subject: Move branch list info tags out of main link --- app/views/projects/branches/_branch.html.haml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 3f95e2a1bf6..ba6995aa671 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -3,17 +3,17 @@ %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do %strong.str-truncated= branch.name -   - - if branch.name == @repository.root_ref - %span.label.label-primary default - - elsif @repository.merged_to_root_ref? branch.name - %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}") - merged +   + - if branch.name == @repository.root_ref + %span.label.label-primary default + - elsif @repository.merged_to_root_ref? branch.name + %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}") + merged - - if @project.protected_branch? branch.name - %span.label.label-success - %i.fa.fa-lock - protected + - if @project.protected_branch? branch.name + %span.label.label-success + %i.fa.fa-lock + protected .controls.hidden-xs - if create_mr_button?(@repository.root_ref, branch.name) = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-grouped btn-xs' do -- cgit v1.2.1 From 7a6986039dd2bd349bbc3dd7d97ab9bbec14e86c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:58:40 +0100 Subject: Only show "Manage group members" in project member list when user has access --- app/views/projects/project_members/_group_members.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml index 0c73d7e34ac..d2810f9707a 100644 --- a/app/views/projects/project_members/_group_members.html.haml +++ b/app/views/projects/project_members/_group_members.html.haml @@ -4,10 +4,11 @@ group members %small (#{members.count}) - .pull-right - = link_to group_group_members_path(@group), class: 'btn' do - = icon('pencil-square-o') - Edit group members + - if can?(current_user, :admin_group_member, @group) + .pull-right + = link_to group_group_members_path(@group), class: 'btn' do + = icon('pencil-square-o') + Manage group members %ul.content-list - members.each do |member| = render 'groups/group_members/group_member', member: member, show_controls: false -- cgit v1.2.1 From 6789bd25ef8686e47ea2fd047e737aefd5033526 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:59:04 +0100 Subject: Make tree view "Add file" button smaller --- app/views/projects/tree/_tree_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 1115ca6b4ca..12356dbcb6b 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -14,7 +14,7 @@ - if allowed_tree_edit? %li %span.dropdown - %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"} + %a.dropdown-toggle.btn.btn-sm.add-to-tree{href: '#', "data-toggle" => "dropdown"} = icon('plus') %ul.dropdown-menu %li -- cgit v1.2.1 From e718bab9e16be3b1bbdcf8dada5825b7e595682d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:59:25 +0100 Subject: "CI build passed", not "CI build success" --- app/assets/stylesheets/pages/profile.scss | 6 ------ app/helpers/ci_status_helper.rb | 16 ++++++++++++++-- app/views/projects/_last_commit.html.haml | 2 +- app/views/projects/builds/show.html.haml | 2 +- app/views/projects/commit/_commit_box.html.haml | 2 +- .../projects/merge_requests/widget/_heading.html.haml | 5 ++--- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 1d6ca0dfc13..95fc26a608a 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -5,12 +5,6 @@ } } -.btn-build-token { - float: left; - padding: 6px 20px; - margin-right: 12px; -} - .profile-avatar-form-option { hr { margin: 10px 0; diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 0ecf77bb45e..70f8c9ae221 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -8,6 +8,10 @@ module CiStatusHelper ci_icon_for_status(ci_commit.status) end + def ci_status_label(ci_commit) + ci_label_for_status(ci_commit.status) + end + def ci_status_color(ci_commit) case ci_commit.status when 'success' @@ -23,7 +27,15 @@ module CiStatusHelper def ci_status_with_icon(status) content_tag :span, class: "ci-status ci-#{status}" do - ci_icon_for_status(status) + ' '.html_safe + status + ci_icon_for_status(status) + ' '.html_safe + ci_label_for_status(status) + end + end + + def ci_label_for_status(status) + if status == 'success' + 'passed' + else + status end end @@ -46,7 +58,7 @@ module CiStatusHelper def render_ci_status(ci_commit) link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", + title: "Build status: #{ci_status_label(ci_commit)}", data: { toggle: 'tooltip', placement: 'left' } do ci_status_icon(ci_commit) end diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml index 7e1ee2b7fc1..386d72e7787 100644 --- a/app/views/projects/_last_commit.html.haml +++ b/app/views/projects/_last_commit.html.haml @@ -3,7 +3,7 @@ - if ci_commit = link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do = ci_status_icon(ci_commit) - = ci_commit.status + = ci_status_label(ci_commit) = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message" diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 907e1ce10bd..7890232540f 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@build.name} (#{@build.id})", "Builds" +- page_title "#{@build.name} (##{@build.id})", "Builds" = render "header_title" .build-page diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 776768537d0..593dc0f7f32 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -43,7 +43,7 @@ = link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do = ci_status_icon(@ci_commit) build: - = @ci_commit.status + = ci_status_label(@ci_commit) .commit-info-row.branches %i.fa.fa-spinner.fa-spin diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index ba5ad22bca7..0f2996efe2c 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,10 +1,9 @@ - ci_commit = @merge_request.ci_commit - if ci_commit - - status = ci_commit.status .mr-widget-heading - .ci_widget{class: "ci-#{status}"} + .ci_widget{class: "ci-#{ci_commit.status}"} = ci_status_icon(ci_commit) - %span CI build #{status} + %span CI build #{ci_status_label(ci_commit)} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage = link_to "View build details", ci_status_path(ci_commit) -- cgit v1.2.1 From 8b7dd1c1ce9822e0c1d1d126e1c618d1094d0b22 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 14:59:33 +0100 Subject: Add clipboard button to commit box SHA --- app/views/projects/commit/_commit_box.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 593dc0f7f32..c1e854cf86e 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -19,7 +19,8 @@ %p %span.light Commit - = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace" + = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace", data: { clipboard_text: @commit.id } + = clipboard_button .commit-info-row %span.light Authored by %strong -- cgit v1.2.1 From 67c74071a8b63cf905e6c65e9818317a99db9105 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:00:16 +0100 Subject: Add permission check for tag list release notes button --- app/views/projects/tags/_tag.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index e2c5178185e..00c0b0b934c 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -11,11 +11,12 @@ = strip_gpg_signature(tag.message) .controls - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn' do - = icon("pencil") - - if can? current_user, :download_code, @project + - if can?(current_user, :download_code, @project) = render 'projects/tags/download', ref: tag.name, project: @project + - if can?(current_user, :push_code, @project) + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do + = icon("pencil") - if commit = render 'projects/branches/commit', commit: commit, project: @project - else -- cgit v1.2.1 From ebc5a62bc082df08e5b5508a3dd9322b2e8f2bcf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:00:22 +0100 Subject: Add tag delete button --- app/views/projects/tags/_tag.html.haml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 00c0b0b934c..28b706c5c7e 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -17,6 +17,11 @@ - if can?(current_user, :push_code, @project) = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do = icon("pencil") + + - if can?(current_user, :admin_project, @project) + = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do + = icon("trash-o") + - if commit = render 'projects/branches/commit', commit: commit, project: @project - else -- cgit v1.2.1 From d8a0e4538d0189b524f6745c32d5b98cd3ed8dc8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:00:28 +0100 Subject: Add tooltip to branch delete button --- app/views/projects/branches/_branch.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index ba6995aa671..5081bae6801 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -26,7 +26,7 @@ Compare - if can_remove_branch?(@project, branch.name) - = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true do + = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = icon("trash-o") - if commit -- cgit v1.2.1 From de33866dce1c544b51b9b42ce382bcebb6822fcf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:02:02 +0100 Subject: Add tooltips to tag buttons --- app/views/projects/tags/show.html.haml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 879c6c7d310..b594d4f1f27 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -5,17 +5,17 @@ .gray-content-block .pull-right - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn', title: 'Edit release notes' do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse source code' do + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped', title: 'Browse commits' do + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do = icon('history') - if can? current_user, :download_code, @project = render 'projects/tags/download', ref: @tag.name, project: @project - if can?(current_user, :admin_project, @project) .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o .title %strong= @tag.name -- cgit v1.2.1 From e3fe255c057e86d841a68f9a96b1a62b07036781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Wed, 2 Dec 2015 15:21:02 +0100 Subject: fixed the documentation of the Guest role in permission.md --- doc/permissions/permissions.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md index 8d4c2ceab7d..bcd00cfc6bf 100644 --- a/doc/permissions/permissions.md +++ b/doc/permissions/permissions.md @@ -6,6 +6,9 @@ If a user is both in a project group and in the project itself, the highest perm If a user is a GitLab administrator they receive all permissions. +On public projects the Guest role is not enforced. +All users will be able to create issues, leave comments, and pull or download the project code. + To add or import a user, you can follow the [project users and members documentation](doc/workflow/add-user/add-user.md). @@ -15,8 +18,8 @@ documentation](doc/workflow/add-user/add-user.md). |---------------------------------------|---------|------------|-------------|----------|--------| | Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ | -| Pull project code | ✓ | ✓ | ✓ | ✓ | ✓ | -| Download project | ✓ | ✓ | ✓ | ✓ | ✓ | +| Pull project code | | ✓ | ✓ | ✓ | ✓ | +| Download project | | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ | -- cgit v1.2.1 From 1fb3c0996acbe9b8e44376e72e24c867a588f4f0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:32:45 +0100 Subject: Fix styling of empty repo message --- app/assets/stylesheets/framework/blocks.scss | 4 ++++ app/assets/stylesheets/pages/projects.scss | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index dec50a0e0f6..8836c8b666b 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -68,6 +68,10 @@ .oneline { line-height: 42px; } + + > p:last-child { + margin-bottom: 0; + } } .cover-block { diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ac2d50f9c18..9d5b62c75d3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -26,12 +26,6 @@ } .project-home-panel { - text-align: center; - background: #f7f8fa; - margin: -$gl-padding; - padding: $gl-padding; - padding: 44px 0 17px 0; - .project-identicon-holder { margin-bottom: 16px; @@ -101,7 +95,6 @@ display: inline-table; position: relative; top: 17px; - margin-bottom: 44px; } .project-repo-buttons { -- cgit v1.2.1 From 845f26d07b97a281057a09facae28b2bbdb0168b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:04:29 +0100 Subject: Remove minimum containe height --- app/assets/stylesheets/framework/layout.scss | 7 ++++--- app/assets/stylesheets/framework/sidebar.scss | 5 ++--- app/assets/stylesheets/pages/login.scss | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index b91c15d8910..a60940a8bee 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -2,10 +2,10 @@ html { overflow-y: scroll; &.touch .tooltip { display: none !important; } +} - body { - padding-top: $header-height; - } +body { + background-color: #EAEBEC !important; } .container { @@ -18,6 +18,7 @@ html { } .navless-container { + padding-top: $header-height; margin-top: 30px; } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index c1b0129c866..81cda68b94c 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,4 +1,6 @@ .page-with-sidebar { + padding-top: $header-height; + .sidebar-wrapper { position: fixed; top: 0; @@ -18,15 +20,12 @@ } .content-wrapper { - min-height: 100vh; width: 100%; padding: 20px; - background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; - min-height: 90vh; &.container-blank { background: none; diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index 83b866c3a64..edd51705136 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -1,5 +1,7 @@ /* Login Page */ .login-page { + background-color: white; + .container { max-width: 960px; } -- cgit v1.2.1 From acc35a58a7455a9107d6ebf2035f88350b1fb1ce Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 15:52:33 +0100 Subject: Revert unrelated changes --- app/assets/stylesheets/framework/sidebar.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 458af76cb75..fa463975805 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -20,12 +20,15 @@ } .content-wrapper { + min-height: 100vh; width: 100%; padding: 20px; + background: #EAEBEC; .container-fluid { background: #FFF; padding: $gl-padding; + min-height: 90vh; &.container-blank { background: none; -- cgit v1.2.1 From 74a81cbfe3e7b6fb6c3b7a620b07905f19971a86 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 17:30:50 +0100 Subject: Fix branch rendering --- app/helpers/merge_requests_helper.rb | 6 ++++-- app/views/projects/merge_requests/_show.html.haml | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index cc4243e1559..7f3a61a5e38 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -56,12 +56,14 @@ module MergeRequestsHelper end def source_branch_with_namespace(merge_request) + branch = link_to(merge_request.source_branch, namespace_project_commits_path(merge_request.source_project.namespace, merge_request.source_project, merge_request.source_branch)) + if merge_request.for_fork? namespace = link_to(merge_request.source_project_namespace, project_path(merge_request.source_project)) - namespace + ":#{merge_request.source_branch}" + namespace + ":" + branch else - merge_request.source_branch + branch end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 09b228b7c0c..e1992504232 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,7 +26,8 @@ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) .normal %span Request to merge - = link_to source_branch_with_namespace(@merge_request), namespace_project_commits_path(@merge_request.source_project.namespace, @merge_request.source_project, @merge_request.source_branch), class: "label-branch" + %span.label-branch + = source_branch_with_namespace(@merge_request) %span into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do = @merge_request.target_branch -- cgit v1.2.1 From f10bd7df90cd509848dfcc9a574d7e4569a0428e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 17:37:06 +0100 Subject: Tweak merge request list item --- app/views/projects/merge_requests/_merge_request.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 60dd4f4f87c..1d4c9b66c42 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -33,7 +33,12 @@ \##{merge_request.iid} · opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)} - - if merge_request.milestone_id? + - if merge_request.target_project.default_branch != merge_request.target_branch +   + = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do + = icon('code-fork') + = merge_request.target_branch + - if merge_request.milestone   = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do = icon('clock-o') @@ -42,11 +47,6 @@   - merge_request.labels.each do |label| = link_to_label(label, project: merge_request.project) - - if merge_request.target_project.default_branch != merge_request.target_branch -   - = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do - = icon('code-fork') - = merge_request.target_branch - if merge_request.tasks?   %span.task-status -- cgit v1.2.1 From 044e0e33dce9371430a3c91e63f9f687b536d35b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 2 Dec 2015 18:48:39 +0100 Subject: Allow invalid URLs in closing pattern --- lib/gitlab/closing_issue_extractor.rb | 4 +- spec/lib/gitlab/closing_issue_extractor_spec.rb | 49 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb index 0cf4c918736..9bef9037ad6 100644 --- a/lib/gitlab/closing_issue_extractor.rb +++ b/lib/gitlab/closing_issue_extractor.rb @@ -1,8 +1,10 @@ module Gitlab class ClosingIssueExtractor ISSUE_CLOSING_REGEX = begin + link_pattern = URI.regexp(%w(http https)) + pattern = Gitlab.config.gitlab.issue_closing_pattern - pattern = pattern.sub('%{issue_ref}', "(?:(?:#{Issue.link_reference_pattern})|(?:#{Issue.reference_pattern}))") + pattern = pattern.sub('%{issue_ref}', "(?:(?:#{link_pattern})|(?:#{Issue.reference_pattern}))") Regexp.new(pattern).freeze end diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index 21254f778d3..e9d7e8c1111 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -2,11 +2,18 @@ require 'spec_helper' describe Gitlab::ClosingIssueExtractor do let(:project) { create(:project) } + let(:project2) { create(:project) } let(:issue) { create(:issue, project: project) } + let(:issue2) { create(:issue, project: project2) } let(:reference) { issue.to_reference } + let(:cross_reference) { issue2.to_reference(project) } subject { described_class.new(project, project.creator) } + before do + project2.team << [project.creator, :master] + end + describe "#closed_by_message" do context 'with a single reference' do it do @@ -130,6 +137,27 @@ describe Gitlab::ClosingIssueExtractor do end end + context "with a cross-project reference" do + it do + message = "Closes #{cross_reference}" + expect(subject.closed_by_message(message)).to eq([issue2]) + end + end + + context "with a cross-project URL" do + it do + message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid}" + expect(subject.closed_by_message(message)).to eq([issue2]) + end + end + + context "with an invalid URL" do + it do + message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid}" + expect(subject.closed_by_message(message)).to eq([]) + end + end + context 'with multiple references' do let(:other_issue) { create(:issue, project: project) } let(:third_issue) { create(:issue, project: project) } @@ -171,6 +199,27 @@ describe Gitlab::ClosingIssueExtractor do expect(subject.closed_by_message(message)). to match_array([issue, other_issue, third_issue]) end + + it "fetches cross-project references" do + message = "Closes #{reference} and #{cross_reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue, issue2]) + end + + it "fetches cross-project URL references" do + message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue, issue2]) + end + + it "ignores invalid cross-project URL references" do + message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + + expect(subject.closed_by_message(message)). + to match_array([issue]) + end end end end -- cgit v1.2.1 From ec754d221368fad2b765fa60c665a461b2b29c78 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 2 Dec 2015 13:21:07 -0500 Subject: Be more explicit with the impersonate return URL --- app/controllers/admin/impersonation_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb index 102dd437402..bf98af78615 100644 --- a/app/controllers/admin/impersonation_controller.rb +++ b/app/controllers/admin/impersonation_controller.rb @@ -11,7 +11,7 @@ class Admin::ImpersonationController < Admin::ApplicationController redirect_to admin_user_path(@user) else session[:impersonator_id] = current_user.username - session[:impersonator_return_to] = request.env['HTTP_REFERER'] + session[:impersonator_return_to] = admin_user_path(@user) warden.set_user(user, scope: 'user') -- cgit v1.2.1 From f1bbd8119db3e494e662cd219a9882e5e17bf4bc Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 2 Dec 2015 22:03:43 -0200 Subject: Style warning about mentioning many people in a comment --- CHANGELOG | 1 + app/views/projects/_md_preview.html.haml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..89d3f854ae1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Style warning about mentioning many people in a comment v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 8218cf11201..c4d3c9cc30a 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -8,17 +8,17 @@ %a.js-md-preview-button(href="#md-preview-holder" tabindex="-1") Preview - - if defined?(referenced_users) && referenced_users - %span.referenced-users.pull-left.hide - = icon('exclamation-triangle') - You are about to add - %strong - %span.js-referenced-users-count 0 - people - to the discussion. Proceed with caution. - %div .md-write-holder = yield .md.md-preview-holder.hide .js-md-preview{class: (preview_class if defined?(preview_class))} + + - if defined?(referenced_users) && referenced_users + %span.referenced-users.pull-left.hide + = icon('exclamation-triangle') + You are about to add + %strong + %span.js-referenced-users-count 0 + people + to the discussion. Proceed with caution. -- cgit v1.2.1 From f94afdbdb232418da08658516bc469cf8429dadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 2 Dec 2015 14:40:43 -0500 Subject: Fix broken spec related to MySQL and fractional seconds support. --- spec/models/project_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f80fada45e9..06a02c13bf1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -444,7 +444,9 @@ describe Project do before do 2.times do - create(:note_on_commit, project: project2, created_at: date) + # Little fix for special issue related to Fractional Seconds support for MySQL. + # See: https://github.com/rails/rails/pull/14359/files + create(:note_on_commit, project: project2, created_at: date + 1) end end -- cgit v1.2.1 From 162cd0099c6c1f4ee0051e35562636468e88ee0c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:33:38 +0200 Subject: fix deprecation messages in tests --- app/models/project_services/buildkite_service.rb | 2 +- app/models/project_services/drone_ci_service.rb | 2 +- app/models/user.rb | 2 +- app/views/ci/notify/build_fail_email.html.haml | 2 +- lib/gitlab/lfs/response.rb | 2 +- spec/mailers/notify_spec.rb | 12 +++++++----- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 40058b53df5..199ee3a9d0d 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -37,7 +37,7 @@ class BuildkiteService < CiService def compose_service_hook hook = service_hook || build_service_hook hook.url = webhook_url - hook.enable_ssl_verification = enable_ssl_verification + hook.enable_ssl_verification = !!enable_ssl_verification hook.save end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 127684bd274..06c3922593c 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -34,7 +34,7 @@ class DroneCiService < CiService hook = service_hook || build_service_hook # If using a service template, project may not be available hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project - hook.enable_ssl_verification = enable_ssl_verification + hook.enable_ssl_verification = !!enable_ssl_verification hook.save end diff --git a/app/models/user.rb b/app/models/user.rb index 15aab315649..719b49b16fe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -690,7 +690,7 @@ class User < ActiveRecord::Base end def starred?(project) - starred_projects.exists?(project) + starred_projects.exists?(project.id) end def toggle_star(project) diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml index b0aaea89075..de6291aa914 100644 --- a/app/views/ci/notify/build_fail_email.html.haml +++ b/app/views/ci/notify/build_fail_email.html.haml @@ -7,7 +7,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index c18dfbd485d..9be9a65671b 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -260,7 +260,7 @@ module Gitlab end def link_to_project(object) - if object && !object.projects.exists?(@project) + if object && !object.projects.exists?(@project.id) object.projects << @project object.save end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index d6796b07a5b..27e509933b2 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -247,7 +247,7 @@ describe Notify do end describe 'that have been reassigned' do - subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) } + subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) } it_behaves_like 'a multiple recipients email' it_behaves_like 'an answer to an existing thread', 'issue' @@ -278,7 +278,7 @@ describe Notify do describe 'status changed' do let(:status) { 'closed' } - subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } + subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread', 'issue' it_behaves_like 'it should show Gmail Actions View Issue link' @@ -382,7 +382,7 @@ describe Notify do describe 'status changed' do let(:status) { 'reopened' } - subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) } + subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) } it_behaves_like 'an answer to an existing thread', 'merge_request' it_behaves_like 'it should show Gmail Actions View Merge request link' @@ -597,8 +597,10 @@ describe Notify do let(:user) { create(:user, email: 'old-email@mail.com') } before do - user.email = "new-email@mail.com" - user.save + perform_enqueued_jobs do + user.email = "new-email@mail.com" + user.save + end end subject { ActionMailer::Base.deliveries.last } -- cgit v1.2.1 From 54b74aa99885b4f35f580bbb2cd923a381562c39 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:49:02 +0200 Subject: fix Celluloid warnings --- Gemfile.lock | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index dcb4a74e239..cd1855758b2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,23 +116,8 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.17.2) - celluloid-essentials - celluloid-extras - celluloid-fsm - celluloid-pool - celluloid-supervision - timers (>= 4.1.1) - celluloid-essentials (0.20.5) - timers (>= 4.1.1) - celluloid-extras (0.20.5) - timers (>= 4.1.1) - celluloid-fsm (0.20.5) - timers (>= 4.1.1) - celluloid-pool (0.20.5) - timers (>= 4.1.1) - celluloid-supervision (0.20.5) - timers (>= 4.1.1) + celluloid (0.16.0) + timers (~> 4.0.0) charlock_holmes (0.7.3) chunky_png (1.3.5) cliver (0.3.2) @@ -757,7 +742,7 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.1.1) + timers (4.0.4) hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) -- cgit v1.2.1 From f21bbd4444f9466d0dc622b0de286deace642598 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 10:53:58 +0200 Subject: Rails deprecation warning about log_level --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 317b113e100..909526605a1 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -32,7 +32,7 @@ Rails.application.configure do # config.force_ssl = true # See everything in the log (default is :info) - # config.log_level = :debug + config.log_level = :info # Suppress 'Rendered template ...' messages in the log # source: http://stackoverflow.com/a/16369363 -- cgit v1.2.1 From b871c38bf2ca23c915fd5ce55ecfb2245b53bbe3 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 11:49:11 +0200 Subject: Fix failures in master --- app/views/admin/labels/_label.html.haml | 2 +- app/views/projects/milestones/show.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index 596e06243dd..e3ccbf6c3a8 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -2,4 +2,4 @@ = render_colored_label(label) .pull-right = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm' - = link_to 'Remove', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"} + = link_to 'Delete', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"} diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 3a898dfbcfd..eab5333ca29 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -23,7 +23,7 @@ = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do %i.fa.fa-trash-o - Remove + Delete %hr - if @milestone.issues.any? && @milestone.can_be_closed? -- cgit v1.2.1 From 129ad8425e8fcbb1d7025e247d29772831d76b06 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 13:17:04 +0100 Subject: Tweak tag form wording --- app/views/projects/tags/new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index 9c9bfa3f55f..3a2f75fecaa 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -32,7 +32,7 @@ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control' = render 'projects/notes/hints' - .help-block Optionally, you can add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. + .help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page. .form-actions = button_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel' -- cgit v1.2.1 From 9cf67f72a581d4648b2b02b53403dd4318e4f1e9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 1 Dec 2015 12:00:11 +0100 Subject: Add validator for award-emoji note --- app/models/note.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/note.rb b/app/models/note.rb index 1c6345e735c..40b46b85da1 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -41,6 +41,7 @@ class Note < ActiveRecord::Base validates :note, :project, presence: true validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } + validates :note, format: { with: /\A[-_+[:alnum:]]*\z/ }, if: -> (n){ n.is_award } validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } -- cgit v1.2.1 From 22d87b74a9de5d603f101699d7a3665db9627037 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 1 Dec 2015 14:45:24 +0100 Subject: Support award-emoji notes only when it a comment for an issue --- app/services/notes/create_service.rb | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index dbff58dfb9c..20a3ba30755 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,9 +5,9 @@ module Notes note.author = current_user note.system = false - if contains_emoji_only?(params[:note]) + if award_emoji_note? note.is_award = true - note.note = emoji_name(params[:note]) + note.note = emoji_name end if note.save @@ -34,12 +34,23 @@ module Notes note.project.execute_services(note_data, :note_hooks) end - def contains_emoji_only?(note) - note =~ /\A:[-_+[:alnum:]]*:\s?\z/ + private + + def award_emoji_note? + # We support award-emojis only in issue discussion + issue_comment? && contains_emoji_only? + end + + def contains_emoji_only? + params[:note] =~ /\A:[-_+[:alnum:]]*:\s?\z/ + end + + def issue_comment? + params[:noteable_type] == 'Issue' end - def emoji_name(note) - note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + def emoji_name + params[:note].match(/\A:([-_+[:alnum:]]*):\s?/)[1] end end end -- cgit v1.2.1 From ba08811d07cd1804b027b1a37a5278723cdbeb2c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 08:48:21 +0100 Subject: Move note emoji-award implementation to note model (feature envy) --- app/models/note.rb | 33 +++++++++++++++++++++++++++++++++ app/services/notes/create_service.rb | 24 ------------------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 40b46b85da1..8acb2cf7582 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -72,6 +72,7 @@ class Note < ActiveRecord::Base serialize :st_diff before_create :set_diff, if: ->(n) { n.line_code.present? } + before_validation :set_award! class << self def discussions_from_notes(notes) @@ -349,4 +350,36 @@ class Note < ActiveRecord::Base def editable? !system? end + + # Checks if note is an award added from an issue comment. + # + # If note is an award, this method sets is_award to true, + # and changes note content to award-emoji name. + # + # Awards are only supported for issue comments. + # + # Method is executed as a before_validation callback. + # + def set_award! + return unless issue_comment? && contains_emoji_only? + + self.is_award = true + self.note = award_emoji_name + end + + private + + def issue_comment? + noteable.kind_of?(Issue) + end + + def contains_emoji_only? + (note =~ /\A:[-_+[:alnum:]]*:\s?\z/) ? true : false + end + + def award_emoji_name + return nil unless contains_emoji_only? + + note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + end end diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index 20a3ba30755..a8486e6a5a1 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -5,11 +5,6 @@ module Notes note.author = current_user note.system = false - if award_emoji_note? - note.is_award = true - note.note = emoji_name - end - if note.save notification_service.new_note(note) @@ -33,24 +28,5 @@ module Notes note.project.execute_hooks(note_data, :note_hooks) note.project.execute_services(note_data, :note_hooks) end - - private - - def award_emoji_note? - # We support award-emojis only in issue discussion - issue_comment? && contains_emoji_only? - end - - def contains_emoji_only? - params[:note] =~ /\A:[-_+[:alnum:]]*:\s?\z/ - end - - def issue_comment? - params[:noteable_type] == 'Issue' - end - - def emoji_name - params[:note].match(/\A:([-_+[:alnum:]]*):\s?/)[1] - end end end -- cgit v1.2.1 From c92b6e66dc3a5bb1b07d1561fa09e8fd051c1956 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 09:36:11 +0100 Subject: Scroll to awards after adding emoji-award comment This makes it more intuitive, as user can see that something actually happened after adding emoji-only comment in long discussions. --- app/assets/javascripts/awards_handler.coffee | 7 ++++++- app/assets/javascripts/notes.js.coffee | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 09b48fe5572..96fd8f8773e 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -88,4 +88,9 @@ class @AwardsHandler callback.call() findEmojiIcon: (emoji) -> - $(".icon[data-emoji='" + emoji + "']") \ No newline at end of file + $(".icon[data-emoji='" + emoji + "']") + + scrollToAwards: -> + $('body, html').animate({ + scrollTop: $('.awards').offset().top - 80 + }, 200) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 7de7632201d..797234e6d9c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -122,6 +122,7 @@ class @Notes if note.award awards_handler.addAwardToEmojiBar(note.note, note.emoji_path) + awards_handler.scrollToAwards() ### Check if note does not exists on page -- cgit v1.2.1 From bfce5d716835f07b98b6d26ccc121d3ac8322aa9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 10:13:29 +0100 Subject: Render json message with errors if note didn't pass validation --- app/controllers/projects/notes_controller.rb | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 5ac18446aa7..a7ff5fcd09a 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -131,16 +131,20 @@ class Projects::NotesController < Projects::ApplicationController end def render_note_json(note) - render json: { - id: note.id, - discussion_id: note.discussion_id, - html: note_to_html(note), - award: note.is_award, - emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "", - note: note.note, - discussion_html: note_to_discussion_html(note), - discussion_with_diff_html: note_to_discussion_with_diff_html(note) - } + if note.valid? + render json: { + id: note.id, + discussion_id: note.discussion_id, + html: note_to_html(note), + award: note.is_award, + emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "", + note: note.note, + discussion_html: note_to_discussion_html(note), + discussion_with_diff_html: note_to_discussion_with_diff_html(note) + } + else + render json: { invalid: true, errors: note.errors } + end end def authorize_admin_note! -- cgit v1.2.1 From a527f5c27ff92d2ee7e2d5e78dc20b6d1d982aa0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 10:51:46 +0100 Subject: Notify user when award-emoji comment is invalid --- app/assets/javascripts/notes.js.coffee | 4 ++++ app/controllers/projects/notes_controller.rb | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 797234e6d9c..af0d62c8495 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -111,6 +111,10 @@ class @Notes Note: for rendering inline notes use renderDiscussionNote ### renderNote: (note) -> + unless note.valid + alert('You have already used this award emoji !') if note.award + return + # render note if it not present in loaded list # or skip if rendered if @isNewNote(note) && !note.award diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index a7ff5fcd09a..88b949a27ab 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -133,6 +133,7 @@ class Projects::NotesController < Projects::ApplicationController def render_note_json(note) if note.valid? render json: { + valid: true, id: note.id, discussion_id: note.discussion_id, html: note_to_html(note), @@ -143,7 +144,11 @@ class Projects::NotesController < Projects::ApplicationController discussion_with_diff_html: note_to_discussion_with_diff_html(note) } else - render json: { invalid: true, errors: note.errors } + render json: { + valid: false, + award: note.is_award, + errors: note.errors + } end end -- cgit v1.2.1 From 4676ba1f7c7b37498d26815c1fbe0e02c2ffaeb8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 14:11:32 +0000 Subject: Add test for award-emoji being added as regular comment --- features/project/issues/award_emoji.feature | 6 +++++- features/steps/project/issues/award_emoji.rb | 31 +++++++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature index a9bc8ffb9bb..2609f129d07 100644 --- a/features/project/issues/award_emoji.feature +++ b/features/project/issues/award_emoji.feature @@ -11,4 +11,8 @@ Feature: Award Emoji And I click to emoji in the picker Then I have award added And I can remove it by clicking to icon - \ No newline at end of file + + @javascript + Scenario: I add award emoji using regular comment + Given I leave comment with a single emoji + Then I have award added diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index 8f7a45dec0e..325eaf2ea6a 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -9,33 +9,40 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps end step 'I click to emoji-picker' do - page.within ".awards-controls" do - page.find(".add-award").click + page.within '.awards-controls' do + page.find('.add-award').click end end step 'I click to emoji in the picker' do - page.within ".awards-menu" do - page.first("img").click + page.within '.awards-menu' do + page.first('img').click end end step 'I can remove it by clicking to icon' do - page.within ".awards" do - page.first(".award").click - expect(page).to_not have_selector ".award" + page.within '.awards' do + page.first('.award').click + expect(page).to_not have_selector '.award' end end step 'I have award added' do - page.within ".awards" do - expect(page).to have_selector ".award" - expect(page.find(".award .counter")).to have_content "1" + page.within '.awards' do + expect(page).to have_selector '.award' + expect(page.find('.award .counter')).to have_content '1' end end step 'project "Shop" has issue "Bugfix"' do - @project = Project.find_by(name: "Shop") - @issue = create(:issue, title: "Bugfix", project: project) + @project = Project.find_by(name: 'Shop') + @issue = create(:issue, title: 'Bugfix', project: project) + end + + step 'I leave comment with a single emoji' do + page.within('.js-main-target-form') do + fill_in 'note[note]', with: ':smile:' + click_button 'Add Comment' + end end end -- cgit v1.2.1 From 6fc1b948560b9e19ac5c1412dd2deb98987aefe8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 2 Dec 2015 18:39:12 +0100 Subject: Show flash message instead of alert when note is invalid --- app/assets/javascripts/notes.js.coffee | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index af0d62c8495..ea190fa8b76 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -112,7 +112,8 @@ class @Notes ### renderNote: (note) -> unless note.valid - alert('You have already used this award emoji !') if note.award + if note.award + new Flash('You have already used this award emoji !', 'alert') return # render note if it not present in loaded list -- cgit v1.2.1 From 70a076c059482e5c26cb723b722bc865142460ea Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 08:51:14 +0100 Subject: Add new features to javascript flash message --- app/assets/javascripts/flash.js.coffee | 19 +++++++++++++------ app/assets/stylesheets/framework/flash.scss | 10 ++++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee index b39ab0c4475..5e34b49c20d 100644 --- a/app/assets/javascripts/flash.js.coffee +++ b/app/assets/javascripts/flash.js.coffee @@ -1,12 +1,19 @@ class @Flash constructor: (message, type)-> - flash = $(".flash-container") - flash.html("") + @flash = $(".flash-container") + @flash.html("") - $('
    ', + innerDiv = $('
    ', class: "flash-#{type}", text: message - ).appendTo(".flash-container") + ) + innerDiv.appendTo(".flash-container") - flash.click -> $(@).fadeOut() - flash.show() + @flash.click -> $(@).fadeOut() + @flash.show() + + pinToTop: -> + @flash.addClass('flash-pinned') + + raise: -> + @flash.addClass('flash-raised') diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss index 82eb50ad4be..1b723021d76 100644 --- a/app/assets/stylesheets/framework/flash.scss +++ b/app/assets/stylesheets/framework/flash.scss @@ -15,3 +15,13 @@ @extend .alert-danger; } } + +.flash-pinned { + position: fixed; + top: 80px; + width: 80%; +} + +.flash-raised { + z-index: 1000; +} -- cgit v1.2.1 From 554f4684622564fe496acb25cacb2daed3b9f3ac Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 08:51:37 +0100 Subject: Pin flash message to top if award note is invalid --- app/assets/javascripts/notes.js.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ea190fa8b76..8eacd05d050 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -113,7 +113,9 @@ class @Notes renderNote: (note) -> unless note.valid if note.award - new Flash('You have already used this award emoji !', 'alert') + flash = new Flash('You have already used this award emoji !', 'alert') + flash.pinToTop() + flash.raise() return # render note if it not present in loaded list -- cgit v1.2.1 From 2b577551177c631d229eca6844520e29478085f8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 09:30:30 +0100 Subject: Add feature test for emoji-only diff notes This specs is related to bug described in #3734 (award-emojis). --- features/project/commits/diff_comments.feature | 6 ++++++ features/steps/shared/diff_note.rb | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/features/project/commits/diff_comments.feature b/features/project/commits/diff_comments.feature index 4a2b870e082..d6e0c84537e 100644 --- a/features/project/commits/diff_comments.feature +++ b/features/project/commits/diff_comments.feature @@ -13,6 +13,12 @@ Feature: Project Commits Diff Comments Given I leave a diff comment like "Typo, please fix" Then I should see a diff comment saying "Typo, please fix" + @javascript + Scenario: I can add a diff comment with a single emoji + Given I open a diff comment form + And I write a diff comment like ":smile:" + Then I should see a diff comment with an emoji image + @javascript Scenario: I get a temporary form for the first comment on a diff line Given I open a diff comment form diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index 72621911a37..dd466cde28d 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -87,6 +87,17 @@ module SharedDiffNote end end + step 'I write a diff comment like ":smile:"' do + page.within(diff_file_selector) do + click_diff_line(sample_commit.line_code) + + page.within("form[rel$='#{sample_commit.line_code}']") do + fill_in 'note[note]', with: ':smile:' + click_button('Add Comment') + end + end + end + step 'I submit the diff comment' do page.within(diff_file_selector) do click_button("Add Comment") @@ -197,6 +208,12 @@ module SharedDiffNote end end + step 'I should see a diff comment with an emoji image' do + page.within("#{diff_file_selector} .note") do + expect(page).to have_xpath("//img[@alt=':smile:']") + end + end + step 'I click side-by-side diff button' do find('#parallel-diff-btn').trigger('click') end -- cgit v1.2.1 From f08f921d537c68ec0bbfb8a1ae7e905cded1bbad Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 12:46:39 +0100 Subject: Support emoji awards also in merge requests --- app/models/note.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 8acb2cf7582..30d15d93fed 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -361,7 +361,7 @@ class Note < ActiveRecord::Base # Method is executed as a before_validation callback. # def set_award! - return unless issue_comment? && contains_emoji_only? + return unless supports_awards? && contains_emoji_only? self.is_award = true self.note = award_emoji_name @@ -369,8 +369,9 @@ class Note < ActiveRecord::Base private - def issue_comment? - noteable.kind_of?(Issue) + def supports_awards? + noteable.kind_of?(Issue) || + noteable.is_a?(MergeRequest) end def contains_emoji_only? -- cgit v1.2.1 From 7af67f619a9ce3dab0a66560bc57ccf606077779 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 12:59:44 +0100 Subject: Combine new javascript Flash methods into one --- app/assets/javascripts/flash.js.coffee | 7 ++----- app/assets/javascripts/notes.js.coffee | 3 +-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee index 5e34b49c20d..9b59d4e57f7 100644 --- a/app/assets/javascripts/flash.js.coffee +++ b/app/assets/javascripts/flash.js.coffee @@ -12,8 +12,5 @@ class @Flash @flash.click -> $(@).fadeOut() @flash.show() - pinToTop: -> - @flash.addClass('flash-pinned') - - raise: -> - @flash.addClass('flash-raised') + pin: -> + @flash.addClass('flash-pinned flash-raised') diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 8eacd05d050..4f559e86378 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -114,8 +114,7 @@ class @Notes unless note.valid if note.award flash = new Flash('You have already used this award emoji !', 'alert') - flash.pinToTop() - flash.raise() + flash.pin() return # render note if it not present in loaded list -- cgit v1.2.1 From 83d8185f5afee7553bf5756ce61b6b855d48c1e5 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 13:03:50 +0100 Subject: Make method `supports_award?` public in `Note` --- app/models/note.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 30d15d93fed..03640be7c93 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -367,13 +367,13 @@ class Note < ActiveRecord::Base self.note = award_emoji_name end - private - def supports_awards? noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest) end + private + def contains_emoji_only? (note =~ /\A:[-_+[:alnum:]]*:\s?\z/) ? true : false end -- cgit v1.2.1 From f905fd239c79f391ce5a7cc2c5c6648b3d6380e4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:00:00 +0100 Subject: Use URL helpers in specs --- lib/gitlab/markdown/label_reference_filter.rb | 4 ++-- spec/fixtures/markdown.md.erb | 20 ++++++++++---------- spec/lib/gitlab/closing_issue_extractor_spec.rb | 12 ++++++++---- .../gitlab/markdown/label_reference_filter_spec.rb | 6 +++--- spec/support/markdown_feature.rb | 4 ++++ 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index a23aa0adeec..ec2b179b7de 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -65,8 +65,8 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers - h.namespace_project_issues_path(project.namespace, project, - label_name: label.name) + h.namespace_project_issues_url( project.namespace, project, label_name: label.name, + only_path: context[:only_path]) end def render_colored_label(label) diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 76e733165ca..e8dfc5c0eb1 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -161,9 +161,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Issue in another project: <%= xissue.to_reference(project) %> - Ignored in code: `<%= issue.to_reference %>` - Ignored in links: [Link to <%= issue.to_reference %>](#issue-link) -- Issue by URL: <%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %> +- Issue by URL: <%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %> - Link to issue by reference: [Issue](<%= issue.to_reference %>) -- Link to issue by URL: [Issue](<%= Gitlab.config.gitlab.url %>/<%= issue.project.to_reference %>/issues/<%= issue.iid %>) +- Link to issue by URL: [Issue](<%= urls.namespace_project_issue_url(issue.project.namespace, issue.project, issue) %>) #### MergeRequestReferenceFilter @@ -171,9 +171,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Merge request in another project: <%= xmerge_request.to_reference(project) %> - Ignored in code: `<%= merge_request.to_reference %>` - Ignored in links: [Link to <%= merge_request.to_reference %>](#merge-request-link) -- Merge request by URL: <%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %> +- Merge request by URL: <%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %> - Link to merge request by reference: [Merge request](<%= merge_request.to_reference %>) -- Link to merge request by URL: [Merge request](<%= Gitlab.config.gitlab.url %>/<%= merge_request.project.to_reference %>/merge_requests/<%= merge_request.iid %>) +- Link to merge request by URL: [Merge request](<%= urls.namespace_project_merge_request_url(merge_request.project.namespace, merge_request.project, merge_request) %>) #### SnippetReferenceFilter @@ -181,9 +181,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Snippet in another project: <%= xsnippet.to_reference(project) %> - Ignored in code: `<%= snippet.to_reference %>` - Ignored in links: [Link to <%= snippet.to_reference %>](#snippet-link) -- Snippet by URL: <%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %> +- Snippet by URL: <%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %> - Link to snippet by reference: [Snippet](<%= snippet.to_reference %>) -- Link to snippet by URL: [Snippet](<%= Gitlab.config.gitlab.url %>/<%= snippet.project.to_reference %>/snippets/<%= snippet.id %>) +- Link to snippet by URL: [Snippet](<%= urls.namespace_project_snippet_url(snippet.project.namespace, snippet.project, snippet) %>) #### CommitRangeReferenceFilter @@ -191,9 +191,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Range in another project: <%= xcommit_range.to_reference(project) %> - Ignored in code: `<%= commit_range.to_reference %>` - Ignored in links: [Link to <%= commit_range.to_reference %>](#commit-range-link) -- Range by URL: <%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %> +- Range by URL: <%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %> - Link to range by reference: [Range](<%= commit_range.to_reference %>) -- Link to range by URL: [Range](<%= Gitlab.config.gitlab.url %>/<%= commit_range.project.to_reference %>/compare/<%= commit_range.id %>) +- Link to range by URL: [Range](<%= urls.namespace_project_compare_url(commit_range.project.namespace, commit_range.project, commit_range.to_param) %>) #### CommitReferenceFilter @@ -201,9 +201,9 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Commit in another project: <%= xcommit.to_reference(project) %> - Ignored in code: `<%= commit.to_reference %>` - Ignored in links: [Link to <%= commit.to_reference %>](#commit-link) -- Commit by URL: <%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %> +- Commit by URL: <%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %> - Link to commit by reference: [Commit](<%= commit.to_reference %>) -- Link to commit by URL: [Commit](<%= Gitlab.config.gitlab.url %>/<%= commit.project.to_reference %>/commit/<%= commit.id %>) +- Link to commit by URL: [Commit](<%= urls.namespace_project_commit_url(commit.project.namespace, commit.project, commit) %>) #### LabelReferenceFilter diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index e9d7e8c1111..fe1b94a484e 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -146,14 +146,14 @@ describe Gitlab::ClosingIssueExtractor do context "with a cross-project URL" do it do - message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid}" + message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)}" expect(subject.closed_by_message(message)).to eq([issue2]) end end context "with an invalid URL" do it do - message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid}" + message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)}" expect(subject.closed_by_message(message)).to eq([]) end end @@ -208,18 +208,22 @@ describe Gitlab::ClosingIssueExtractor do end it "fetches cross-project URL references" do - message = "Closes #{Gitlab.config.gitlab.url}/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)} and #{reference}" expect(subject.closed_by_message(message)). to match_array([issue, issue2]) end it "ignores invalid cross-project URL references" do - message = "Closes https://google.com/#{project2.to_reference}/issues/#{issue2.iid} and #{reference}" + message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)} and #{reference}" expect(subject.closed_by_message(message)). to match_array([issue]) end end end + + def urls + Gitlab::Application.routes.url_helpers + end end diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb index f5a6d67f772..ef6dd524aba 100644 --- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb +++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb @@ -71,7 +71,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) end it 'links with adjacent text' do @@ -94,7 +94,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm' end @@ -118,7 +118,7 @@ module Gitlab::Markdown doc = reference_filter("See #{reference}") expect(doc.css('a').first.attr('href')).to eq urls. - namespace_project_issues_path(project.namespace, project, label_name: label.name) + namespace_project_issues_url(project.namespace, project, label_name: label.name) expect(doc.text).to eq 'See gfm references' end diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb index bedc1a7f1db..d6d3062a197 100644 --- a/spec/support/markdown_feature.rb +++ b/spec/support/markdown_feature.rb @@ -93,6 +93,10 @@ class MarkdownFeature end end + def urls + Gitlab::Application.routes.url_helpers + end + def raw_markdown markdown = File.read(Rails.root.join('spec/fixtures/markdown.md.erb')) ERB.new(markdown).result(binding) -- cgit v1.2.1 From 0de8e260662a62603dbee5efb133cd4f1e0f0ed7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:00:41 +0100 Subject: Pass original text along with label reference filter. --- lib/gitlab/markdown/label_reference_filter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index ec2b179b7de..36cf7a8f4ce 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -51,7 +51,11 @@ module Gitlab if label = project.labels.find_by(params) url = url_for_label(project, label) klass = reference_class(:label) - data = data_attribute(project: project.id, label: label.id) + data = data_attribute( + original: link_text || match, + project: project.id, + label: label.id + ) text = link_text || render_colored_label(label) -- cgit v1.2.1 From 9ce8c867eeb5da53eb76bc01ef7eaef5c798243c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 3 Dec 2015 15:05:43 +0200 Subject: Fix mailer queue --- Procfile | 2 +- bin/background_jobs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Procfile b/Procfile index fd5f7ecb94b..eaf79ccc2ea 100644 --- a/Procfile +++ b/Procfile @@ -1,3 +1,3 @@ web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default +worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/bin/background_jobs b/bin/background_jobs index d4578f6a222..5c85fb339e6 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -37,7 +37,7 @@ start_no_deamonize() start_sidekiq() { - bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 + bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 } load_ok() -- cgit v1.2.1 From 90899b3208035f5c74feb439da5fbe689dd1d2d7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 14:54:14 +0100 Subject: Fix specs --- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/labels.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 024dc5e72d3..de1dbfdfa93 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -46,7 +46,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps end step 'I submit the merge request' do - click_button "Submit new merge request" + click_button "Submit merge request" end step 'I follow the target commit link' do diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index 047cf701bb0..f749ea2eca8 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -31,19 +31,19 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label \'bug\'' do fill_in 'Title', with: 'bug' - fill_in 'Background Color', with: '#F95610' + fill_in 'Background color', with: '#F95610' click_button 'Save' end step 'I submit new label with invalid color' do fill_in 'Title', with: 'support' - fill_in 'Background Color', with: '#12' + fill_in 'Background color', with: '#12' click_button 'Save' end @@ -85,7 +85,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' - fill_in 'Background Color', with: '#F15610' + fill_in 'Background color', with: '#F15610' click_button 'Save' end -- cgit v1.2.1 From 0a081e7eff9730beebd4bea1eb40873d907b6293 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 3 Dec 2015 14:59:10 +0100 Subject: If a user clicks on the LFS object, it should be served if the user has access to the object. --- app/controllers/projects/blob_controller.rb | 17 +++++++++++++++++ app/models/lfs_object.rb | 10 ++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 31a33bfd237..d0108c823a9 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -17,6 +17,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :require_branch_head, only: [:edit, :update] before_action :editor_variables, except: [:show, :preview, :diff] before_action :after_edit_path, only: [:edit, :update] + before_action :show_lfs_object, only: :show def new commit unless @repository.empty? @@ -193,4 +194,20 @@ class Projects::BlobController < Projects::ApplicationController file_content_encoding: params[:encoding] } end + + def show_lfs_object + return unless @blob && @blob.text? && @blob.data.present? + + if @blob.data.starts_with?("version https://git-lfs.github.com/spec") + oid = @blob.data.match(/#{LfsObject::MATCH_FROM_POINTER_REGEX}/) + if oid && oid[1] + lfs_object = LfsObject.find_by_oid(oid[1]) + return nil unless lfs_object && lfs_object.file.exists? + + if lfs_object.projects.exists?(lfs_object.storage_project(@project).id) + send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment' + end + end + end + end end diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 3c1426f59d0..f087d07b5b2 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -5,4 +5,14 @@ class LfsObject < ActiveRecord::Base validates :oid, presence: true, uniqueness: true mount_uploader :file, LfsObjectUploader + + MATCH_FROM_POINTER_REGEX = "(?<=sha256:)([0-9a-f]{64})" + + def storage_project(project) + if project && project.forked? + project.forked_from_project + else + project + end + end end -- cgit v1.2.1 From 5145706c82613d64462fe736850d09799224cd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 25 Nov 2015 19:20:40 -0500 Subject: Run custom Git hooks when creating or deleting branches through the UI. #1156 --- CHANGELOG | 1 + app/models/repository.rb | 54 +++++++++-------- app/services/create_branch_service.rb | 5 +- app/services/delete_branch_service.rb | 4 +- app/services/files/base_service.rb | 2 +- app/services/git_hooks_service.rb | 32 ++++++++++ spec/models/repository_spec.rb | 100 ++++++++++++++++++++++++++++++++ spec/services/git_hooks_service_spec.rb | 38 ++++++++++++ 8 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 app/services/git_hooks_service.rb create mode 100644 spec/services/git_hooks_service_spec.rb diff --git a/CHANGELOG b/CHANGELOG index db812796b69..882648b36a2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Run custom Git hooks when branch is created or deleted. #1156 v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index d247b0f5012..c304955b0b3 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1,7 +1,6 @@ require 'securerandom' class Repository - class PreReceiveError < StandardError; end class CommitError < StandardError; end include Gitlab::ShellAdapter @@ -108,10 +107,19 @@ class Repository tags.find { |tag| tag.name == name } end - def add_branch(branch_name, ref) - expire_branches_cache + def add_branch(user, branch_name, target) + oldrev = Gitlab::Git::BLANK_SHA + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name + target = commit(target).try(:id) + + return false unless target + + GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do + rugged.branches.create(branch_name, target) + end - gitlab_shell.add_branch(path_with_namespace, branch_name, ref) + expire_branches_cache + find_branch(branch_name) end def add_tag(tag_name, ref, message = nil) @@ -120,10 +128,20 @@ class Repository gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end - def rm_branch(branch_name) + def rm_branch(user, branch_name) expire_branches_cache - gitlab_shell.rm_branch(path_with_namespace, branch_name) + branch = find_branch(branch_name) + oldrev = branch.try(:target) + newrev = Gitlab::Git::BLANK_SHA + ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name + + GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do + rugged.branches.delete(branch_name) + end + + expire_branches_cache + true end def rm_tag(tag_name) @@ -550,7 +568,6 @@ class Repository def commit_with_hooks(current_user, branch) oldrev = Gitlab::Git::BLANK_SHA ref = Gitlab::Git::BRANCH_REF_PREFIX + branch - gl_id = Gitlab::ShellEnv.gl_id(current_user) was_empty = empty? # Create temporary ref @@ -569,15 +586,7 @@ class Repository raise CommitError.new('Failed to create commit') end - # Run GitLab pre-receive hook - pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo) - pre_receive_hook_status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref) - - # Run GitLab update hook - update_hook = Gitlab::Git::Hook.new('update', path_to_repo) - update_hook_status = update_hook.trigger(gl_id, oldrev, newrev, ref) - - if pre_receive_hook_status && update_hook_status + GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do if was_empty # Create branch rugged.references.create(ref, newrev) @@ -592,16 +601,11 @@ class Repository raise CommitError.new('Commit was rejected because branch received new push') end end - - # Run GitLab post receive hook - post_receive_hook = Gitlab::Git::Hook.new('post-receive', path_to_repo) - post_receive_hook.trigger(gl_id, oldrev, newrev, ref) - else - # Remove tmp ref and return error to user - rugged.references.delete(tmp_ref) - - raise PreReceiveError.new('Commit was rejected by git hook') end + rescue GitHooksService::PreReceiveError + # Remove tmp ref and return error to user + rugged.references.delete(tmp_ref) + raise end private diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index cf7ae4345f3..de18f3bc556 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -13,8 +13,7 @@ class CreateBranchService < BaseService return error('Branch already exists') end - repository.add_branch(branch_name, ref) - new_branch = repository.find_branch(branch_name) + new_branch = repository.add_branch(current_user, branch_name, ref) if new_branch push_data = build_push_data(project, current_user, new_branch) @@ -27,6 +26,8 @@ class CreateBranchService < BaseService else error('Invalid reference name') end + rescue GitHooksService::PreReceiveError + error('Branch creation was rejected by Git hook') end def success(branch) diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index b19b112a0c4..22bf9dd935e 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -24,7 +24,7 @@ class DeleteBranchService < BaseService return error('You dont have push access to repo', 405) end - if repository.rm_branch(branch_name) + if repository.rm_branch(current_user, branch_name) push_data = build_push_data(branch) EventCreateService.new.push(project, current_user, push_data) @@ -35,6 +35,8 @@ class DeleteBranchService < BaseService else error('Failed to remove branch') end + rescue GitHooksService::PreReceiveError + error('Branch deletion was rejected by Git hook') end def error(message, return_code = 400) diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 008833eed80..f50aaf2eb52 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -26,7 +26,7 @@ module Files else error("Something went wrong. Your changes were not committed") end - rescue Repository::CommitError, Repository::PreReceiveError, ValidationError => ex + rescue Repository::CommitError, GitHooksService::PreReceiveError, ValidationError => ex error(ex.message) end diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb new file mode 100644 index 00000000000..53f1fdef796 --- /dev/null +++ b/app/services/git_hooks_service.rb @@ -0,0 +1,32 @@ +class GitHooksService + PreReceiveError = Class.new(StandardError) + + def execute(user, repo_path, oldrev, newrev, ref) + @repo_path = repo_path + @user = Gitlab::ShellEnv.gl_id(user) + @oldrev = oldrev + @newrev = newrev + @ref = ref + + pre_status = run_hook('pre-receive') + + if pre_status + yield + + run_hook('post-receive') + end + end + + private + + def run_hook(name) + hook = Gitlab::Git::Hook.new(name, @repo_path) + status = hook.trigger(@user, @oldrev, @newrev, @ref) + + if !status && (name != 'post-receive') + raise PreReceiveError.new("Git operation was rejected by #{name} hook") + end + + status + end +end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 319fa0a7c8d..c746b8db621 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -4,6 +4,7 @@ describe Repository do include RepoHelpers let(:repository) { create(:project).repository } + let(:user) { create(:user) } describe :branch_names_contains do subject { repository.branch_names_contains(sample_commit.id) } @@ -99,5 +100,104 @@ describe Repository do it { expect(subject.startline).to eq(186) } it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") } end + end + + describe :add_branch do + context 'when pre hooks were successful' do + it 'should run without errors' do + hook = double(trigger: true) + expect(Gitlab::Git::Hook).to receive(:new).twice.and_return(hook) + + expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error + end + + it 'should create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + branch = repository.add_branch(user, 'new_feature', 'master') + + expect(branch.name).to eq('new_feature') + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.add_branch(user, 'new_feature', 'master') + end.to raise_error(GitHooksService::PreReceiveError) + end + + it 'should not create the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.add_branch(user, 'new_feature', 'master') + end.to raise_error(GitHooksService::PreReceiveError) + expect(repository.find_branch('new_feature')).to be_nil + end + end + end + + describe :rm_branch do + context 'when pre hooks were successful' do + it 'should run without errors' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + expect { repository.rm_branch(user, 'feature') }.not_to raise_error + end + + it 'should delete the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(true) + + expect { repository.rm_branch(user, 'feature') }.not_to raise_error + + expect(repository.find_branch('feature')).to be_nil + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.rm_branch(user, 'new_feature') + end.to raise_error(GitHooksService::PreReceiveError) + end + + it 'should not delete the branch' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.rm_branch(user, 'feature') + end.to raise_error(GitHooksService::PreReceiveError) + expect(repository.find_branch('feature')).not_to be_nil + end + end + end + + describe :commit_with_hooks do + context 'when pre hooks were successful' do + it 'should run without errors' do + expect_any_instance_of(GitHooksService).to receive(:execute).and_return(true) + + expect do + repository.commit_with_hooks(user, 'feature') { sample_commit.id } + end.not_to raise_error + end + end + + context 'when pre hooks failed' do + it 'should get an error' do + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return(false) + + expect do + repository.commit_with_hooks(user, 'feature') { sample_commit.id } + end.to raise_error(GitHooksService::PreReceiveError) + end + end + end + end diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb new file mode 100644 index 00000000000..21585cc4629 --- /dev/null +++ b/spec/services/git_hooks_service_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe GitHooksService do + include RepoHelpers + + let(:user) { create :user } + let(:project) { create :project } + let(:service) { GitHooksService.new } + + before do + @blankrev = Gitlab::Git::BLANK_SHA + @oldrev = sample_commit.parent_id + @newrev = sample_commit.id + @ref = 'refs/heads/feature' + @repo_path = project.repository.path_to_repo + end + + describe '#execute' do + + context 'when pre hooks were successful' do + it 'should call post hooks' do + expect(service).to receive(:run_hook).with('pre-receive').and_return(true) + expect(service).to receive(:run_hook).with('post-receive').and_return(true) + expect(service.execute(user, @repo_path, @blankrev, @newrev, @ref) { }).to eq(true) + end + end + + context 'when pre hooks failed' do + it 'should not call post hooks' do + expect(service).to receive(:run_hook).with('pre-receive').and_return(false) + expect(service).not_to receive(:run_hook).with('post-receive') + + service.execute(user, @repo_path, @blankrev, @newrev, @ref) + end + end + + end +end -- cgit v1.2.1 From 338eb2c41ea766779d6bb7798079a1dd3a50e11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 1 Dec 2015 00:22:45 -0500 Subject: Call update hook from new GitHooksService class. #3069 --- app/services/git_hooks_service.rb | 4 +--- spec/models/repository_spec.rb | 2 +- spec/services/git_hooks_service_spec.rb | 23 +++++++++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb index 53f1fdef796..b7804ed472f 100644 --- a/app/services/git_hooks_service.rb +++ b/app/services/git_hooks_service.rb @@ -8,9 +8,7 @@ class GitHooksService @newrev = newrev @ref = ref - pre_status = run_hook('pre-receive') - - if pre_status + if run_hook('pre-receive') && run_hook('update') yield run_hook('post-receive') diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c746b8db621..fa261e64c35 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -107,7 +107,7 @@ describe Repository do context 'when pre hooks were successful' do it 'should run without errors' do hook = double(trigger: true) - expect(Gitlab::Git::Hook).to receive(:new).twice.and_return(hook) + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) expect { repository.add_branch(user, 'new_feature', 'master') }.not_to raise_error end diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index 21585cc4629..bb639a5ae23 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -17,16 +17,17 @@ describe GitHooksService do describe '#execute' do - context 'when pre hooks were successful' do - it 'should call post hooks' do - expect(service).to receive(:run_hook).with('pre-receive').and_return(true) - expect(service).to receive(:run_hook).with('post-receive').and_return(true) + context 'when receive hooks were successful' do + it 'should call post-receive hook' do + hook = double(trigger: true) + expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook) + expect(service.execute(user, @repo_path, @blankrev, @newrev, @ref) { }).to eq(true) end end - context 'when pre hooks failed' do - it 'should not call post hooks' do + context 'when pre-receive hook failed' do + it 'should not call post-receive hook' do expect(service).to receive(:run_hook).with('pre-receive').and_return(false) expect(service).not_to receive(:run_hook).with('post-receive') @@ -34,5 +35,15 @@ describe GitHooksService do end end + context 'when update hook failed' do + it 'should not call post-receive hook' do + expect(service).to receive(:run_hook).with('pre-receive').and_return(true) + expect(service).to receive(:run_hook).with('update').and_return(false) + expect(service).not_to receive(:run_hook).with('post-receive') + + service.execute(user, @repo_path, @blankrev, @newrev, @ref) + end + end + end end -- cgit v1.2.1 From 5e6a5270d52ea2f13ca9967913012691e257439b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 1 Dec 2015 12:31:44 -0500 Subject: Raise the exception from #execute instead of #run_hook. #1156 #3069 --- app/services/git_hooks_service.rb | 20 +++++++++----------- spec/services/git_hooks_service_spec.rb | 8 ++++++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb index b7804ed472f..8f5c3393dfc 100644 --- a/app/services/git_hooks_service.rb +++ b/app/services/git_hooks_service.rb @@ -8,23 +8,21 @@ class GitHooksService @newrev = newrev @ref = ref - if run_hook('pre-receive') && run_hook('update') - yield - - run_hook('post-receive') + %w(pre-receive update).each do |hook_name| + unless run_hook(hook_name) + raise PreReceiveError.new("Git operation was rejected by #{hook_name} hook") + end end + + yield + + run_hook('post-receive') end private def run_hook(name) hook = Gitlab::Git::Hook.new(name, @repo_path) - status = hook.trigger(@user, @oldrev, @newrev, @ref) - - if !status && (name != 'post-receive') - raise PreReceiveError.new("Git operation was rejected by #{name} hook") - end - - status + hook.trigger(@user, @oldrev, @newrev, @ref) end end diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index bb639a5ae23..7e018d3c9fe 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -31,7 +31,9 @@ describe GitHooksService do expect(service).to receive(:run_hook).with('pre-receive').and_return(false) expect(service).not_to receive(:run_hook).with('post-receive') - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + expect do + service.execute(user, @repo_path, @blankrev, @newrev, @ref) + end.to raise_error(GitHooksService::PreReceiveError) end end @@ -41,7 +43,9 @@ describe GitHooksService do expect(service).to receive(:run_hook).with('update').and_return(false) expect(service).not_to receive(:run_hook).with('post-receive') - service.execute(user, @repo_path, @blankrev, @newrev, @ref) + expect do + service.execute(user, @repo_path, @blankrev, @newrev, @ref) + end.to raise_error(GitHooksService::PreReceiveError) end end -- cgit v1.2.1 From dbbd2b863b402e460ac1dc90f852fcae617a2351 Mon Sep 17 00:00:00 2001 From: Greg Smethells Date: Mon, 30 Nov 2015 14:47:44 -0600 Subject: sort milestones by due_date --- CHANGELOG | 1 + app/controllers/concerns/global_milestones.rb | 2 ++ app/finders/milestones_finder.rb | 2 +- app/helpers/milestones_helper.rb | 2 ++ app/models/global_milestone.rb | 31 +++++++++++++++++++++- .../dashboard/milestones/_milestone.html.haml | 11 +++++--- app/views/projects/milestones/_milestone.html.haml | 6 +---- app/views/shared/_milestone_expired.html.haml | 5 ++++ 8 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 app/views/shared/_milestone_expired.html.haml diff --git a/CHANGELOG b/CHANGELOG index db812796b69..aad58af6678 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Fix: sort milestones by due date once again (Greg Smethells) v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/controllers/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb index b428249acd3..3e4c0e63601 100644 --- a/app/controllers/concerns/global_milestones.rb +++ b/app/controllers/concerns/global_milestones.rb @@ -2,8 +2,10 @@ module GlobalMilestones extend ActiveSupport::Concern def milestones + epoch = DateTime.parse('1970-01-01') @milestones = MilestonesFinder.new.execute(@projects, params) @milestones = GlobalMilestone.build_collection(@milestones) + @milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE) end diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb index b704e878903..630c73c2a94 100644 --- a/app/finders/milestones_finder.rb +++ b/app/finders/milestones_finder.rb @@ -1,7 +1,7 @@ class MilestonesFinder def execute(projects, params) milestones = Milestone.of_projects(projects) - milestones = milestones.order("due_date ASC") + milestones = milestones.reorder("due_date ASC") case params[:state] when 'closed' then milestones.closed diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb index ad43892b639..a42cbcff182 100644 --- a/app/helpers/milestones_helper.rb +++ b/app/helpers/milestones_helper.rb @@ -28,7 +28,9 @@ module MilestonesHelper Milestone.where(project_id: @projects) end.active + epoch = DateTime.parse('1970-01-01') grouped_milestones = GlobalMilestone.build_collection(milestones) + grouped_milestones = grouped_milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } grouped_milestones.unshift(Milestone::None) grouped_milestones.unshift(Milestone::Any) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 1321ccd963f..33ddb265fba 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -19,6 +19,14 @@ class GlobalMilestone @title.parameterize end + def expired? + if due_date + due_date.past? + else + false + end + end + def projects milestones.map { |milestone| milestone.project } end @@ -98,4 +106,25 @@ class GlobalMilestone def complete? total_items_count == closed_items_count end -end + + def due_date + return @due_date if defined?(@due_date) + + @due_date = + if @milestones.all? { |x| x.due_date == @milestones.first.due_date } + @milestones.first.due_date + else + nil + end + end + + def expires_at + if due_date + if due_date.past? + "expired at #{due_date.stamp("Aug 21, 2011")}" + else + "expires at #{due_date.stamp("Aug 21, 2011")}" + end + end + end +end \ No newline at end of file diff --git a/app/views/dashboard/milestones/_milestone.html.haml b/app/views/dashboard/milestones/_milestone.html.haml index 55080d6b3fe..7c882a32702 100644 --- a/app/views/dashboard/milestones/_milestone.html.haml +++ b/app/views/dashboard/milestones/_milestone.html.haml @@ -16,7 +16,10 @@ = milestone_progress_bar(milestone) .row .col-sm-6 - - milestone.milestones.each do |milestone| - = link_to milestone_path(milestone) do - %span.label.label-gray - = milestone.project.name_with_namespace + .expiration + = render 'shared/milestone_expired', milestone: milestone + .projects + - milestone.milestones.each do |milestone| + = link_to milestone_path(milestone) do + %span.label.label-gray + = milestone.project.name_with_namespace diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 334172b976f..d6a44c9f0a1 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -18,11 +18,7 @@ .row .col-sm-6 - - if milestone.expired? and not milestone.closed? - %span.cred (Expired) - - if milestone.expires_at - %span - = milestone.expires_at + = render 'shared/milestone_expired', milestone: milestone .col-sm-6 - if can?(current_user, :admin_milestone, milestone.project) and milestone.active? = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs edit-milestone-link btn-grouped" do diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml new file mode 100644 index 00000000000..b8eef15fbec --- /dev/null +++ b/app/views/shared/_milestone_expired.html.haml @@ -0,0 +1,5 @@ +- if milestone.expired? and not milestone.closed? + %span.cred (Expired) +- if milestone.expires_at + %span + = milestone.expires_at -- cgit v1.2.1 From 86ed2e43d58ef074ae3b1d80e0b18fe338ca9afd Mon Sep 17 00:00:00 2001 From: Phillip Berndt Date: Thu, 3 Dec 2015 16:04:39 +0100 Subject: time_ago_with_tooltip javascript snippet: Don't reinitialize older elements see bug #3758 --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3230ff1b004..21f962df206 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -209,7 +209,7 @@ module ApplicationHelper title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), data: { toggle: 'tooltip', placement: placement, container: 'body' } - element += javascript_tag "$('.js-timeago').timeago()" unless skip_js + element += javascript_tag "$('.js-timeago').last().timeago()" unless skip_js element end -- cgit v1.2.1 From 11b54edf36c8d44f02004de166cf8b809df721ad Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 3 Dec 2015 09:36:45 -0600 Subject: Filter current user to the top of issue assignee list --- app/views/shared/issuable/_context.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index f1646b4ce64..ced16f820cb 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -9,7 +9,7 @@ none .issuable-context-selectbox - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true) + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) %div.prepend-top-default.clearfix .issuable-context-title -- cgit v1.2.1 From ea52a81da4888af232e9868d722cc91d5e442723 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Thu, 3 Dec 2015 17:08:09 +0100 Subject: Move the file serving to Raw controller, add a few ifs to view. --- app/controllers/projects/blob_controller.rb | 17 ---------------- app/controllers/projects/raw_controller.rb | 31 ++++++++++++++++++++++------- app/views/projects/blob/_actions.html.haml | 6 +++--- app/views/projects/blob/_blob.html.haml | 6 ++++-- app/views/projects/blob/_download.html.haml | 6 +++++- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index d0108c823a9..31a33bfd237 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -17,7 +17,6 @@ class Projects::BlobController < Projects::ApplicationController before_action :require_branch_head, only: [:edit, :update] before_action :editor_variables, except: [:show, :preview, :diff] before_action :after_edit_path, only: [:edit, :update] - before_action :show_lfs_object, only: :show def new commit unless @repository.empty? @@ -194,20 +193,4 @@ class Projects::BlobController < Projects::ApplicationController file_content_encoding: params[:encoding] } end - - def show_lfs_object - return unless @blob && @blob.text? && @blob.data.present? - - if @blob.data.starts_with?("version https://git-lfs.github.com/spec") - oid = @blob.data.match(/#{LfsObject::MATCH_FROM_POINTER_REGEX}/) - if oid && oid[1] - lfs_object = LfsObject.find_by_oid(oid[1]) - return nil unless lfs_object && lfs_object.file.exists? - - if lfs_object.projects.exists?(lfs_object.storage_project(@project).id) - send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment' - end - end - end - end end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index d5ee6ac8663..c56f432a1f1 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -10,15 +10,13 @@ class Projects::RawController < Projects::ApplicationController @blob = @repository.blob_at(@commit.id, @path) if @blob - type = get_blob_type - headers['X-Content-Type-Options'] = 'nosniff' - send_data( - @blob.data, - type: type, - disposition: 'inline' - ) + if @blob.lfs_pointer? + send_lfs_object + else + stream_data + end else render_404 end @@ -35,4 +33,23 @@ class Projects::RawController < Projects::ApplicationController 'application/octet-stream' end end + + def stream_data + type = get_blob_type + + send_data( + @blob.data, + type: type, + disposition: 'inline' + ) + end + + def send_lfs_object + lfs_object = LfsObject.find_by_oid(@blob.lfs_oid) + return nil unless lfs_object && lfs_object.file.exists? + + if lfs_object.projects.exists?(lfs_object.storage_project(@project).id) + send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment' + end + end end diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index ba3e0c3c590..15cd8f056f5 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,9 +1,9 @@ .btn-group.tree-btn-group - = edit_blob_link(@project, @ref, @path) + = edit_blob_link(@project, @ref, @path) unless @blob.lfs_pointer? = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-sm', target: '_blank' -# only show normal/blame view links for text files - - if @blob.text? + - if @blob.text? && !@blob.lfs_pointer? - if current_page? namespace_project_blame_path(@project.namespace, @project, @id) = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id), class: 'btn btn-sm' @@ -16,7 +16,7 @@ = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.sha, @path)), class: 'btn btn-sm' -- if allowed_tree_edit? +- if allowed_tree_edit? && !@blob.lfs_pointer? .btn-group{ role: "group" } %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 42f632b38ef..bb9e1c63413 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -29,10 +29,12 @@ %strong = blob.name %small - = number_to_human_size(blob.size) + = number_to_human_size(blob.size) unless blob.lfs_pointer? .file-actions.hidden-xs = render "actions" - - if blob.text? + - if blob.lfs_pointer? + = render "download", blob: blob + - elsif blob.text? = render "text", blob: blob - elsif blob.image? = render "image", blob: blob diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml index f2c5e95ecf4..39ec6f693e2 100644 --- a/app/views/projects/blob/_download.html.haml +++ b/app/views/projects/blob/_download.html.haml @@ -4,4 +4,8 @@ %h1.light %i.fa.fa-download %h4 - Download (#{number_to_human_size blob.size}) + - if blob.lfs_pointer? + - size = blob.lfs_size + - else + - size = blob.size + Download (#{number_to_human_size size}) -- cgit v1.2.1 From c204aca83b0c8308080a9f53b692f509a80ddffc Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 3 Dec 2015 15:30:10 -0200 Subject: Improve style of the warning when mentioning many people in a comment --- app/assets/stylesheets/framework/markdown_area.scss | 8 +++----- app/views/projects/_md_preview.html.haml | 15 ++++++++------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index cc660529cb4..11b609f3b79 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -73,11 +73,9 @@ } .referenced-users { - padding: 10px 0; - color: #999; - margin-left: 10px; - margin-top: 1px; - margin-right: 130px; + color: #4c4e54; + display: inline-block; + padding-top: 10px; } .md-preview-holder { diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index c4d3c9cc30a..5bdceb9523a 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -15,10 +15,11 @@ .js-md-preview{class: (preview_class if defined?(preview_class))} - if defined?(referenced_users) && referenced_users - %span.referenced-users.pull-left.hide - = icon('exclamation-triangle') - You are about to add - %strong - %span.js-referenced-users-count 0 - people - to the discussion. Proceed with caution. + %div.clearfix + %span.referenced-users.hide + = icon('exclamation-triangle') + You are about to add + %strong + %span.js-referenced-users-count 0 + people + to the discussion. Proceed with caution. -- cgit v1.2.1 From 0cbb7717df8d243d7812041099d99f731de7f82b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:32:29 +0100 Subject: Fix spec --- features/steps/project/issues/labels.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index f749ea2eca8..e273bb391b3 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -32,19 +32,19 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I submit new label \'support\'' do fill_in 'Title', with: 'support' fill_in 'Background color', with: '#F95610' - click_button 'Save' + click_button 'Create Label' end step 'I submit new label \'bug\'' do fill_in 'Title', with: 'bug' fill_in 'Background color', with: '#F95610' - click_button 'Save' + click_button 'Create Label' end step 'I submit new label with invalid color' do fill_in 'Title', with: 'support' fill_in 'Background color', with: '#12' - click_button 'Save' + click_button 'Create Label' end step 'I should see label label exist error message' do @@ -86,7 +86,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I change label \'bug\' to \'fix\'' do fill_in 'Title', with: 'fix' fill_in 'Background color', with: '#F15610' - click_button 'Save' + click_button 'Save changes' end step 'I should see label \'fix\'' do -- cgit v1.2.1 From 5ab1b11e901712ab462b10f030971610ba140257 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:42:37 +0100 Subject: Fix specs --- features/steps/project/source/markdown_render.rb | 4 ++-- features/steps/project/wiki.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb index ec88c8c20c8..3a4f7a6e01c 100644 --- a/features/steps/project/source/markdown_render.rb +++ b/features/steps/project/source/markdown_render.rb @@ -252,7 +252,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see Gitlab API document' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "api") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page api" end step 'I click on Rake tasks link' do @@ -261,7 +261,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps step 'I see Rake tasks directory' do expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "raketasks") - expect(page).to have_content "Editing" + expect(page).to have_content "Edit Page raketasks" end step 'I go directory which contains README file' do diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb index 935c1ca8a2e..91d227fadbf 100644 --- a/features/steps/project/wiki.rb +++ b/features/steps/project/wiki.rb @@ -156,7 +156,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps end step 'I should see the Editing page' do - expect(page).to have_content('Editing') + expect(page).to have_content('Edit Page') end step 'I view the page history of a Wiki page that has a path' do -- cgit v1.2.1 From 1dd7c978862b900b92bde355f99ce5e869a98328 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:51:44 +0100 Subject: Fix background and padding of login and error pages --- app/assets/stylesheets/framework/layout.scss | 8 ++++++-- app/assets/stylesheets/pages/login.scss | 3 +-- app/views/layouts/devise.html.haml | 4 ++-- app/views/layouts/errors.html.haml | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index a60940a8bee..aa5acb93cc5 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -6,6 +6,10 @@ html { body { background-color: #EAEBEC !important; + + &.navless { + background-color: white !important; + } } .container { @@ -18,8 +22,8 @@ body { } .navless-container { - padding-top: $header-height; - margin-top: 30px; + margin-top: $header-height; + padding-top: $gl-padding * 2; } .container-limited { diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index edd51705136..f9c6f1b39f9 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -1,7 +1,5 @@ /* Login Page */ .login-page { - background-color: white; - .container { max-width: 960px; } @@ -21,6 +19,7 @@ h1:first-child { font-weight: normal; margin-bottom: 30px; + margin-top: 0; } img { diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 95e077c339f..f08cb0a5428 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -1,13 +1,13 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body.ui_charcoal.login-page.application + %body.ui_charcoal.login-page.application.navless = render "layouts/header/empty" = render "layouts/broadcast" .container.navless-container .content = render "layouts/flash" - .row.prepend-top-20 + .row .col-sm-5.pull-right = yield .col-sm-7.brand-holder.pull-left diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index 2af265a2296..915acc4612e 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head" - %body{class: "#{user_application_theme} application"} + %body{class: "#{user_application_theme} application navless"} = render "layouts/header/empty" .container.navless-container = render "layouts/flash" -- cgit v1.2.1 From a89d6d1428d61bd2ae6f530acfc5a34d5a9c46e8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:53:17 +0100 Subject: Add authorization to new branch/tag pages. --- app/controllers/projects/branches_controller.rb | 2 +- app/controllers/projects/tags_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3ac0a75fa70..3c2849a7601 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -3,7 +3,7 @@ class Projects::BranchesController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_download_code! - before_action :authorize_push_code!, only: [:create, :destroy] + before_action :authorize_push_code!, only: [:new, :create, :destroy] def index @sort = params[:sort] || 'name' diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index cb39c2b8782..280fe12cc7c 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -2,7 +2,7 @@ class Projects::TagsController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_download_code! - before_action :authorize_push_code!, only: [:create] + before_action :authorize_push_code!, only: [:new, :create] before_action :authorize_admin_project!, only: [:destroy] def index -- cgit v1.2.1 From 494ebad39b465ca6a70888ca376db0dd962a618f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:56:43 +0100 Subject: Fix spec --- features/steps/project/issues/issues.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..9683bebd563 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -65,7 +65,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps step 'I see current user as the first user' do expect(page).to have_selector('.user-result', visible: true, count: 4) users = page.all('.user-name') - expect(users[0].text).to eq 'Any' + expect(users[0].text).to eq 'Any Assignee' expect(users[1].text).to eq 'Unassigned' expect(users[2].text).to eq current_user.name end -- cgit v1.2.1 From e41a0c60f448057f69c2952f15d88f9d8191c2fe Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 18:59:03 +0100 Subject: Add missing milestone ID --- app/models/milestone.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index c2642b75b8a..58acc5d6ccc 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -16,9 +16,9 @@ class Milestone < ActiveRecord::Base # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. - MilestoneStruct = Struct.new(:title, :name) - None = MilestoneStruct.new('No Milestone', 'No Milestone') - Any = MilestoneStruct.new('Any', '') + MilestoneStruct = Struct.new(:title, :name, :id) + None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) + Any = MilestoneStruct.new('Any', '', -1) include InternalId include Sortable -- cgit v1.2.1 From f9d954fae71d40a314a5812c1a7eec5a601d1575 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 19:02:56 +0100 Subject: Satisfy rubocop --- lib/gitlab/markdown/label_reference_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb index 36cf7a8f4ce..a2026eecaeb 100644 --- a/lib/gitlab/markdown/label_reference_filter.rb +++ b/lib/gitlab/markdown/label_reference_filter.rb @@ -70,7 +70,7 @@ module Gitlab def url_for_label(project, label) h = Gitlab::Application.routes.url_helpers h.namespace_project_issues_url( project.namespace, project, label_name: label.name, - only_path: context[:only_path]) + only_path: context[:only_path]) end def render_colored_label(label) -- cgit v1.2.1 From 82e916b0f2cbd500d14f545095ac82464c0a3889 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 1 Dec 2015 18:11:12 -0200 Subject: Touch project when toggling stars to update cache --- app/models/users_star_project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb index 3d49cb05949..413f3f485a8 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -10,7 +10,7 @@ # class UsersStarProject < ActiveRecord::Base - belongs_to :project, counter_cache: :star_count + belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user validates :user, presence: true -- cgit v1.2.1 From e5c865eebf95d58ed33ef3d86f7582aa2db9bb09 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 3 Dec 2015 20:01:35 +0100 Subject: Fix specs --- app/assets/stylesheets/pages/issuable.scss | 11 +++++++++++ app/assets/stylesheets/pages/issues.scss | 11 ----------- app/assets/stylesheets/pages/merge_requests.scss | 11 ----------- app/views/shared/issuable/_context.html.haml | 2 +- features/steps/project/forked_merge_requests.rb | 2 +- features/steps/project/issues/issues.rb | 2 +- 6 files changed, 14 insertions(+), 25 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 51d8e5b4657..b5f449ff695 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -90,6 +90,17 @@ } } +.issuable-show-labels { + a { + margin-right: 5px; + margin-bottom: 5px; + display: inline-block; + .color-label { + padding: 6px 10px; + } + } +} + .cross-project-reference { text-align: center; width: 100%; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 41c069f0ad3..f5548c5b88b 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -56,17 +56,6 @@ } } -.issue-show-labels { - a { - margin-right: 5px; - margin-bottom: 5px; - display: inline-block; - .color-label { - padding: 6px 10px; - } - } -} - form.edit-issue { margin: 0; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 017a86bcd9a..af6c6830fab 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -183,17 +183,6 @@ display: none; } -.merge-request-show-labels { - a { - margin-right: 5px; - margin-bottom: 5px; - display: inline-block; - .color-label { - padding: 6px 10px; - } - } -} - .merge-request-form .select2-container { width: 250px !important; } diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 521364ac858..f44b439a843 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -33,7 +33,7 @@ %div.prepend-top-default.clearfix .issuable-context-title %label Labels - .merge-request-show-labels + .issuable-show-labels - issuable.labels.each do |label| = link_to_label(label) diff --git a/features/steps/project/forked_merge_requests.rb b/features/steps/project/forked_merge_requests.rb index 789befc9df2..34b435919ac 100644 --- a/features/steps/project/forked_merge_requests.rb +++ b/features/steps/project/forked_merge_requests.rb @@ -115,7 +115,7 @@ class Spinach::Features::ProjectForkedMergeRequests < Spinach::FeatureSteps expect(find(:select, "merge_request_source_project_id", {}).value).to eq @forked_project.id.to_s expect(find(:select, "merge_request_target_project_id", {}).value).to eq @project.id.to_s expect(find(:select, "merge_request_source_branch", {}).value).to eq "" - expect(find(:select, "merge_request_target_branch", {}).value).to eq "" + expect(find(:select, "merge_request_target_branch", {}).value).to eq "master" click_button "Compare branches" end diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index af2da41badb..264408e85ec 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -86,7 +86,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'I should see label \'bug\' with issue' do - page.within '.issue-show-labels' do + page.within '.issuable-show-labels' do expect(page).to have_content 'bug' end end -- cgit v1.2.1 From c5b6b31c847d5d2f467453d8292e13ca735ee630 Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Wed, 18 Nov 2015 20:08:07 +0200 Subject: Fixed invalid link on starred projects dashboard. Fixes #3468 --- CHANGELOG | 1 + app/views/dashboard/projects/index.html.haml | 2 +- app/views/dashboard/projects/starred.html.haml | 2 +- app/views/explore/projects/index.html.haml | 2 +- app/views/explore/projects/starred.html.haml | 2 +- app/views/explore/projects/trending.html.haml | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index db812796b69..438cae617a7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.2.2 - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu) - Fix: Raw private snippets access workflow - Prevent "413 Request entity too large" errors when pushing large files with LFS + - Fix invalid links within projects dashboard header v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 7a16b811f6b..53abf274bdb 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -3,7 +3,7 @@ = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity") - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path = render 'dashboard/projects_head' diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml index f75f2e0a32a..70705923d42 100644 --- a/app/views/dashboard/projects/starred.html.haml +++ b/app/views/dashboard/projects/starred.html.haml @@ -1,5 +1,5 @@ - page_title "Starred Projects" -- header_title "Projects", projects_path +- header_title "Projects", dashboard_projects_path = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 67e38ca3127..76bdd68fd76 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml index 596cb0a96cd..e30c3633223 100644 --- a/app/views/explore/projects/starred.html.haml +++ b/app/views/explore/projects/starred.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml index 5ea6d81c5b9..1412b19acde 100644 --- a/app/views/explore/projects/trending.html.haml +++ b/app/views/explore/projects/trending.html.haml @@ -1,5 +1,5 @@ - page_title "Projects" -- header_title "Projects", root_path +- header_title "Projects", dashboard_projects_path - if current_user = render 'dashboard/projects_head' -- cgit v1.2.1 From 0decc7f941b96bf53e172de0340847d3b7d714b7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 3 Dec 2015 19:40:31 -0200 Subject: Fix specs --- app/assets/stylesheets/framework/markdown_area.scss | 1 - app/views/projects/_md_preview.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 11b609f3b79..2b044786738 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -74,7 +74,6 @@ .referenced-users { color: #4c4e54; - display: inline-block; padding-top: 10px; } diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index 5bdceb9523a..54c818baaf4 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -15,8 +15,8 @@ .js-md-preview{class: (preview_class if defined?(preview_class))} - if defined?(referenced_users) && referenced_users - %div.clearfix - %span.referenced-users.hide + %div.referenced-users.hide + %span = icon('exclamation-triangle') You are about to add %strong -- cgit v1.2.1 From 83e6d8294f9e1c4ee199a3577e5fc810df33def6 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 3 Dec 2015 23:14:19 +0100 Subject: Update .ruby-version to 2.1.7 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 399088bf465..04b10b4f150 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.1.6 +2.1.7 -- cgit v1.2.1 From 387e5656a1e158eaa1c010f22d332d758b336179 Mon Sep 17 00:00:00 2001 From: Kevin Pankonen Date: Thu, 3 Dec 2015 15:40:08 -0700 Subject: fixes #3263 slashes are replaced with two underscores --- doc/ci/docker/using_docker_images.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index ef8a7ec1e86..64e52eba3a2 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -60,11 +60,11 @@ This is image that have fully preconfigured `wordpress` and have `MySQL` server ``` Next time when you run your application the `tutum/wordpress` will be started -and you will have access to it from your build container under hostname: `tutum_wordpress`. +and you will have access to it from your build container under hostname: `tutum__wordpress`. Alias hostname for the service is made from the image name: 1. Everything after `:` is stripped, -2. '/' is replaced to `_`. +2. '/' is replaced with `__`. ### Configuring services Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment. -- cgit v1.2.1 From 0ccd7de7f369d98615833867abe84142bcc25193 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 10:55:36 +0100 Subject: Fix wrong doc in merge request API Signed-off-by: Dmitriy Zaporozhets --- doc/api/merge_requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 0cef09d5b27..3a1fc406fd1 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -159,7 +159,7 @@ Parameters: "updated_at": "2015-02-02T19:49:26.013Z", "due_date": null }, - "files": [ + "changes": [ { "old_path": "VERSION", "new_path": "VERSION", -- cgit v1.2.1 From 0b68a0e79e1cbe12c44beb6c23e79d48b71f219e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 11:08:10 +0100 Subject: Add API endpoint to fetch merge request commits list Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + doc/api/merge_requests.md | 39 ++++++++++++++++++++++++++++++++ lib/api/merge_requests.rb | 16 +++++++++++++ spec/requests/api/merge_requests_spec.rb | 17 ++++++++++++++ 4 files changed, 73 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 2f310a4b028..2876e36f9ef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Add ignore whitespace change option to commit view - Fire update hook from GitLab - Don't show project fork event as "imported" + - Add API endpoint to fetch merge request commits list v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 3a1fc406fd1..2b1498c85a0 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -101,6 +101,45 @@ Parameters: } ``` +## Get single MR commits + +Get a list of repository commits in a merge request. + +``` +GET /projects/:id/merge_request/:merge_request_id/commits +``` + +Parameters: + +- `id` (required) - The ID of a project +- `merge_request_id` (required) - The ID of MR + + +```json +[ + { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Replace sanitize with escape once", + "author_name": "Dmitriy Zaporozhets", + "author_email": "dzaporozhets@sphereconsultinginc.com", + "created_at": "2012-09-20T11:50:22+03:00", + "message": "Replace sanitize with escape once", + "allow_failure": false + }, + { + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "short_id": "6104942438c", + "title": "Sanitize for network graph", + "author_name": "randx", + "author_email": "dmitriy.zaporozhets@gmail.com", + "created_at": "2012-09-20T09:06:12+03:00", + "message": "Sanitize for network graph", + "allow_failure": false + } +] +``` + ## Get single MR changes Shows information about the merge request including its files and changes. diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 6eb84baf9cb..e7c5f808aea 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -76,6 +76,22 @@ module API present merge_request, with: Entities::MergeRequest end + # Show MR commits + # + # Parameters: + # id (required) - The ID of a project + # merge_request_id (required) - The ID of MR + # + # Example: + # GET /projects/:id/merge_request/:merge_request_id/commits + # + get ':id/merge_request/:merge_request_id/commits' do + merge_request = user_project.merge_requests. + find(params[:merge_request_id]) + authorize! :read_merge_request, merge_request + present merge_request.commits, with: Entities::RepoCommit + end + # Show MR changes # # Parameters: diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index a68c7b1e461..c6d3aef0af9 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -131,6 +131,23 @@ describe API::API, api: true do end end + describe 'GET /projects/:id/merge_request/:merge_request_id/commits' do + context 'valid merge request' do + before { get api("/projects/#{project.id}/merge_request/#{merge_request.id}/commits", user) } + let(:commit) { merge_request.commits.first } + + it { expect(response.status).to eq 200 } + it { expect(json_response.size).to eq(merge_request.commits.size) } + it { expect(json_response.first['id']).to eq(commit.id) } + it { expect(json_response.first['title']).to eq(commit.title) } + end + + it 'returns a 404 when merge_request_id not found' do + get api("/projects/#{project.id}/merge_request/999/commits", user) + expect(response.status).to eq(404) + end + end + describe 'GET /projects/:id/merge_request/:merge_request_id/changes' do it 'should return the change information of the merge_request' do get api("/projects/#{project.id}/merge_request/#{merge_request.id}/changes", user) -- cgit v1.2.1 From c366e81da7fb6c9ef921cbd57e5ac662999f0bc0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 11:26:35 +0100 Subject: Improve docs Signed-off-by: Dmitriy Zaporozhets --- doc/api/merge_requests.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2b1498c85a0..82f2cef969f 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -103,7 +103,7 @@ Parameters: ## Get single MR commits -Get a list of repository commits in a merge request. +Get a list of merge request commits. ``` GET /projects/:id/merge_request/:merge_request_id/commits @@ -124,8 +124,7 @@ Parameters: "author_name": "Dmitriy Zaporozhets", "author_email": "dzaporozhets@sphereconsultinginc.com", "created_at": "2012-09-20T11:50:22+03:00", - "message": "Replace sanitize with escape once", - "allow_failure": false + "message": "Replace sanitize with escape once" }, { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", @@ -134,8 +133,7 @@ Parameters: "author_name": "randx", "author_email": "dmitriy.zaporozhets@gmail.com", "created_at": "2012-09-20T09:06:12+03:00", - "message": "Sanitize for network graph", - "allow_failure": false + "message": "Sanitize for network graph" } ] ``` -- cgit v1.2.1 From b9a0f96d42f52dfdad57ac8a2a5056a9262e6a88 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 4 Dec 2015 12:01:08 +0100 Subject: Don't show diffs for lfs pointers. --- app/models/lfs_object.rb | 2 -- app/views/projects/diffs/_file.html.haml | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index f087d07b5b2..43b845b29c6 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -6,8 +6,6 @@ class LfsObject < ActiveRecord::Base mount_uploader :file, LfsObjectUploader - MATCH_FROM_POINTER_REGEX = "(?<=sha256:)([0-9a-f]{64})" - def storage_project(project) if project && project.forked? project.forked_from_project diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index b3392d00e01..ec1c665716e 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -25,7 +25,7 @@ = "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" .diff-controls - - if blob.text? + - if blob.text? && !blob.lfs_pointer? = link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do %i.fa.fa-comments   @@ -40,7 +40,7 @@ .diff-content.diff-wrap-lines -# Skipp all non non-supported blobs - return unless blob.respond_to?('text?') - - if blob.text? + - if blob.text? && !blob.lfs_pointer? - if diff_view == 'parallel' = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i - else -- cgit v1.2.1 From 3227a5ead22c90873794b0c0e4c788436283fb3e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 4 Dec 2015 12:21:06 +0100 Subject: Extent Event and Note API * add note to Events API * add author section to Events API * add noteable_id and noteable_type to Notes API Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + doc/api/notes.md | 15 ++++++-- doc/api/projects.md | 71 +++++++++++++++++++++++++++++++++++--- lib/api/entities.rb | 3 ++ spec/requests/api/projects_spec.rb | 30 ++++++++++++---- 5 files changed, 106 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2f310a4b028..002a132b9cd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ v 8.3.0 (unreleased) - Add ignore whitespace change option to commit view - Fire update hook from GitLab - Don't show project fork event as "imported" + - Expose events API with comment information and author info v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/doc/api/notes.md b/doc/api/notes.md index e7f299c0994..4d7ef288df8 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -35,7 +35,9 @@ Parameters: "created_at": "2013-10-02T09:22:45Z", "system": true, "upvote": false, - "downvote": false + "downvote": false, + "noteable_id": 377, + "noteable_type": "Issue" }, { "id": 305, @@ -52,7 +54,9 @@ Parameters: "created_at": "2013-10-02T09:56:03Z", "system": true, "upvote": false, - "downvote": false + "downvote": false, + "noteable_id": 121, + "noteable_type": "Issue" } ] ``` @@ -219,7 +223,12 @@ Parameters: "state": "active", "created_at": "2013-09-30T13:46:01Z" }, - "created_at": "2013-10-02T08:57:14Z" + "created_at": "2013-10-02T08:57:14Z", + "system": false, + "upvote": false, + "downvote": false, + "noteable_id": 2, + "noteable_type": "MergeRequest" } ``` diff --git a/doc/api/projects.md b/doc/api/projects.md index 755cc6525c2..42919a312ae 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -245,9 +245,17 @@ Parameters: "target_id": 830, "target_type": "Issue", "author_id": 1, - "author_username": "john", "data": null, - "target_title": "Public project search field" + "target_title": "Public project search field", + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" }, { "title": null, @@ -256,6 +264,14 @@ Parameters: "target_id": null, "target_type": null, "author_id": 1, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, "author_username": "john", "data": { "before": "50d4420237a9de7be1304607147aec22e4a14af7", @@ -292,9 +308,56 @@ Parameters: "target_id": 840, "target_type": "Issue", "author_id": 1, - "author_username": "john", "data": null, - "target_title": "Finish & merge Code search PR" + "target_title": "Finish & merge Code search PR", + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" + }, + { + "title": null, + "project_id": 15, + "action_name": "commented on", + "target_id": 1312, + "target_type": "Note", + "author_id": 1, + "data": null, + "target_title": null, + "created_at": "2015-12-04T10:33:58.089Z", + "note": { + "id": 1312, + "body": "What an awesome day!", + "attachment": null, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "created_at": "2015-12-04T10:33:56.698Z", + "system": false, + "upvote": false, + "downvote": false, + "noteable_id": 377, + "noteable_type": "Issue" + }, + "author": { + "name": "Dmitriy Zaporozhets", + "username": "root", + "id": 1, + "state": "active", + "avatar_url": "http://localhost:3000/uploads/user/avatar/1/fox_avatar.png", + "web_url": "http://localhost:3000/u/root" + }, + "author_username": "root" } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9f337bc3cc6..96b73df6af9 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -194,6 +194,7 @@ module API expose :author, using: Entities::UserBasic expose :created_at expose :system?, as: :system + expose :noteable_id, :noteable_type # upvote? and downvote? are deprecated, always return false expose :upvote?, as: :upvote expose :downvote?, as: :downvote @@ -224,6 +225,8 @@ module API expose :target_id, :target_type, :author_id expose :data, :target_title expose :created_at + expose :note, using: Entities::Note, if: ->(event, options) { event.note? } + expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author } expose :author_username do |event, options| if event.author diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 9fc294118ae..c59ee7af8ab 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -389,14 +389,30 @@ describe API::API, api: true do describe 'GET /projects/:id/events' do before { project_member2 } - it 'should return a project events' do - get api("/projects/#{project.id}/events", user) - expect(response.status).to eq(200) - json_event = json_response.first + context 'valid request' do + before do + note = create(:note_on_issue, note: 'What an awesome day!', project: project) + EventCreateService.new.leave_note(note, note.author) + get api("/projects/#{project.id}/events", user) + end + + it { expect(response.status).to eq(200) } + + context 'joined event' do + let(:json_event) { json_response[1] } - expect(json_event['action_name']).to eq('joined') - expect(json_event['project_id'].to_i).to eq(project.id) - expect(json_event['author_username']).to eq(user3.username) + it { expect(json_event['action_name']).to eq('joined') } + it { expect(json_event['project_id'].to_i).to eq(project.id) } + it { expect(json_event['author_username']).to eq(user3.username) } + it { expect(json_event['author']['name']).to eq(user3.name) } + end + + context 'comment event' do + let(:json_event) { json_response.first } + + it { expect(json_event['action_name']).to eq('commented on') } + it { expect(json_event['note']['body']).to eq('What an awesome day!') } + end end it 'should return a 404 error if not found' do -- cgit v1.2.1 From b2c4675cb0e681027334e5bd046451d3116924c8 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Fri, 4 Dec 2015 12:32:13 +0100 Subject: Recursivity needed if a fork is a fork of a fork. --- app/models/lfs_object.rb | 2 +- lib/gitlab/lfs/response.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 43b845b29c6..a243c7b77cc 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -8,7 +8,7 @@ class LfsObject < ActiveRecord::Base def storage_project(project) if project && project.forked? - project.forked_from_project + storage_project(project.forked_from_project) else project end diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb index 9be9a65671b..9d9617761b3 100644 --- a/lib/gitlab/lfs/response.rb +++ b/lib/gitlab/lfs/response.rb @@ -220,7 +220,7 @@ module Gitlab def storage_project(project) if project.forked? - project.forked_from_project + storage_project(project.forked_from_project) else project end -- cgit v1.2.1 From 496870ddec632ae13cff8b5e8f6ca0bff0fa3ec7 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 3 Dec 2015 21:24:39 -0200 Subject: Migrate from Sidetiq to Sidekiq-cron Updated Sidekiq to 3.5.x --- Gemfile | 4 +-- Gemfile.lock | 46 +++++++++++++++++++++++------------ app/workers/stuck_ci_builds_worker.rb | 3 --- config/initializers/sidekiq.rb | 6 +++++ config/routes.rb | 1 + config/schedule.yml | 10 ++++++++ 6 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 config/schedule.yml diff --git a/Gemfile b/Gemfile index 67640bb9ae0..954c5e13503 100644 --- a/Gemfile +++ b/Gemfile @@ -120,8 +120,8 @@ gem 'acts-as-taggable-on', '~> 3.4' # Background jobs gem 'sinatra', '~> 1.4.4', require: nil -gem 'sidekiq', '3.3.0' -gem 'sidetiq', '~> 0.6.3' +gem 'sidekiq', '~> 3.5.0' +gem 'sidekiq-cron', '~> 0.3.0' # HTTP requests gem "httparty", '~> 0.13.3' diff --git a/Gemfile.lock b/Gemfile.lock index cd1855758b2..67807bed4c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,8 +116,23 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.16.0) - timers (~> 4.0.0) + celluloid (0.17.2) + celluloid-essentials + celluloid-extras + celluloid-fsm + celluloid-pool + celluloid-supervision + timers (>= 4.1.1) + celluloid-essentials (0.20.5) + timers (>= 4.1.1) + celluloid-extras (0.20.5) + timers (>= 4.1.1) + celluloid-fsm (0.20.5) + timers (>= 4.1.1) + celluloid-pool (0.20.5) + timers (>= 4.1.1) + celluloid-supervision (0.20.5) + timers (>= 4.1.1) charlock_holmes (0.7.3) chunky_png (1.3.5) cliver (0.3.2) @@ -369,7 +384,6 @@ GEM multi_xml (>= 0.5.2) httpclient (2.7.0.1) i18n (0.7.0) - ice_cube (0.11.1) ice_nine (0.11.1) inflecto (0.0.2) ipaddress (0.8.0) @@ -640,6 +654,7 @@ GEM sexp_processor (~> 4.1) rubyntlm (0.5.2) rubypants (0.2.0) + rufus-scheduler (3.1.10) rugged (0.23.3) safe_yaml (1.0.4) sanitize (2.1.0) @@ -667,16 +682,15 @@ GEM rack shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (3.3.0) - celluloid (>= 0.16.0) - connection_pool (>= 2.0.0) - json - redis (>= 3.0.6) - redis-namespace (>= 1.3.1) - sidetiq (0.6.3) - celluloid (>= 0.14.1) - ice_cube (= 0.11.1) - sidekiq (>= 3.0.0) + sidekiq (3.5.3) + celluloid (~> 0.17.2) + connection_pool (~> 2.2, >= 2.2.0) + json (~> 1.0) + redis (~> 3.2, >= 3.2.1) + redis-namespace (~> 1.5, >= 1.5.2) + sidekiq-cron (0.3.1) + rufus-scheduler (>= 2.0.24) + sidekiq (>= 2.17.3) simple_oauth (0.1.9) simplecov (0.10.0) docile (~> 1.1.0) @@ -742,7 +756,7 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.0.4) + timers (4.1.1) hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) @@ -936,8 +950,8 @@ DEPENDENCIES settingslogic (~> 2.0.9) sham_rack shoulda-matchers (~> 2.8.0) - sidekiq (= 3.3.0) - sidetiq (~> 0.6.3) + sidekiq (~> 3.5.0) + sidekiq-cron (~> 0.3.0) simplecov (~> 0.10.0) sinatra (~> 1.4.4) six (~> 0.2.0) diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb index 4e5eddbaba1..ca594e77e7c 100644 --- a/app/workers/stuck_ci_builds_worker.rb +++ b/app/workers/stuck_ci_builds_worker.rb @@ -1,11 +1,8 @@ class StuckCiBuildsWorker include Sidekiq::Worker - include Sidetiq::Schedulable BUILD_STUCK_TIMEOUT = 1.day - recurrence { daily } - def perform Rails.logger.info 'Cleaning stuck builds' diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index e856499732e..6e5701e33da 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -17,6 +17,12 @@ Sidekiq.configure_server do |config| chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] end + + # Sidekiq-cron: load recurring jobs from schedule.yml + schedule_file = 'config/schedule.yml' + if File.exists?(schedule_file) + Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) + end end Sidekiq.configure_client do |config| diff --git a/config/routes.rb b/config/routes.rb index 5c114452a3f..6aa822bf859 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ require 'sidekiq/web' +require 'sidekiq/cron/web' require 'api/api' Rails.application.routes.draw do diff --git a/config/schedule.yml b/config/schedule.yml new file mode 100644 index 00000000000..993a95fef56 --- /dev/null +++ b/config/schedule.yml @@ -0,0 +1,10 @@ +# Here is a list of jobs that are scheduled to run periodically. +# We use a UNIX cron notation to specify execution schedule. +# +# Please read here for more information: +# https://github.com/ondrejbartas/sidekiq-cron#adding-cron-job + +stuck_ci_builds_worker: + cron: "0 0 * * *" + class: "StuckCiBuildsWorker" + queue: "default" -- cgit v1.2.1 From 5c1b49f494f07bf37ba3c60f3b9f70d1842d8b60 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 4 Dec 2015 16:23:21 +0200 Subject: Add added, modified and removed properties to commit object in webhook --- CHANGELOG | 3 +++ app/models/commit.rb | 24 ++++++++++++++++++++++- doc/web_hooks/web_hooks.md | 14 +++++++++----- lib/gitlab/push_data_builder.rb | 32 ++----------------------------- spec/lib/gitlab/push_data_builder_spec.rb | 9 +++------ spec/models/commit_spec.rb | 11 +++++++++++ 6 files changed, 51 insertions(+), 42 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..d8d878c45c9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,9 @@ v 8.3.0 (unreleased) - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info +v 8.2.3 + - Webhook payload has an added, modified and removed properties for each commit + v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) - Ensure cached application settings are refreshed at startup (Stan Hu) diff --git a/app/models/commit.rb b/app/models/commit.rb index 492f6be1ce3..912b4dedf51 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -146,7 +146,10 @@ class Commit author: { name: author_name, email: author_email - } + }, + added: repo_changes[:added], + modified: repo_changes[:modified], + removed: repo_changes[:removed] } end @@ -196,4 +199,23 @@ class Commit def status ci_commit.try(:status) || :not_found end + + private + + def repo_changes + changes = { added: [], modified: [], removed: [] } + + diffs.each do |diff| + case true + when diff.deleted_file + changes[:removed] << diff.old_path + when diff.renamed_file, diff.new_file + changes[:added] << diff.new_path + else + changes[:modified] << diff.new_path + end + end + + changes + end end diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md index 7d838187a26..03746dd9df3 100644 --- a/doc/web_hooks/web_hooks.md +++ b/doc/web_hooks/web_hooks.md @@ -57,6 +57,9 @@ X-Gitlab-Event: Push Hook "name": "Jordi Mallach", "email": "jordi@softcatala.org" } + "added": ["CHANGELOG"], + "modified": ["app/controller/application.rb"], + "removed": [] }, { "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", @@ -66,13 +69,14 @@ X-Gitlab-Event: Push Hook "author": { "name": "GitLab dev user", "email": "gitlabdev@dv6700.(none)" - } + }, + "added": ["CHANGELOG"], + "modified": ["app/controller/application.rb"], + "removed": [] } ], - "total_commits_count": 4, - "added": ["CHANGELOG"], - "modified": ["app/controller/application.rb"], - "removed": [] + "total_commits_count": 4 + } ``` diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index fa068d50763..cdcdb02a052 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -18,10 +18,7 @@ module Gitlab # homepage: String, # }, # commits: Array, - # total_commits_count: Fixnum, - # added: ["CHANGELOG"], - # modified: [], - # removed: ["tmp/file.txt"] + # total_commits_count: Fixnum # } # def build(project, user, oldrev, newrev, ref, commits = [], message = nil) @@ -37,7 +34,6 @@ module Gitlab type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" - repo_changes = repo_changes(project, newrev, oldrev) # Hash to be passed as post_receive_data data = { object_kind: type, @@ -60,10 +56,7 @@ module Gitlab visibility_level: project.visibility_level }, commits: commit_attrs, - total_commits_count: commits_count, - added: repo_changes[:added], - modified: repo_changes[:modified], - removed: repo_changes[:removed] + total_commits_count: commits_count } data @@ -94,27 +87,6 @@ module Gitlab newrev end end - - def repo_changes(project, newrev, oldrev) - changes = { added: [], modified: [], removed: [] } - compare_result = CompareService.new. - execute(project, newrev, project, oldrev) - - if compare_result - compare_result.diffs.each do |diff| - case true - when diff.deleted_file - changes[:removed] << diff.old_path - when diff.renamed_file, diff.new_file - changes[:added] << diff.new_path - else - changes[:modified] << diff.new_path - end - end - end - - changes - end end end end diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index 02710742625..2170399ab5c 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -17,9 +17,9 @@ describe 'Gitlab::PushDataBuilder' do it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) } it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) } it { expect(data[:total_commits_count]).to eq(3) } - it { expect(data[:added]).to eq(["gitlab-grack"]) } - it { expect(data[:modified]).to eq([".gitmodules", "files/ruby/popen.rb", "files/ruby/regex.rb"]) } - it { expect(data[:removed]).to eq([]) } + it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) } + it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) } + it { expect(data[:commits].first[:removed]).to eq([]) } end describe :build do @@ -38,8 +38,5 @@ describe 'Gitlab::PushDataBuilder' do it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } it { expect(data[:commits]).to be_empty } it { expect(data[:total_commits_count]).to be_zero } - it { expect(data[:added]).to eq([]) } - it { expect(data[:modified]).to eq([]) } - it { expect(data[:removed]).to eq([]) } end end diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 90be9324951..b417bc98fa7 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -100,4 +100,15 @@ eos # Include the subject in the repository stub. let(:extra_commits) { [subject] } end + + describe '#hook_attrs' do + let(:data) { commit.hook_attrs } + + it { expect(data).to be_a(Hash) } + it { expect(data[:message]).to include('Add submodule from gitlab.com') } + it { expect(data[:timestamp]).to eq('2014-02-27T11:01:38+02:00') } + it { expect(data[:added]).to eq(["gitlab-grack"]) } + it { expect(data[:modified]).to eq([".gitmodules"]) } + it { expect(data[:removed]).to eq([]) } + end end -- cgit v1.2.1 From 32b45493b89b35b7b7d3f086e8996d1ed7b8d0f3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 3 Dec 2015 00:09:10 -0800 Subject: Fix application settings cache not expiring after changes cache_key is an instance method that relies on updated_at. When changes were made, the time-dependent key was being used instead of X.application_setting.last. Closes #3609 --- CHANGELOG | 1 + app/models/application_setting.rb | 12 +++++------- app/models/ci/application_setting.rb | 11 ++++------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..9ea091c3ce5 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.3.0 (unreleased) + - Fix application settings cache not expiring after changes (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 3df8135acf1..5ddcf3d9a0b 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -30,6 +30,8 @@ # class ApplicationSetting < ActiveRecord::Base + CACHE_KEY = 'application_setting.last' + serialize :restricted_visibility_levels serialize :import_sources serialize :restricted_signup_domains, Array @@ -73,21 +75,17 @@ class ApplicationSetting < ActiveRecord::Base end after_commit do - Rails.cache.write(cache_key, self) + Rails.cache.write(CACHE_KEY, self) end def self.current - Rails.cache.fetch(cache_key) do + Rails.cache.fetch(CACHE_KEY) do ApplicationSetting.last end end def self.expire - Rails.cache.delete(cache_key) - end - - def self.cache_key - 'application_setting.last' + Rails.cache.delete(CACHE_KEY) end def self.create_from_defaults diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb index 4e512d290ee..7f5df8ce6c4 100644 --- a/app/models/ci/application_setting.rb +++ b/app/models/ci/application_setting.rb @@ -12,17 +12,18 @@ module Ci class ApplicationSetting < ActiveRecord::Base extend Ci::Model + CACHE_KEY = 'ci_application_setting.last' after_commit do - Rails.cache.write(cache_key, self) + Rails.cache.write(CACHE_KEY, self) end def self.expire - Rails.cache.delete(cache_key) + Rails.cache.delete(CACHE_KEY) end def self.current - Rails.cache.fetch(cache_key) do + Rails.cache.fetch(CACHE_KEY) do Ci::ApplicationSetting.last end end @@ -33,9 +34,5 @@ module Ci add_pusher: Settings.gitlab_ci['add_pusher'], ) end - - def self.cache_key - 'ci_application_setting.last' - end end end -- cgit v1.2.1 From f1fd4880d9bbb7c34e910b357bc52874d2e6188e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 2 Dec 2015 20:11:58 +0000 Subject: Check GitLab Workhorse status in init.d script when reporting all components are up and running Closes https://github.com/gitlabhq/gitlabhq/issues/9869 --- lib/support/init.d/gitlab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index f0a6c2b30e9..43fda6fa92e 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -327,7 +327,7 @@ print_status() { printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n" fi fi - if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then + if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then printf "GitLab and all its components are \033[32mup and running\033[0m.\n" fi } -- cgit v1.2.1 From a120b78940b6c7150f405091d620b34c0fccbd28 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 1 Dec 2015 16:15:01 -0800 Subject: Handle and report SSL errors in Web hook test. Check for status 200 for success. If a Web hook test fails due to an SSL error or some other error, report the result back to the user instead of an Error 500. Closes #3656 Handle response --- CHANGELOG | 1 + app/controllers/projects/hooks_controller.rb | 5 ++-- app/models/hooks/web_hook.rb | 36 +++++++++++++++------------- spec/models/hooks/web_hook_spec.rb | 6 +++++ 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..faee22405dd 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.3.0 (unreleased) + - Handle and report SSL errors in Web hook test (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index c7569541899..6a62880cb71 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -25,13 +25,12 @@ class Projects::HooksController < Projects::ApplicationController def test if !@project.empty_repo? - status = TestHookService.new.execute(hook, current_user) + status, message = TestHookService.new.execute(hook, current_user) if status flash[:notice] = 'Hook successfully executed.' else - flash[:alert] = 'Hook execution failed. '\ - 'Ensure hook URL is correct and service is up.' + flash[:alert] = "Hook execution failed: #{message}" end else flash[:alert] = 'Hook execution failed. Ensure the project has commits.' diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index d6c6f415c4a..2caf26cc8c9 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -37,31 +37,33 @@ class WebHook < ActiveRecord::Base def execute(data, hook_name) parsed_url = URI.parse(url) if parsed_url.userinfo.blank? - WebHook.post(url, - body: data.to_json, - headers: { - "Content-Type" => "application/json", - "X-Gitlab-Event" => hook_name.singularize.titleize - }, - verify: enable_ssl_verification) + response = WebHook.post(url, + body: data.to_json, + headers: { + "Content-Type" => "application/json", + "X-Gitlab-Event" => hook_name.singularize.titleize + }, + verify: enable_ssl_verification) else post_url = url.gsub("#{parsed_url.userinfo}@", "") auth = { username: URI.decode(parsed_url.user), password: URI.decode(parsed_url.password), } - WebHook.post(post_url, - body: data.to_json, - headers: { - "Content-Type" => "application/json", - "X-Gitlab-Event" => hook_name.singularize.titleize - }, - verify: enable_ssl_verification, - basic_auth: auth) + response = WebHook.post(post_url, + body: data.to_json, + headers: { + "Content-Type" => "application/json", + "X-Gitlab-Event" => hook_name.singularize.titleize + }, + verify: enable_ssl_verification, + basic_auth: auth) end - rescue SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e + + [response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)] + rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e logger.error("WebHook Error => #{e}") - false + [false, e.to_s] end def async_execute(data, hook_name) diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 2fdc49f02ee..35042788c65 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -71,5 +71,11 @@ describe ProjectHook do expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) end + + it "handles SSL exceptions" do + expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error')) + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) + end end end -- cgit v1.2.1 From ba3c702073f2f57eebbeabb2926fbd367aad87ea Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 1 Dec 2015 16:45:40 -0800 Subject: Fix spec --- features/steps/project/hooks.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/features/steps/project/hooks.rb b/features/steps/project/hooks.rb index df4a23a3716..be4db770948 100644 --- a/features/steps/project/hooks.rb +++ b/features/steps/project/hooks.rb @@ -70,8 +70,6 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps step 'I should see hook service down error message' do expect(page).to have_selector '.flash-alert', - text: 'Hook execution failed. '\ - 'Ensure hook URL is correct and '\ - 'service is up.' + text: 'Hook execution failed: Exception from' end end -- cgit v1.2.1 From 253301bb47d14494ea45230aeb682a9b7dffd5bb Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Nov 2015 16:38:23 -0800 Subject: Make current user the first user in assignee dropdown in issues detail page Closes #3679 --- CHANGELOG | 1 + app/views/shared/issuable/_context.html.haml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..93d59966873 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.2.2 - Fix: Raw private snippets access workflow - Prevent "413 Request entity too large" errors when pushing large files with LFS - Fix invalid links within projects dashboard header + - Make current user the first user in assignee dropdown in issues detail page (Stan Hu) v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index f44b439a843..2aa46662613 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -9,7 +9,7 @@ none .issuable-context-selectbox - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true) + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) %div.prepend-top-default.clearfix .issuable-context-title -- cgit v1.2.1 From aa1ba0093632a66c9c9c0eac710d63d7513ad358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Tue, 24 Nov 2015 22:41:36 -0500 Subject: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 --- CHANGELOG | 1 + .../javascripts/merge_request_widget.js.coffee | 9 ++++-- app/helpers/gitlab_routing_helper.rb | 2 +- app/views/projects/merge_requests/merge.js.haml | 2 +- .../merge_requests/widget/_merged.html.haml | 2 +- features/project/merge_requests/accept.feature | 17 +++++++++++ .../steps/project/merge_requests/acceptance.rb | 35 ++++++++++++++++++++++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 features/project/merge_requests/accept.feature create mode 100644 features/steps/project/merge_requests/acceptance.rb diff --git a/CHANGELOG b/CHANGELOG index 58a0a3c8944..f21129c6d61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ v 8.3.0 (unreleased) - Don't show project fork event as "imported" - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info + - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 3176e5a8965..c4b63966fe7 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -10,17 +10,20 @@ class @MergeRequestWidget constructor: (@opts) -> modal = $('#modal_merge_info').modal(show: false) - mergeInProgress: -> + mergeInProgress: (deleteSourceBranch = false)-> $.ajax type: 'GET' url: $('.merge-request').data('url') success: (data) => if data.state == "merged" - location.reload() + urlSuffix = if deleteSourceBranch then '?delete_source=true' else '' + + window.location.href = window.location.href + urlSuffix else if data.merge_error $('.mr-widget-body').html("

    " + data.merge_error + "

    ") else - setTimeout(merge_request_widget.mergeInProgress, 2000) + callback = -> merge_request_widget.mergeInProgress(deleteSourceBranch) + setTimeout(callback, 2000) dataType: 'json' getMergeStatus: -> diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb index b0b536d4649..f3fddef01cb 100644 --- a/app/helpers/gitlab_routing_helper.rb +++ b/app/helpers/gitlab_routing_helper.rb @@ -6,7 +6,7 @@ # # For example instead of this: # -# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request) +# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) # # We can simply use shortcut: # diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml index 33321651e32..518ecb9f00f 100644 --- a/app/views/projects/merge_requests/merge.js.haml +++ b/app/views/projects/merge_requests/merge.js.haml @@ -1,6 +1,6 @@ - if @status :plain - merge_request_widget.mergeInProgress(); + merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'}); - else :plain $('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/reload'))}"); diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index ac08e0b498a..5c6fece8c5c 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -7,7 +7,7 @@ by #{link_to_member(@project, @merge_request.merge_event.author, avatar: true)} #{time_ago_with_tooltip(@merge_request.merge_event.created_at)} %div - - if !@merge_request.source_branch_exists? + - if !@merge_request.source_branch_exists? || (params[:delete_source] == 'true') = succeed '.' do The changes were merged into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature new file mode 100644 index 00000000000..3e6e59a3808 --- /dev/null +++ b/features/project/merge_requests/accept.feature @@ -0,0 +1,17 @@ +Feature: Project Merge Requests Acceptance + Background: + Given There is an open Merge Request + And I am signed in as a developer of the project + + @javascript + Scenario: Accepting the Merge Request and removing the source branch + Given I am on the Merge Request detail page + When I click on "Remove source branch" option + And I click on Accept Merge Request + Then I should not see the Remove Source Branch button + + @javascript + Scenario: Accepting the Merge Request without removing the source branch + Given I am on the Merge Request detail page + When I click on Accept Merge Request + Then I should see the Remove Source Branch button diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb new file mode 100644 index 00000000000..6adecaa8385 --- /dev/null +++ b/features/steps/project/merge_requests/acceptance.rb @@ -0,0 +1,35 @@ +class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps + include LoginHelpers + include GitlabRoutingHelper + + step 'I am on the Merge Request detail page' do + visit merge_request_path(@merge_request) + end + + step 'I click on "Remove source branch" option' do + check('Remove source branch') + end + + step 'I click on Accept Merge Request' do + click_button('Accept Merge Request') + end + + step 'I should see the Remove Source Branch button' do + expect(page).to have_link('Remove Source Branch') + end + + step 'I should not see the Remove Source Branch button' do + expect(page).not_to have_link('Remove Source Branch') + end + + step 'There is an open Merge Request' do + @user = create(:user) + @project = create(:project, :public) + @project_member = create(:project_member, user: @user, project: @project, access_level: ProjectMember::DEVELOPER) + @merge_request = create(:merge_request, :with_diffs, :simple, source_project: @project) + end + + step 'I am signed in as a developer of the project' do + login_as(@user) + end +end -- cgit v1.2.1 From 12fdc13ad386299b04431c43c7d4ab307698b1a8 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Fri, 4 Dec 2015 17:31:01 -0200 Subject: Fix 500 error when creating a merge request that removes a submodule --- CHANGELOG | 1 + app/views/projects/diffs/_file.html.haml | 3 +-- spec/controllers/commit_controller_spec.rb | 20 ++++++++++++++++++ .../projects/merge_requests_controller_spec.rb | 24 ++++++++++++++++++++++ spec/support/test_env.rb | 3 ++- 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6b61cbc11e9..9b26f526fd8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.3.0 (unreleased) - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 + - Fix 500 error when creating a merge request that removes a submodule v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index b3392d00e01..b77e9f9f403 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -3,9 +3,8 @@ - if diff_file.diff.submodule? %span = icon('archive fw') - - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) %strong - = submodule_link(submodule_item, @commit.id, project.repository) + = submodule_link(blob, @commit.id, project.repository) - else %span = blob_icon blob.mode, blob.name diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index 5337a69e84b..7793bf1e421 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -110,6 +110,26 @@ describe Projects::CommitController do expect(response.body).to match(/^diff --git/) end end + + context 'commit that removes a submodule' do + render_views + + let(:fork_project) { create(:forked_project_with_submodules) } + let(:commit) { fork_project.commit('remove-submodule') } + + before do + fork_project.team << [user, :master] + end + + it 'renders it' do + get(:show, + namespace_id: fork_project.namespace.to_param, + project_id: fork_project.to_param, + id: commit.id) + + expect(response).to be_success + end + end end describe "#branches" do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 3e5e1fa87ae..6aaec224f6e 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -10,6 +10,30 @@ describe Projects::MergeRequestsController do project.team << [user, :master] end + describe '#new' do + context 'merge request that removes a submodule' do + render_views + + let(:fork_project) { create(:forked_project_with_submodules) } + + before do + fork_project.team << [user, :master] + end + + it 'renders it' do + get :new, + namespace_id: fork_project.namespace.to_param, + project_id: fork_project.to_param, + merge_request: { + source_branch: 'remove-submodule', + target_branch: 'master' + } + + expect(response).to be_success + end + end + end + describe "#show" do shared_examples "export merge as" do |format| it "should generally work" do diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 787670e9297..78b9a0f42fa 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -21,7 +21,8 @@ module TestEnv # We currently only need a subset of the branches FORKED_BRANCH_SHA = { 'add-submodule-version-bump' => '3f547c08', - 'master' => '5937ac0' + 'master' => '5937ac0', + 'remove-submodule' => '2a33e0c0' } # Test environment -- cgit v1.2.1 From 3c8051776b25add2e2845344a328328db33d1671 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 4 Dec 2015 14:35:29 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 6b61cbc11e9..ad15ed43b74 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - - Fix application settings cache not expiring after changes (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) @@ -12,6 +11,9 @@ v 8.3.0 (unreleased) - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 +v 8.2.3 + - Fix application settings cache not expiring after changes (Stan Hu) + v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) - Ensure cached application settings are refreshed at startup (Stan Hu) -- cgit v1.2.1 From d800a949d2d5497e8aff3ae28ec8520e5b99cdb8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 3 Dec 2015 23:33:52 -0800 Subject: Fix Error 500 when creating global milestones with Unicode characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues: 1. The constraints in the resources were incorrect. Here's what it was before: ``` group_milestone GET /groups/:group_id/milestones/:id(.:format) groups/milestones#show {:id=>/[a-zA-Z.0-9_\-]+(?/[a-zA-Z.0-9_\-]+(?/[^\/]+/, :group_id=>/[a-zA-Z.0-9_\-]+(?"show", :controller=>"groups/milestones", :group_id=>#, :id=>"", :title=>"肯定不是中文的问题"} missing required keys: [:id]): This change uses the babosa library to create a better slug, which surprisingly isn't actually used by the global milestone controllers. Instead, they use the title passed as a query string for some reason. Closes https://github.com/gitlabhq/gitlabhq/issues/9881 Fix constraints --- CHANGELOG | 1 + Gemfile | 1 + Gemfile.lock | 2 ++ app/controllers/groups/milestones_controller.rb | 2 +- app/models/global_milestone.rb | 2 +- config/routes.rb | 2 +- .../groups/milestones_controller_spec.rb | 27 ++++++++++++++++++++++ 7 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 spec/controllers/groups/milestones_controller_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 7b2f1528656..228848c13f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.3.0 (unreleased) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) + - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/Gemfile b/Gemfile index 67640bb9ae0..860e6bee47d 100644 --- a/Gemfile +++ b/Gemfile @@ -171,6 +171,7 @@ gem "underscore-rails", "~> 1.4.4" # Sanitize user input gem "sanitize", '~> 2.0' +gem 'babosa', '~> 1.0.2' # Protect against bruteforcing gem "rack-attack", '~> 4.3.0' diff --git a/Gemfile.lock b/Gemfile.lock index cd1855758b2..be3e39d8e84 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -73,6 +73,7 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) + babosa (1.0.2) bcrypt (3.1.10) benchmark-ips (2.3.0) better_errors (1.0.1) @@ -823,6 +824,7 @@ DEPENDENCIES asciidoctor (~> 1.5.2) attr_encrypted (~> 1.3.4) awesome_print (~> 1.2.0) + babosa (~> 1.0.2) benchmark-ips better_errors (~> 1.0.1) binding_of_caller (~> 0.7.2) diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 10233222ee1..0c2a350bc39 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -46,7 +46,7 @@ class Groups::MilestonesController < Groups::ApplicationController end def milestone_path(title) - group_milestone_path(@group, title.parameterize, title: title) + group_milestone_path(@group, title.to_slug.to_s, title: title) end def projects diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 1321ccd963f..85aa71662fe 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -16,7 +16,7 @@ class GlobalMilestone end def safe_title - @title.parameterize + @title.to_slug.to_s end def projects diff --git a/config/routes.rb b/config/routes.rb index 5c114452a3f..fdd387fd184 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -368,7 +368,7 @@ Rails.application.routes.draw do end resource :avatar, only: [:destroy] - resources :milestones, only: [:index, :show, :update, :new, :create] + resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create] end end diff --git a/spec/controllers/groups/milestones_controller_spec.rb b/spec/controllers/groups/milestones_controller_spec.rb new file mode 100644 index 00000000000..eb0c6ac6d80 --- /dev/null +++ b/spec/controllers/groups/milestones_controller_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe Groups::MilestonesController do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + let(:project2) { create(:empty_project, group: group) } + let(:user) { create(:user) } + let(:title) { '肯定不是中文的问题' } + + before do + sign_in(user) + group.add_owner(user) + project.team << [user, :master] + controller.instance_variable_set(:@group, group) + end + + describe "#create" do + it "should create group milestone with Chinese title" do + post :create, + group_id: group.id, + milestone: { project_ids: [project.id, project2.id], title: title } + + expect(response).to redirect_to(group_milestone_path(group, title.to_slug.to_s, title: title)) + expect(Milestone.where(title: title).count).to eq(2) + end + end +end -- cgit v1.2.1 From 4fab178850f512ad15715b6f723ace6fce7882fc Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 4 Dec 2015 22:56:10 -0800 Subject: Fix spec that broke due to fact that iid is needed, not id, for MilestonesController --- spec/controllers/projects/milestones_controller_spec.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 8127efabe6e..d173bb350f1 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -5,7 +5,7 @@ describe Projects::MilestonesController do let(:user) { create(:user) } let(:milestone) { create(:milestone, project: project) } let(:issue) { create(:issue, project: project, milestone: milestone) } - let(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) } + let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) } before do sign_in(user) @@ -15,10 +15,9 @@ describe Projects::MilestonesController do describe "#destroy" do it "should remove milestone" do - merge_request.reload expect(issue.milestone_id).to eq(milestone.id) - delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js + delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid, format: :js expect(response).to be_success expect(Event.first.action).to eq(Event::DESTROYED) -- cgit v1.2.1 From 2462a96e459c95f987f39e3c380de7c7cc350cfd Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 3 Dec 2015 10:27:34 +0100 Subject: Incorporate feedback --- app/models/merge_request.rb | 46 +++++++++++----------- .../merge_when_build_succeeds_service.rb | 9 ++--- app/services/system_note_service.rb | 6 +-- .../merge_requests/_merge_request.html.haml | 5 +-- .../merge_requests/widget/_heading.html.haml | 9 ++--- .../merge_requests/widget/open/_accept.html.haml | 3 +- .../open/_merge_when_build_succeeds.html.haml | 24 +++++------ spec/factories/merge_requests.rb | 5 +++ .../merge_when_build_succeeds_spec.rb | 21 ++++++++-- spec/models/merge_request_spec.rb | 44 +++++++++++++++++---- spec/requests/api/merge_requests_spec.rb | 2 +- .../merge_when_build_succeeds_service_spec.rb | 30 ++++++++------ .../merge_requests/refresh_service_spec.rb | 3 +- spec/services/system_note_service_spec.rb | 10 ++--- 14 files changed, 134 insertions(+), 83 deletions(-) diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 131dfda6b5f..1f81e23c7a3 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2,25 +2,28 @@ # # Table name: merge_requests # -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) +# id :integer not null, primary key +# target_branch :string(255) not null +# source_branch :string(255) not null +# source_project_id :integer not null +# author_id :integer +# assignee_id :integer +# title :string(255) +# created_at :datetime +# updated_at :datetime +# milestone_id :integer +# state :string(255) +# merge_status :string(255) +# target_project_id :integer not null +# iid :integer +# description :text +# position :integer default(0) +# locked_at :datetime +# updated_by_id :integer +# merge_error :string(255) +# merge_params :text (serialized to hash) +# merge_when_build_succeeds :boolean default(false), not null +# merge_user_id :integer # require Rails.root.join("app/models/commit") @@ -124,6 +127,7 @@ class MergeRequest < ActiveRecord::Base validates :source_branch, presence: true validates :target_project, presence: true validates :target_branch, presence: true + validates :merge_user, presence: true, if: :merge_when_build_succeeds? validate :validate_branches validate :validate_fork @@ -496,8 +500,6 @@ class MergeRequest < ActiveRecord::Base end def ci_commit - if last_commit - source_project.ci_commit(last_commit.id) - end + @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit end end diff --git a/app/services/merge_requests/merge_when_build_succeeds_service.rb b/app/services/merge_requests/merge_when_build_succeeds_service.rb index 2f101e53a3f..5cf7404a493 100644 --- a/app/services/merge_requests/merge_when_build_succeeds_service.rb +++ b/app/services/merge_requests/merge_when_build_succeeds_service.rb @@ -1,6 +1,6 @@ module MergeRequests class MergeWhenBuildSucceedsService < MergeRequests::BaseService - # Marks the passed `merge_request` to be marked when the build succeeds or + # Marks the passed `merge_request` to be merged when the build succeeds or # updates the params for the automatic merge def execute(merge_request) merge_request.merge_params.merge!(params) @@ -12,7 +12,7 @@ module MergeRequests merge_request.merge_when_build_succeeds = true merge_request.merge_user = @current_user - SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.ci_commit) + SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.last_commit) end merge_request.save @@ -25,8 +25,7 @@ module MergeRequests merge_requests.each do |merge_request| next unless merge_request.merge_when_build_succeeds? - ci_commit = merge_request.ci_commit - if ci_commit && ci_commit.success? && merge_request.mergeable? + if merge_request.ci_commit && merge_request.ci_commit.success? && merge_request.mergeable? MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params) end end @@ -34,7 +33,7 @@ module MergeRequests # Cancels the automatic merge def cancel(merge_request) - if merge_request.merge_when_build_succeeds? && merge_request.open? && !merge_request.merged? + if merge_request.merge_when_build_succeeds? && merge_request.open? merge_request.reset_merge_when_build_succeeds SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index ed557fef814..f84e480ca9c 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -131,15 +131,15 @@ class SystemNoteService end # Called when 'merge when build succeeds' is executed - def self.merge_when_build_succeeds(noteable, project, author, ci_commit) - body = "Enabled an automatic merge when the build for #{ci_commit.sha} succeeds" + def self.merge_when_build_succeeds(noteable, project, author, last_commit) + body = "Enabled an automatic merge when the build for #{last_commit.to_reference} succeeds" create_note(noteable: noteable, project: project, author: author, note: body) end # Called when 'merge when build succeeds' is canceled def self.cancel_merge_when_build_succeeds(noteable, project, author) - body = "Canceled the automatic merge" + body = "Cancelled the automatic merge" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index c5234c0618c..d10ccb30571 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,4 +1,3 @@ -- ci_commit = merge_request.ci_commit %li{ class: mr_css_classes(merge_request) } .merge-request-title %span.merge-request-title-text @@ -7,8 +6,8 @@ - merge_request.labels.each do |label| = link_to_label(label, project: merge_request.project) .pull-right.light - - if ci_commit - = render_ci_status(ci_commit) + - if merge_request.ci_commit + = render_ci_status(merge_request.ci_commit) - if merge_request.merged? %span %i.fa.fa-check diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index ba5ad22bca7..9513a18f105 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,13 +1,12 @@ -- ci_commit = @merge_request.ci_commit -- if ci_commit - - status = ci_commit.status +- if @merge_request.ci_commit + - status = @merge_request.ci_commit.status .mr-widget-heading .ci_widget{class: "ci-#{status}"} - = ci_status_icon(ci_commit) + = ci_status_icon(@merge_request.ci_commit) %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_status_path(ci_commit) + = link_to "View build details", ci_status_path(@merge_request.ci_commit) - elsif @merge_request.has_ci? - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 279e2ec91f8..f7d872aa455 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -4,8 +4,7 @@ = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container .accept-action - - ci_commit = @merge_request.ci_commit - - if ci_commit && ci_commit.active? + - if @merge_request.ci_commit && @merge_request.ci_commit.active? = f.button class: "btn btn-create btn-grouped merge_when_build_succeeds", name: "merge_when_build_succeeds" do Merge When Build Succeeds = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index 43ba49c5a5e..9788d68b54e 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -1,27 +1,27 @@ %h4 - Approved by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} + Set by #{link_to_member(@project, @merge_request.merge_user, avatar: true)} to be merged automatically when #{link_to "the build", ci_status_path(@merge_request.ci_commit)} succeeds. %div - - source_branch_removed = @merge_request.merge_params["should_remove_source_branch"].present? - - if source_branch_removed + - should_remove_source_branch = @merge_request.merge_params["should_remove_source_branch"].present? + %p = succeed '.' do The changes will be merged into %span.label-branch= @merge_request.target_branch - The source branch will be removed. - - else - %p - = succeed '.' do - The changes will be merged into - %span.label-branch= @merge_request.target_branch + - if should_remove_source_branch + The source branch will be removed. + - else The source branch will not be removed. - - if (@merge_request.can_remove_source_branch?(current_user) && !source_branch_removed) || @merge_request.can_cancel_merge_when_build_succeeds?(current_user) + - remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch + - user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user) + - if remove_source_branch_button || user_can_cancel_automatic_merge .clearfix.prepend-top-10 - - if @merge_request.can_remove_source_branch?(current_user) && !source_branch_removed + - if remove_source_branch_button = link_to merge_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, merge_when_build_succeeds: true, should_remove_source_branch: true), remote: true, method: :post, class: "btn btn-grouped btn-primary btn-sm remove_source_branch" do = icon('times') Remove Source Branch When Merged - - if @merge_request.can_cancel_merge_when_build_succeeds?(current_user) + + - if user_can_cancel_automatic_merge = link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-warning btn-sm" do Cancel Automatic Merge diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 729a49c9f72..5b4d7f41bc4 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -65,6 +65,11 @@ FactoryGirl.define do target_branch "master" end + trait :merge_when_build_succeeds do + merge_when_build_succeeds true + merge_user author + end + factory :closed_merge_request, traits: [:closed] factory :reopened_merge_request, traits: [:reopened] factory :merge_request_with_diffs, traits: [:with_diffs] diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index b25a3f05e29..2e64e903d1e 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -1,4 +1,6 @@ require 'spec_helper' +# rubocop:disable Lint/UselessAssignment +# As rubocop doesn't see a need for both `ci_commit` and `ci_build` feature 'Merge When Build Succeeds', feature: true, js: true do let(:user) { create(:user) } @@ -32,16 +34,20 @@ feature 'Merge When Build Succeeds', feature: true, js: true do it 'activates Merge When Build Succeeds feature' do expect(page).to have_link "Cancel Automatic Merge" - expect(page).to have_content "Approved by #{user.name} to be merged automatically when the build succeeds." + expect(page).to have_content "Set by #{user.name} to be merged automatically when the build succeeds." expect(page).to have_content "The source branch will not be removed." + + visit_merge_request(merge_request) # Needed to refresh the page + expect(page).to have_content /Enabled an automatic merge when the build for [0-9a-f]{8} succeeds/i end end end context 'When it is enabled' do - # No clue how, but push a new commit to the branch - let(:merge_request) { create(:merge_request_with_diffs, source_project: project, # source_branch: "mepmep", - author: user, title: "Bug NS-04", merge_when_build_succeeds: true) } + let(:merge_request) do + create(:merge_request_with_diffs, source_project: project, author: user, + merge_user: user, title: "MepMep", merge_when_build_succeeds: true) + end before do merge_request.source_project.team << [user, :master] @@ -60,10 +66,16 @@ feature 'Merge When Build Succeeds', feature: true, js: true do click_link "Cancel Automatic Merge" expect(page).to have_button "Merge When Build Succeeds" + + visit_merge_request(merge_request) # Needed to refresh the page + expect(page).to have_content "Cancelled the automatic merge" end it "allows the user to remove the source branch" do expect(page).to have_link "Remove Source Branch When Merged" + + click_link "Remove Source Branch When Merged" + expect(page).to have_content "The source branch will be removed" end end @@ -78,3 +90,4 @@ feature 'Merge When Build Succeeds', feature: true, js: true do visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) end end +# rubocop:enable Lint/UselessAssignment diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index c7a9765825e..63e1cd1fb92 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -48,6 +48,24 @@ describe MergeRequest do describe 'validation' do it { is_expected.to validate_presence_of(:target_branch) } it { is_expected.to validate_presence_of(:source_branch) } + + context "Validation of merge user with Merge When Build succeeds" do + it "allows user to be nil when the feature is disabled" do + expect(subject).to be_valid + end + + it "is invalid without merge user" do + subject.merge_when_build_succeeds = true + expect(subject).not_to be_valid + end + + it "is valid with merge user" do + subject.merge_when_build_succeeds = true + subject.merge_user = build(:user) + + expect(subject).to be_valid + end + end end describe 'respond to' do @@ -175,31 +193,41 @@ describe MergeRequest do end describe '#can_remove_source_branch' do - let(:user) { build(:user)} + let(:user) { create(:user) } + let(:user2) { create(:user) } before do subject.source_project.team << [user, :master] - end - it "cant be merged when its a a protected branch" do - subject.source_project.protected_branches = []; + subject.source_branch = "feature" + subject.target_branch = "master" + subject.save! + end + it "can't be removed when its a protected branch" do + allow(subject.source_project).to receive(:protected_branch?).and_return(true) expect(subject.can_remove_source_branch?(user)).to be_falsey end it "cant remove a root ref" do - subject.source_branch = "master"; + subject.source_branch = "master" + subject.target_branch = "feature" expect(subject.can_remove_source_branch?(user)).to be_falsey end - it "is truthy in all other cases" do - expect(subject.can_remove_source_branch?(user)) + it "is unable to remove the source branch for a project the user cannot push to" do + expect(subject.can_remove_source_branch?(user2)).to be_falsey + end + + it "is can be removed in all other cases" do + expect(subject.can_remove_source_branch?(user)).to be_truthy end end describe "#reset_merge_when_build_succeeds" do - let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true } + let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user) } + it "sets the item to false" do merge_if_green.reset_merge_when_build_succeeds merge_if_green.reload diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 91ae2059e95..6b7a066ac40 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -303,7 +303,7 @@ describe API::API, api: true do end describe "PUT /projects/:id/merge_request/:merge_request_id/merge" do - let (:ci_commit) { create(:ci_commit_without_jobs) } + let(:ci_commit) { create(:ci_commit_without_jobs) } it "should return merge_request in case of success" do put api("/projects/#{project.id}/merge_request/#{merge_request.id}/merge", user) diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index 8638539173b..a62d88cde86 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -3,37 +3,38 @@ require 'spec_helper' describe MergeRequests::MergeWhenBuildSucceedsService do let(:user) { create(:user) } let(:merge_request) { create(:merge_request) } - let(:mr_merge_if_green_enabled) { create(:merge_request, merge_when_build_succeeds: true, - source_branch: "source_branch", target_branch: project.default_branch, - source_project: project, target_project: project, state: "opened") } - let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch) } + + let(:mr_merge_if_green_enabled) do + create(:merge_request, merge_when_build_succeeds: true, merge_user: user, + source_branch: "source_branch", target_branch: project.default_branch, + source_project: project, target_project: project, state: "opened") + end + let(:project) { create(:project) } + let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, gl_project: project) } let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') } - before do - project.ci_commits = [ci_commit] - project.save! - end describe "#execute" do context 'first time enabling' do before do allow(merge_request).to receive(:ci_commit).and_return(ci_commit) + service.execute(merge_request) end it 'sets the params, merge_user, and flag' do - service.execute(merge_request) - expect(merge_request).to be_valid expect(merge_request.merge_when_build_succeeds).to be_truthy expect(merge_request.merge_params).to eq commit_message: 'Awesome message' expect(merge_request.merge_user).to be user + end + it 'creates a system note' do note = merge_request.notes.last - expect(note.note).to include 'Enabled an automatic merge when the build for' + expect(note.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-z]{8}/ end end - context 'allready approved' do + context 'already approved' do let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, new_key: true) } let(:build) { create(:ci_build, ref: mr_merge_if_green_enabled.source_branch) } @@ -74,5 +75,10 @@ describe MergeRequests::MergeWhenBuildSucceedsService do expect(mr_merge_if_green_enabled.merge_params).to eq({}) expect(mr_merge_if_green_enabled.merge_user).to be nil end + + it 'Posts a system note' do + note = mr_merge_if_green_enabled.notes.last + expect(note.note).to include 'Cancelled the automatic merge' + end end end diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 18b2659c1f6..9a8174f95fd 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -18,7 +18,8 @@ describe MergeRequests::RefreshService do source_branch: 'master', target_branch: 'feature', target_project: @project, - merge_when_build_succeeds: true) + merge_when_build_succeeds: true, + merge_user: @user) @fork_merge_request = create(:merge_request, source_project: @fork_project, diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 5d41a5cdc69..333035f2d2c 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -208,20 +208,20 @@ describe SystemNoteService do end describe '.merge_when_build_succeeds' do - let(:ci_commit) { create :ci_commit_without_jobs } + let(:ci_commit) { build :ci_commit_without_jobs } let(:noteable) { create :merge_request } - subject { described_class.merge_when_build_succeeds(noteable, project, author, ci_commit) } + subject { described_class.merge_when_build_succeeds(noteable, project, author, noteable.last_commit) } it_behaves_like 'a system note' it "posts the Merge When Build Succeeds system note" do - expect(subject.note).to eq "Enabled an automatic merge when the build for 97de212e80737a608d939f648d959671fb0a0142 succeeds" + expect(subject.note).to match /Enabled an automatic merge when the build for (\w+\/\w+@)?[0-9a-f]{40} succeeds/ end end describe '.cancel_merge_when_build_succeeds' do - let(:ci_commit) { create :ci_commit_without_jobs } + let(:ci_commit) { build :ci_commit_without_jobs } let(:noteable) { create :merge_request } subject { described_class.cancel_merge_when_build_succeeds(noteable, project, author) } @@ -229,7 +229,7 @@ describe SystemNoteService do it_behaves_like 'a system note' it "posts the Merge When Build Succeeds system note" do - expect(subject.note).to eq "Canceled the automatic merge" + expect(subject.note).to eq "Cancelled the automatic merge" end end -- cgit v1.2.1 From 1c53dc28b505f2853750ed4ea8b954385c5bf598 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 2 Dec 2015 19:02:15 -0500 Subject: Notify user if they cannot create projects --- app/assets/javascripts/user.js.coffee | 6 ++++++ app/assets/stylesheets/pages/projects.scss | 2 +- app/controllers/profiles_controller.rb | 1 + app/views/dashboard/_projects_head.html.haml | 3 +++ app/views/shared/_project_limit.html.haml | 8 ++++++++ db/migrate/20151203162133_add_hide_project_limit_to_users.rb | 5 +++++ db/schema.rb | 3 ++- 7 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 app/views/shared/_project_limit.html.haml create mode 100644 db/migrate/20151203162133_add_hide_project_limit_to_users.rb diff --git a/app/assets/javascripts/user.js.coffee b/app/assets/javascripts/user.js.coffee index d0d81f96921..ec4271b092c 100644 --- a/app/assets/javascripts/user.js.coffee +++ b/app/assets/javascripts/user.js.coffee @@ -2,3 +2,9 @@ class @User constructor: -> $('.profile-groups-avatars').tooltip("placement": "top") new ProjectsList() + + $('.hide-project-limit-message').on 'click', (e) -> + path = '/' + $.cookie('hide_project_limit_message', 'false', { path: path }) + $(@).parents('.project-limit-message').remove() + e.preventDefault() diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 352f0ba2781..2ded32dba12 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -5,7 +5,7 @@ font-weight: normal; } } -.no-ssh-key-message { +.no-ssh-key-message, .project-limit-message { background-color: #f28d35; margin-bottom: 16px; } diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 8da7b4d50ea..28803164fcf 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -70,6 +70,7 @@ class ProfilesController < Profiles::ApplicationController :email, :hide_no_password, :hide_no_ssh_key, + :hide_project_limit, :linkedin, :location, :name, diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index ed480b8caf8..991e67b1cd3 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -1,3 +1,6 @@ += content_for :flash_message do + = render 'shared/project_limit' + %ul.center-top-menu = nav_link(path: ['projects#index', 'root#index']) do = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do diff --git a/app/views/shared/_project_limit.html.haml b/app/views/shared/_project_limit.html.haml new file mode 100644 index 00000000000..960ff00b49d --- /dev/null +++ b/app/views/shared/_project_limit.html.haml @@ -0,0 +1,8 @@ +- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project? + .project-limit-message.alert.alert-warning.hidden-xs + You won't be able to create new projects because you have reached your project limit. + + .pull-right + = link_to "Don't show again", profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link' + | + = link_to 'Remind later', '#', class: 'hide-project-limit-message alert-link' diff --git a/db/migrate/20151203162133_add_hide_project_limit_to_users.rb b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb new file mode 100644 index 00000000000..6ffadfa1894 --- /dev/null +++ b/db/migrate/20151203162133_add_hide_project_limit_to_users.rb @@ -0,0 +1,5 @@ +class AddHideProjectLimitToUsers < ActiveRecord::Migration + def change + add_column :users, :hide_project_limit, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index fbcb711e569..fb59e187625 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: 20151118162244) do +ActiveRecord::Schema.define(version: 20151203162133) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -814,6 +814,7 @@ ActiveRecord::Schema.define(version: 20151118162244) do t.integer "project_view", default: 0 t.integer "consumed_timestep" t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree -- cgit v1.2.1 From 176d6e2a8ff97a33d533495aa3a2775dbb87284f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 5 Dec 2015 22:09:52 +0100 Subject: Refactor note awards to reuse `emoji_pattern` and improve validator --- app/models/note.rb | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 03640be7c93..2bee19479c5 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -39,9 +39,11 @@ class Note < ActiveRecord::Base delegate :name, to: :project, prefix: true delegate :name, :email, to: :author, prefix: true + before_validation :set_award! + validates :note, :project, presence: true validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } - validates :note, format: { with: /\A[-_+[:alnum:]]*\z/ }, if: -> (n){ n.is_award } + validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award } validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } @@ -72,7 +74,6 @@ class Note < ActiveRecord::Base serialize :st_diff before_create :set_diff, if: ->(n) { n.line_code.present? } - before_validation :set_award! class << self def discussions_from_notes(notes) @@ -351,36 +352,31 @@ class Note < ActiveRecord::Base !system? end - # Checks if note is an award added from an issue comment. + # Checks if note is an award added as a comment # - # If note is an award, this method sets is_award to true, - # and changes note content to award-emoji name. - # - # Awards are only supported for issue comments. + # If note is an award, this method sets is_award to true + # and changes content of the note to award name. # # Method is executed as a before_validation callback. # def set_award! - return unless supports_awards? && contains_emoji_only? - + return unless awards_supported? && contains_emoji_only? self.is_award = true self.note = award_emoji_name end - def supports_awards? - noteable.kind_of?(Issue) || - noteable.is_a?(MergeRequest) - end - private + def awards_supported? + noteable.kind_of?(Issue) || noteable.is_a?(MergeRequest) + end + def contains_emoji_only? - (note =~ /\A:[-_+[:alnum:]]*:\s?\z/) ? true : false + emoji_only_pattern = /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/ + (note =~ emoji_only_pattern) ? true : false end def award_emoji_name - return nil unless contains_emoji_only? - - note.match(/\A:([-_+[:alnum:]]*):\s?/)[1] + note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] end end -- cgit v1.2.1 From bfe91b692a89f7a5ee8a0b044fabf5ec397b2904 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 5 Dec 2015 22:18:13 +0100 Subject: Remove space before exclamation mark in award alert [ci skip] --- app/assets/javascripts/notes.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 4f559e86378..dd6cbcfc70b 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -113,7 +113,7 @@ class @Notes renderNote: (note) -> unless note.valid if note.award - flash = new Flash('You have already used this award emoji !', 'alert') + flash = new Flash('You have already used this award emoji!', 'alert') flash.pin() return -- cgit v1.2.1 From 0ba1efbb05fb3ed2cbbaf646b7fae5b9098c3021 Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Sat, 5 Dec 2015 14:24:56 -0800 Subject: Link to the gitlab-ci.yml file of GitLab itself. --- doc/ci/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ci/README.md b/doc/ci/README.md index 97325069ceb..1746ea6dd4f 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -12,6 +12,7 @@ ### Examples ++ [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) + [Test and deploy Ruby applications to Heroku](examples/test-and-deploy-ruby-application-to-heroku.md) + [Test and deploy Python applications to Heroku](examples/test-and-deploy-python-application-to-heroku.md) + [Test Clojure applications](examples/test-clojure-application.md) -- cgit v1.2.1 From 6ee43ada3a68407affd9e73ff4f1d5f9665da2d4 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 5 Dec 2015 17:49:32 -0500 Subject: Bump gitlab_emoji to ~> 0.2.0 --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 67640bb9ae0..efcfcb10a5a 100644 --- a/Gemfile +++ b/Gemfile @@ -193,7 +193,7 @@ gem 'jquery-turbolinks', '~> 2.1.0' gem 'addressable', '~> 2.3.8' gem 'bootstrap-sass', '~> 3.0' gem 'font-awesome-rails', '~> 4.2' -gem 'gitlab_emoji', '~> 0.1' +gem 'gitlab_emoji', '~> 0.2.0' gem 'gon', '~> 6.0.1' gem 'jquery-atwho-rails', '~> 1.3.2' gem 'jquery-rails', '~> 3.1.3' diff --git a/Gemfile.lock b/Gemfile.lock index cd1855758b2..7eaa0d2c841 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -864,7 +864,7 @@ DEPENDENCIES github-linguist (~> 4.7.0) github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) - gitlab_emoji (~> 0.1) + gitlab_emoji (~> 0.2.0) gitlab_git (~> 7.2.20) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) -- cgit v1.2.1 From ee134d09e7cedb57cd021314d5de459a4adf3d4d Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Sat, 5 Dec 2015 15:06:32 -0800 Subject: Move release cycle comments to the documentation. --- README.md | 2 +- doc/release/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52e2d977620..4cc9b350635 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab ## GitLab release cycle -Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). +For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/). ## Upgrading diff --git a/doc/release/README.md b/doc/release/README.md index 1342b90f3b3..52eca7c02a6 100644 --- a/doc/release/README.md +++ b/doc/release/README.md @@ -1,4 +1,8 @@ -GitLab has the following updates: +## Release cycle + +Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases are published when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). Features that will likely be in the next releases can be found on the [direction page](https://about.gitlab.com/direction/). + +## Release process documentation - [Monthly release](monthly.md), every month on the 22nd. - [Patch release](patch.md), if there are serious regressions. -- cgit v1.2.1 From caa6851bf5a65e454b702104a2895e63e368a21a Mon Sep 17 00:00:00 2001 From: Anton Baklanov Date: Sun, 29 Nov 2015 22:42:54 +0200 Subject: Fixed duplicated issue note email notifications. Fixes #2560 --- CHANGELOG | 1 + app/services/notification_service.rb | 1 + spec/services/notification_service_spec.rb | 9 ++++++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7b2f1528656..99c5fdd4d07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.2.2 - Prevent "413 Request entity too large" errors when pushing large files with LFS - Fix invalid links within projects dashboard header - Make current user the first user in assignee dropdown in issues detail page (Stan Hu) + - Fix: duplicate email notifications on issue comments v 8.2.1 - Forcefully update builds that didn't want to update with state machine diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 388a4defb26..bdf7b3ad2bb 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -145,6 +145,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, note.noteable) recipients.delete(note.author) + recipients = recipients.uniq # build notify method like 'note_commit_email' notify_method = "note_#{note.noteable_type.underscore}_email".to_sym diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index a4e2b2953cc..35fa412ed80 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -45,6 +45,7 @@ describe NotificationService do project.team << [issue.author, :master] project.team << [issue.assignee, :master] project.team << [note.author, :master] + create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@subscribed_participant cc this guy') end describe :new_note do @@ -60,6 +61,7 @@ describe NotificationService do should_email(note.noteable.assignee) should_email(@u_mentioned) should_email(@subscriber) + should_email(@subscribed_participant) should_not_email(note.author) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -381,18 +383,19 @@ describe NotificationService do def add_users_with_subscription(project, issuable) @subscriber = create :user @unsubscriber = create :user + @subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: Notification::N_PARTICIPATING) + project.team << [@subscribed_participant, :master] project.team << [@subscriber, :master] project.team << [@unsubscriber, :master] issuable.subscriptions.create(user: @subscriber, subscribed: true) + issuable.subscriptions.create(user: @subscribed_participant, subscribed: true) issuable.subscriptions.create(user: @unsubscriber, subscribed: false) end def sent_to_user?(user) - ActionMailer::Base.deliveries.any? do |message| - message.to.include?(user.email) - end + ActionMailer::Base.deliveries.map(&:to).flatten.count(user.email) == 1 end def should_email(user) -- cgit v1.2.1 From 1c4213acd5dde6ce44a70b79dd766e9e7f8b59b4 Mon Sep 17 00:00:00 2001 From: Vyacheslav Stetskevych Date: Sun, 6 Dec 2015 03:10:29 +0200 Subject: Fix gitlab-ssl nginx config to work when multiple server_names are served over https --- lib/support/nginx/gitlab-ssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 016f7a536fb..79fe1474821 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -56,7 +56,7 @@ server { listen [::]:80 ipv6only=on default_server; server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com server_tokens off; ## Don't show the nginx version number, a security best practice - return 301 https://$server_name$request_uri; + return 301 https://$http_host$request_uri; access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; } -- cgit v1.2.1 From 631a30276e30354cfde6b759527abbb26ff6cf96 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 5 Dec 2015 17:36:19 -0800 Subject: Fix API setting of 'public' attribute to false will make a project private Closes #3864 --- CHANGELOG | 1 + lib/api/projects.rb | 8 ++++++-- spec/requests/api/projects_spec.rb | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7b2f1528656..65be6b05478 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.3.0 (unreleased) + - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 2b4ada6e2eb..6928fe0eb9d 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -7,8 +7,12 @@ module API helpers do def map_public_to_visibility_level(attrs) publik = attrs.delete(:public) - publik = parse_boolean(publik) - attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true + if publik.present? && !attrs[:visibility_level].present? + publik = parse_boolean(publik) + # Since setting the public attribute to private could mean either + # private or internal, use the more conservative option, private. + attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE + end attrs end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index c59ee7af8ab..24b765f4979 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -742,6 +742,18 @@ describe API::API, api: true do end end + it 'should update visibility_level from public to private' do + project3.update_attributes({ visibility_level: Gitlab::VisibilityLevel::PUBLIC }) + + project_param = { public: false } + put api("/projects/#{project3.id}", user), project_param + expect(response.status).to eq(200) + project_param.each_pair do |k, v| + expect(json_response[k.to_s]).to eq(v) + end + expect(json_response['visibility_level']).to eq(Gitlab::VisibilityLevel::PRIVATE) + end + it 'should not update name to existing name' do project_param = { name: project3.name } put api("/projects/#{project.id}", user), project_param -- cgit v1.2.1 From 88217029fc37e972fd84aec7eb9a77247a9532bf Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Sun, 6 Dec 2015 20:48:04 +0100 Subject: When rendering the clone page, check user profile to decide default clone protocol. If the user has uploaded SSH-keys, use SSH; otherwise, use http(s). Close #3504. --- app/helpers/projects_helper.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 48729e5260e..22db8d860e5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -175,11 +175,21 @@ module ProjectsHelper end def default_url_to_repo(project = @project) - current_user ? project.url_to_repo : project.http_url_to_repo + if default_clone_protocol == "ssh" + project.ssh_url_to_repo + else + project.http_url_to_repo + end end def default_clone_protocol - current_user ? "ssh" : "http" + if !current_user + "http" + elsif current_user.require_ssh_key? + "http" + else + "ssh" + end end def project_last_activity(project) -- cgit v1.2.1 From 46278ec7ba7c618acaf7381ad466742ce84e33db Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 09:59:04 +0100 Subject: Button fix --- app/services/system_note_service.rb | 2 +- .../merge_requests/widget/open/_accept.html.haml | 33 +++++++++++++++++++--- .../merge_when_build_succeeds_spec.rb | 2 +- spec/models/merge_request_spec.rb | 2 +- .../merge_when_build_succeeds_service_spec.rb | 2 +- spec/services/system_note_service_spec.rb | 2 +- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index f84e480ca9c..6d15a49145d 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -139,7 +139,7 @@ class SystemNoteService # Called when 'merge when build succeeds' is canceled def self.cancel_merge_when_build_succeeds(noteable, project, author) - body = "Cancelled the automatic merge" + body = "Canceled the automatic merge" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index f7d872aa455..c2badf342db 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -5,10 +5,22 @@ .accept-merge-holder.clearfix.js-toggle-container .accept-action - if @merge_request.ci_commit && @merge_request.ci_commit.active? - = f.button class: "btn btn-create btn-grouped merge_when_build_succeeds", name: "merge_when_build_succeeds" do - Merge When Build Succeeds - = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do - Accept Merge Request Now + %span.btn-group + = link_to "#", class: "btn btn-create merge_when_build_succeeds" do + Merge When Build Succeeds + %a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %span.caret + %span.sr-only + Select Merge Moment + %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } + %li + = link_to "#", class: "merge_when_build_succeeds" do + = icon('check fw') + Merge When Build Succeeds + %li + = link_to "#", class: "accept_merge_request" do + = icon('warning fw') + Accept Merge Request Now - else = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do Accept Merge Request @@ -26,6 +38,8 @@ text: @merge_request.merge_commit_message, rows: 14, hint: true + = hidden_field_tag :merge_when_build_succeeds, "" + :javascript $('.accept_merge_request').on('click', function() { $(this).html(" Merge in progress"); @@ -34,3 +48,14 @@ $('.accept-mr-form').on('ajax:send', function() { $(".accept-mr-form :input").disable(); }); + + $('a.accept_merge_request').on('click', function(e) { + e.preventDefault(); + $(this).closest("form").submit(); + }); + + $('a.merge_when_build_succeeds').on('click', function(e) { + e.preventDefault(); + $("#merge_when_build_succeeds").val("1"); + $(this).closest("form").submit(); + }); diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index 2e64e903d1e..b5f319f2040 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -68,7 +68,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do expect(page).to have_button "Merge When Build Succeeds" visit_merge_request(merge_request) # Needed to refresh the page - expect(page).to have_content "Cancelled the automatic merge" + expect(page).to have_content "Canceled the automatic merge" end it "allows the user to remove the source branch" do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 63e1cd1fb92..33acfa37fea 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -192,7 +192,7 @@ describe MergeRequest do end end - describe '#can_remove_source_branch' do + describe '#can_remove_source_branch?' do let(:user) { create(:user) } let(:user2) { create(:user) } diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index a62d88cde86..188fda6211f 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -78,7 +78,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do it 'Posts a system note' do note = mr_merge_if_green_enabled.notes.last - expect(note.note).to include 'Cancelled the automatic merge' + expect(note.note).to include 'Canceled the automatic merge' end end end diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 333035f2d2c..15173cee0a2 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -229,7 +229,7 @@ describe SystemNoteService do it_behaves_like 'a system note' it "posts the Merge When Build Succeeds system note" do - expect(subject.note).to eq "Cancelled the automatic merge" + expect(subject.note).to eq "Canceled the automatic merge" end end -- cgit v1.2.1 From cfc95b828724b0d7cb07254091e675d6d8093dba Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 10:14:37 +0100 Subject: Fix schema --- db/schema.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index fb59e187625..94b87040d88 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -476,9 +476,9 @@ ActiveRecord::Schema.define(version: 20151203162133) do add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" t.string "title" @@ -487,13 +487,16 @@ ActiveRecord::Schema.define(version: 20151203162133) do t.integer "milestone_id" t.string "state" t.string "merge_status" - t.integer "target_project_id", null: false + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" t.string "merge_error" + t.text "merge_params" + t.boolean "merge_when_build_succeeds", default: false, null: false + t.integer "merge_user_id" end add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree -- cgit v1.2.1 From 900d3a09a73b953e3271a233aa6fc4937ac9d59c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20St=C3=B6ckler?= Date: Mon, 7 Dec 2015 10:28:47 +0100 Subject: Fix typos in integration docs --- doc/integration/bitbucket.md | 2 +- doc/integration/crowd.md | 2 +- doc/integration/github.md | 2 +- doc/integration/gitlab.md | 2 +- doc/integration/google.md | 2 +- doc/integration/saml.md | 2 +- doc/integration/twitter.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index 6a0fa4ce015..63432b04432 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -30,7 +30,7 @@ Bitbucket will generate an application ID and secret key for you to use. sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/crowd.md b/doc/integration/crowd.md index 2ecc8795ac1..40d93aef2a9 100644 --- a/doc/integration/crowd.md +++ b/doc/integration/crowd.md @@ -10,7 +10,7 @@ To enable the Crowd OmniAuth provider you must register your application with Cr sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/github.md b/doc/integration/github.md index b64501c2aaa..a789d2c814f 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -32,7 +32,7 @@ GitHub will generate an application ID and secret key for you to use. sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md index 216f1f11a9b..80e3c0142a0 100644 --- a/doc/integration/gitlab.md +++ b/doc/integration/gitlab.md @@ -38,7 +38,7 @@ GitLab.com will generate an application ID and secret key for you to use. sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/google.md b/doc/integration/google.md index e1c14c7c948..91e9b2495cc 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -35,7 +35,7 @@ To enable the Google OAuth2 OmniAuth provider you must register your application sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 4aa6dbe758a..1b8c28dd0f4 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -14,7 +14,7 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index 1350c8f693c..52ed4a22339 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -37,7 +37,7 @@ To enable the Twitter OmniAuth provider you must register your application with sudo editor /etc/gitlab/gitlab.rb ``` - For instalations from source: + For installations from source: ```sh cd /home/git/gitlab -- cgit v1.2.1 From 893d08c0dc6a1eba14db7694636707f30b28a7f4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 7 Dec 2015 11:00:03 +0100 Subject: Simplify `contains_emoji_only?` method in `Note` --- app/models/note.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 2bee19479c5..239a0f77f8e 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -372,8 +372,7 @@ class Note < ActiveRecord::Base end def contains_emoji_only? - emoji_only_pattern = /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/ - (note =~ emoji_only_pattern) ? true : false + note =~ /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/ end def award_emoji_name -- cgit v1.2.1 From 6eb75ed6ad2710126b577f8093e78faff64bae1b Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 11:29:23 +0100 Subject: Fix specs --- spec/features/merge_requests/merge_when_build_succeeds_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index b5f319f2040..3a8f0a344e8 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -23,12 +23,12 @@ feature 'Merge When Build Succeeds', feature: true, js: true do end it 'displays the Merge When Build Succeeds button' do - expect(page).to have_button "Merge When Build Succeeds" + expect(page).to have_link "Merge When Build Succeeds" end context "Merge When Build succeeds enabled" do before do - click_button "Merge When Build Succeeds" + click_link "Merge When Build Succeeds" end it 'activates Merge When Build Succeeds feature' do @@ -65,7 +65,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do it 'cancels the automatic merge' do click_link "Cancel Automatic Merge" - expect(page).to have_button "Merge When Build Succeeds" + expect(page).to have_link "Merge When Build Succeeds" visit_merge_request(merge_request) # Needed to refresh the page expect(page).to have_content "Canceled the automatic merge" @@ -82,7 +82,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do context 'Build is not active' do it "should not allow for enabling" do visit_merge_request(merge_request) - expect(page).not_to have_button "Merge When Build Succeeds" + expect(page).not_to have_link "Merge When Build Succeeds" end end -- cgit v1.2.1 From 96520227ae6fc5b02b0725bc515511960bcfefed Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 11:29:48 +0100 Subject: Prevent Firefox from remembering hidden field value --- app/views/projects/merge_requests/widget/open/_accept.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index c2badf342db..0fe3af97b70 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -38,7 +38,7 @@ text: @merge_request.merge_commit_message, rows: 14, hint: true - = hidden_field_tag :merge_when_build_succeeds, "" + = hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off" :javascript $('.accept_merge_request').on('click', function() { -- cgit v1.2.1 From 4c8666e69fc64200bdd2f5069bbc8c9b22fe12ab Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 11:30:09 +0100 Subject: Fix commit message textarea position --- .../merge_requests/widget/open/_accept.html.haml | 63 +++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 0fe3af97b70..6b4395fe4dc 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -3,37 +3,38 @@ = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = hidden_field_tag :authenticity_token, form_authenticity_token .accept-merge-holder.clearfix.js-toggle-container - .accept-action - - if @merge_request.ci_commit && @merge_request.ci_commit.active? - %span.btn-group - = link_to "#", class: "btn btn-create merge_when_build_succeeds" do - Merge When Build Succeeds - %a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } - %span.caret - %span.sr-only - Select Merge Moment - %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } - %li - = link_to "#", class: "merge_when_build_succeeds" do - = icon('check fw') - Merge When Build Succeeds - %li - = link_to "#", class: "accept_merge_request" do - = icon('warning fw') - Accept Merge Request Now - - else - = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do - Accept Merge Request - - if @merge_request.can_remove_source_branch?(current_user) - .accept-control.checkbox - = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do - = check_box_tag :should_remove_source_branch - Remove source branch - .accept-control.right - = link_to "#", class: "modify-merge-commit-link js-toggle-button" do - = icon('edit') - Modify commit message - .js-toggle-content.hide.prepend-top-20 + .clearfix + .accept-action + - if @merge_request.ci_commit && @merge_request.ci_commit.active? + %span.btn-group + = link_to "#", class: "btn btn-create merge_when_build_succeeds" do + Merge When Build Succeeds + %a.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' } + %span.caret + %span.sr-only + Select Merge Moment + %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } + %li + = link_to "#", class: "merge_when_build_succeeds" do + = icon('check fw') + Merge When Build Succeeds + %li + = link_to "#", class: "accept_merge_request" do + = icon('warning fw') + Merge Immediately + - else + = f.button class: "btn btn-create btn-grouped accept_merge_request #{status_class}" do + Accept Merge Request + - if @merge_request.can_remove_source_branch?(current_user) + .accept-control.checkbox + = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do + = check_box_tag :should_remove_source_branch + Remove source branch + .accept-control.right + = link_to "#", class: "modify-merge-commit-link js-toggle-button" do + = icon('edit') + Modify commit message + .js-toggle-content.hide.prepend-top-default = render 'shared/commit_message_container', params: params, text: @merge_request.merge_commit_message, rows: 14, hint: true -- cgit v1.2.1 From f5ec1ebe2caa0d8b4ccccb671ae6a4cc99cddbe0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 12:07:13 +0100 Subject: Remove changelog entry issue number --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 882648b36a2..00a24d4317b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,7 +6,7 @@ v 8.3.0 (unreleased) - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) - Add ignore whitespace change option to commit view - Fire update hook from GitLab - - Run custom Git hooks when branch is created or deleted. #1156 + - Run custom Git hooks when branch is created or deleted. v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) -- cgit v1.2.1 From 839a8b924972d87e37085333adf1b74b41bbd26c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 12:07:40 +0100 Subject: Move changelog item --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 9c00aa7a657..2cb341d882d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,10 +11,10 @@ v 8.3.0 (unreleased) - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 + - Run custom Git hooks when branch is created or deleted. v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) - - Run custom Git hooks when branch is created or deleted. v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) -- cgit v1.2.1 From 5df2c4419c5019b5003ddfa6adb59c84c3d9910c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 7 Dec 2015 14:11:15 +0200 Subject: fox specs --- app/models/commit.rb | 37 +++++++++++++++++++++++-------------- app/models/merge_request.rb | 2 +- lib/gitlab/push_data_builder.rb | 4 +++- spec/models/commit_spec.rb | 2 +- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 912b4dedf51..fecadfeec8e 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -135,10 +135,10 @@ class Commit description.present? end - def hook_attrs + def hook_attrs(with_changed_files = false) path_with_namespace = project.path_with_namespace - { + data = { id: id, message: safe_message, timestamp: committed_date.xmlschema, @@ -146,11 +146,18 @@ class Commit author: { name: author_name, email: author_email - }, - added: repo_changes[:added], - modified: repo_changes[:modified], - removed: repo_changes[:removed] + } } + + if with_changed_files + data.merge!({ + added: repo_changes[:added], + modified: repo_changes[:modified], + removed: repo_changes[:removed] + }) + end + + data end # Discover issues should be closed when this commit is pushed to a project's @@ -205,14 +212,16 @@ class Commit def repo_changes changes = { added: [], modified: [], removed: [] } - diffs.each do |diff| - case true - when diff.deleted_file - changes[:removed] << diff.old_path - when diff.renamed_file, diff.new_file - changes[:added] << diff.new_path - else - changes[:modified] << diff.new_path + if diffs.any? + diffs.each do |diff| + case true + when diff.deleted_file + changes[:removed] << diff.old_path + when diff.renamed_file, diff.new_file + changes[:added] << diff.new_path + else + changes[:modified] << diff.new_path + end end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 1b3d6079d2c..92a82d44c76 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -291,7 +291,7 @@ class MergeRequest < ActiveRecord::Base work_in_progress: work_in_progress? } - unless last_commit.nil? + if last_commit attrs.merge!(last_commit: last_commit.hook_attrs) end diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index cdcdb02a052..5842b740e8e 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -30,7 +30,9 @@ module Gitlab # For performance purposes maximum 20 latest commits # will be passed as post receive hook data. - commit_attrs = commits_limited.map(&:hook_attrs) + commit_attrs = commits_limited.map do |commit| + commit.hook_attrs(true) + end type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index b417bc98fa7..6728722b503 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -102,7 +102,7 @@ eos end describe '#hook_attrs' do - let(:data) { commit.hook_attrs } + let(:data) { commit.hook_attrs(true) } it { expect(data).to be_a(Hash) } it { expect(data[:message]).to include('Add submodule from gitlab.com') } -- cgit v1.2.1 From e7969d6f6c743ead94b3b430b9184ad21f647337 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 11:54:07 +0100 Subject: Satisfy Douwe Maan --- CHANGELOG | 2 +- app/services/system_note_service.rb | 2 +- .../merge_requests/merge_when_build_succeeds_spec.rb | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6c2fa75899c..163a0ea54e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) -- Merge when build succeeds (Zeger-Jan van de Weg) + - Merge when build succeeds (Zeger-Jan van de Weg) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Fix 500 error when update group member permission - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 7cb9417b13e..6975b2ee55b 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -132,7 +132,7 @@ class SystemNoteService # Called when 'merge when build succeeds' is executed def self.merge_when_build_succeeds(noteable, project, author, last_commit) - body = "Enabled an automatic merge when the build for #{last_commit.to_reference} succeeds" + body = "Enabled an automatic merge when the build for #{last_commit.to_reference(project)} succeeds" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index 3a8f0a344e8..ee2fb25e9e5 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -1,6 +1,4 @@ require 'spec_helper' -# rubocop:disable Lint/UselessAssignment -# As rubocop doesn't see a need for both `ci_commit` and `ci_build` feature 'Merge When Build Succeeds', feature: true, js: true do let(:user) { create(:user) } @@ -14,10 +12,10 @@ feature 'Merge When Build Succeeds', feature: true, js: true do end context "Active build for Merge Request" do - before do - ci_commit = create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) - ci_build = create(:ci_build, commit: ci_commit) + let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_build) { create(:ci_build, commit: ci_commit) } + before do login_as user visit_merge_request(merge_request) end @@ -49,14 +47,15 @@ feature 'Merge When Build Succeeds', feature: true, js: true do merge_user: user, title: "MepMep", merge_when_build_succeeds: true) end + let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_build) { create(:ci_build, commit: ci_commit) } + before do merge_request.source_project.team << [user, :master] merge_request.source_branch = "feature" merge_request.target_branch = "master" merge_request.save! - ci_commit = create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) - ci_build = create(:ci_build, commit: ci_commit) login_as user visit_merge_request(merge_request) @@ -90,4 +89,3 @@ feature 'Merge When Build Succeeds', feature: true, js: true do visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request) end end -# rubocop:enable Lint/UselessAssignment -- cgit v1.2.1 From ff08ce9ca4bef1a4f81f7a4b323614a639efe959 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 13:45:00 +0100 Subject: Satisfy Rubocop --- app/models/global_milestone.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index 33ddb265fba..dd9f88704a6 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -127,4 +127,4 @@ class GlobalMilestone end end end -end \ No newline at end of file +end -- cgit v1.2.1 From 1a942111653e9376d6275c5bd31f23cb27f58ab5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 13:55:30 +0100 Subject: Remove pointless explanation for user tabs --- app/assets/stylesheets/framework/common.scss | 10 +++++++--- app/views/users/show.html.haml | 14 +------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index d2f491daf78..58c750bc373 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -377,10 +377,9 @@ table { .center-top-menu { @include nav-menu; text-align: center; - margin-top: 5px; - margin-bottom: $gl-padding; height: 56px; - margin-top: -$gl-padding; + margin: -$gl-padding; + margin-bottom: $gl-padding; padding-top: $gl-padding; &.no-bottom { @@ -390,6 +389,11 @@ table { &.no-top { margin-top: 0; } + + &.bottom-border { + border-bottom: 1px solid $border-color; + height: 57px; + } } .center-middle-menu { diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index e35e69bda80..a0a6e2d9810 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -73,7 +73,7 @@ .user-calendar-activities -%ul.center-top-menu.no-top.no-bottom +%ul.center-top-menu.no-top.no-bottom.bottom-border %li.active = link_to "#activity", 'data-toggle' => 'tab' do Activity @@ -92,26 +92,17 @@ .tab-content .tab-pane.active#activity - .gray-content-block.middle-block - Public activity by #{@user.name} - .content_list = spinner - if @groups.any? .tab-pane#groups - .gray-content-block.middle-block - Groups #{@user.name} is a member of - %ul.content-list - @groups.each do |group| = render 'shared/groups/group', group: group - if @contributed_projects.present? .tab-pane#contributed - .gray-content-block.middle-block - Projects #{@user.name} has contributed to - .contributed-projects = render 'shared/projects/list', projects: @contributed_projects.sort_by(&:star_count).reverse, @@ -119,9 +110,6 @@ - if @projects.present? .tab-pane#personal - .gray-content-block.middle-block - Projects owned by #{@user.name} - .personal-projects = render 'shared/projects/list', projects: @projects.sort_by(&:star_count).reverse, -- cgit v1.2.1 From a426fb1596978221510c8c78a17703658ad7d161 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 14:11:13 +0100 Subject: Update documentation about automatic issue closing --- config/gitlab.yml.example | 2 +- doc/customization/issue_closing.md | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1da42ab38f3..db378118f85 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -76,7 +76,7 @@ production: &base # This happens when the commit is pushed or merged into the default branch of a project. # When not specified the default issue_closing_pattern as specified below will be used. # Tip: you can test your closing pattern at http://rubular.com. - # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' + # issue_closing_pattern: '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' ## Default project features settings default_projects_features: diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md index 64f128f5a63..00edfc97ed9 100644 --- a/doc/customization/issue_closing.md +++ b/doc/customization/issue_closing.md @@ -1,38 +1,39 @@ # Issue closing pattern -Here's how to close multiple issues in one commit message: +When a commit or merge request resolves one or more issues, it is possible to automatically have these issues closed when the commit or merge request lands in the project's default branch. -If a commit message matches the regular expression below, all issues referenced from -the matched text will be closed. This happens when the commit is pushed or merged -into the default branch of a project. +If a commit message or merge request description contains a sentence matching the regular expression below, all issues referenced from +the matched text will be closed. This happens when the commit is pushed to a project's default branch, or when a commit or merge request is merged into there. -When not specified, the default issue_closing_pattern as shown below will be used: +When not specified, the default `issue_closing_pattern` as shown below will be used: ```bash -((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+) +((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+) ``` +Here, `%{issue_ref}` is a complex regular expression defined inside GitLab, that matches a reference to a local issue (`#123`), cross-project issue (`group/project#123`) or a link to an issue (`https://gitlab.example.com/group/project/issues/123`). + For example: ``` -git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes #22). This commit is also related to #17 and fixes #18, #19 and #23." +git commit -m "Awesome commit message (Fix #20, Fixes #21 and Closes group/otherproject#2). This commit is also related to #17 and fixes #18, #19 and https://gitlab.example.com/group/otherproject/issues/23." ``` -will close `#20`, `#21`, `#22`, `#18`, `#19` and `#23`, but `#17` won't be closed -as it does not match the pattern. It also works with multiline commit messages. +will close `#18`, `#19`, `#20`, and `#21` in the project this commit is pushed to, as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it does not match the pattern. It also works with multiline commit messages. Tip: you can test this closing pattern at [http://rubular.com][1]. Use this site to test your own patterns. +Because Rubular doesn't understand `%{issue_ref}`, you can replace this by `#\d+` in testing, which matches only local issue references like `#123`. ## Change the pattern For Omnibus installs you can change the default pattern in `/etc/gitlab/gitlab.rb`: ``` -issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' +issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?))+)' ``` -For manual installs you can customize the pattern in [gitlab.yml][0]. +For manual installs you can customize the pattern in [gitlab.yml][0] using the `issue_closing_pattern` key. -[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/40c3675372320febf5264061c9bcd63db2dfd13c/config/gitlab.yml.example#L65 -[1]: http://rubular.com/r/Xmbexed1OJ \ No newline at end of file +[0]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/gitlab.yml.example +[1]: http://rubular.com/r/Xmbexed1OJ -- cgit v1.2.1 From 3c97cbc74cf87856ed7b1af197358d4e3adb1240 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 7 Dec 2015 15:13:06 +0200 Subject: fixes after review --- app/models/commit.rb | 25 +++++++++---------------- lib/gitlab/push_data_builder.rb | 2 +- spec/models/commit_spec.rb | 2 +- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index fecadfeec8e..14883c96f5f 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -135,7 +135,7 @@ class Commit description.present? end - def hook_attrs(with_changed_files = false) + def hook_attrs(with_changed_files: false) path_with_namespace = project.path_with_namespace data = { @@ -150,11 +150,7 @@ class Commit } if with_changed_files - data.merge!({ - added: repo_changes[:added], - modified: repo_changes[:modified], - removed: repo_changes[:removed] - }) + data.merge!(repo_changes) end data @@ -212,16 +208,13 @@ class Commit def repo_changes changes = { added: [], modified: [], removed: [] } - if diffs.any? - diffs.each do |diff| - case true - when diff.deleted_file - changes[:removed] << diff.old_path - when diff.renamed_file, diff.new_file - changes[:added] << diff.new_path - else - changes[:modified] << diff.new_path - end + diffs.each do |diff| + if diff.deleted_file + changes[:removed] << diff.old_path + elsif diff.renamed_file || diff.new_file + changes[:added] << diff.new_path + else + changes[:modified] << diff.new_path end end diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index 5842b740e8e..4f9cdef3869 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -31,7 +31,7 @@ module Gitlab # For performance purposes maximum 20 latest commits # will be passed as post receive hook data. commit_attrs = commits_limited.map do |commit| - commit.hook_attrs(true) + commit.hook_attrs(with_changed_files: true) end type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push" diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 6728722b503..0b1e2bf74d0 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -102,7 +102,7 @@ eos end describe '#hook_attrs' do - let(:data) { commit.hook_attrs(true) } + let(:data) { commit.hook_attrs(with_changed_files: true) } it { expect(data).to be_a(Hash) } it { expect(data[:message]).to include('Add submodule from gitlab.com') } -- cgit v1.2.1 From e53b350cb6db7438c1a50c500b324fd87afc41c4 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 7 Dec 2015 15:03:50 +0100 Subject: Add specs for showing lfs object in UI. --- app/controllers/projects/raw_controller.rb | 24 ++++++++++++------ app/helpers/blob_helper.rb | 14 ++++++++++- app/helpers/tree_helper.rb | 4 +++ app/models/lfs_object.rb | 4 +++ app/views/projects/blob/_actions.html.haml | 6 ++--- app/views/projects/blob/_blob.html.haml | 2 +- app/views/projects/blob/_download.html.haml | 6 +---- app/views/projects/blob/show.html.haml | 2 +- app/views/projects/diffs/_file.html.haml | 4 +-- features/project/source/browse_files.feature | 6 +++++ features/steps/project/source/browse_files.rb | 26 +++++++++++++++++++ spec/controllers/projects/raw_controller_spec.rb | 32 ++++++++++++++++++++++++ spec/support/test_env.rb | 2 +- 13 files changed, 111 insertions(+), 21 deletions(-) diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index c56f432a1f1..be7d5c187fe 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -38,18 +38,28 @@ class Projects::RawController < Projects::ApplicationController type = get_blob_type send_data( - @blob.data, - type: type, - disposition: 'inline' - ) + @blob.data, + type: type, + disposition: 'inline' + ) end def send_lfs_object - lfs_object = LfsObject.find_by_oid(@blob.lfs_oid) - return nil unless lfs_object && lfs_object.file.exists? + lfs_object = find_lfs_object - if lfs_object.projects.exists?(lfs_object.storage_project(@project).id) + if lfs_object && lfs_object.project_allowed_access?(@project) send_file lfs_object.file.path, filename: @blob.name, disposition: 'attachment' + else + render_404 + end + end + + def find_lfs_object + lfs_object = LfsObject.find_by_oid(@blob.lfs_oid) + if lfs_object && lfs_object.file.exists? + lfs_object + else + nil end end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index df5f5fae23c..fa1b2522051 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -30,7 +30,7 @@ module BlobHelper nil end - if blob && blob.text? + if blob && blob.text? && !blob.lfs_pointer? text = 'Edit' after = options[:after] || '' from_mr = options[:from_merge_request_id] @@ -71,4 +71,16 @@ module BlobHelper def blob_icon(mode, name) icon("#{file_type_icon_class('file', mode, name)} fw") end + + def viewable?(blob) + blob.text? && !blob.lfs_pointer? + end + + def blob_size(blob) + if blob.lfs_pointer? + blob.lfs_size + else + blob.size + end + end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 03a49e119b8..6afa1aacc5b 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -54,6 +54,10 @@ module TreeHelper ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) end + def can_delete_or_replace?(blob) + allowed_tree_edit? && !blob.lfs_pointer? + end + def tree_breadcrumbs(tree, max_links = 2) if @path.present? part_path = "" diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index a243c7b77cc..18657c3e1c8 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -13,4 +13,8 @@ class LfsObject < ActiveRecord::Base project end end + + def project_allowed_access?(project) + projects.exists?(storage_project(project).id) + end end diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 15cd8f056f5..c31f6442f12 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,9 +1,9 @@ .btn-group.tree-btn-group - = edit_blob_link(@project, @ref, @path) unless @blob.lfs_pointer? + = edit_blob_link(@project, @ref, @path) = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-sm', target: '_blank' -# only show normal/blame view links for text files - - if @blob.text? && !@blob.lfs_pointer? + - if viewable?(@blob) - if current_page? namespace_project_blame_path(@project.namespace, @project, @id) = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id), class: 'btn btn-sm' @@ -16,7 +16,7 @@ = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.sha, @path)), class: 'btn btn-sm' -- if allowed_tree_edit? && !@blob.lfs_pointer? +- if can_delete_or_replace?(@blob) .btn-group{ role: "group" } %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index bb9e1c63413..2a3315da3db 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -29,7 +29,7 @@ %strong = blob.name %small - = number_to_human_size(blob.size) unless blob.lfs_pointer? + = number_to_human_size(blob_size(blob)) .file-actions.hidden-xs = render "actions" - if blob.lfs_pointer? diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml index 39ec6f693e2..7908fcae3de 100644 --- a/app/views/projects/blob/_download.html.haml +++ b/app/views/projects/blob/_download.html.haml @@ -4,8 +4,4 @@ %h1.light %i.fa.fa-download %h4 - - if blob.lfs_pointer? - - size = blob.lfs_size - - else - - size = blob.size - Download (#{number_to_human_size size}) + Download (#{number_to_human_size blob_size(blob)}) diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index b7276868ce6..09d6fc18e3e 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -6,7 +6,7 @@ %div#tree-holder.tree-holder = render 'blob', blob: @blob -- if allowed_tree_edit? +- if can_delete_or_replace?(@blob) = render 'projects/blob/remove' - title = "Replace #{@blob.name}" diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index ec1c665716e..f6ba64d31b5 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -25,7 +25,7 @@ = "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" .diff-controls - - if blob.text? && !blob.lfs_pointer? + - if viewable?(blob) = link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do %i.fa.fa-comments   @@ -40,7 +40,7 @@ .diff-content.diff-wrap-lines -# Skipp all non non-supported blobs - return unless blob.respond_to?('text?') - - if blob.text? && !blob.lfs_pointer? + - if viewable?(blob) - if diff_view == 'parallel' = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i - else diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index e545ea63ca8..37f99b37619 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -221,3 +221,9 @@ Feature: Project Source Browse Files Given I switch ref to fix And I visit the fix tree Then I see the commit data for a directory with a leading dot + + Scenario: I browse LFS object + Given I click on "files/lfs/lfs_object.iso" file in repo + Then I should see download link and object size + And I should not see lfs pointer details + And I should see buttons for allowed commands diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index ceab23b9a4d..2edbca9b7fe 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -305,6 +305,32 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).not_to have_content('Loading commit data...') end + step 'I click on "files/lfs/lfs_object.iso" file in repo' do + click_link 'files' + click_link "lfs" + click_link "lfs_object.iso" + end + + step 'I should see download link and object size' do + expect(page).to have_content 'Download (1.5 MB)' + end + + step 'I should not see lfs pointer details' do + expect(page).not_to have_content 'version https://git-lfs.github.com/spec/v1' + expect(page).not_to have_content 'oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897' + expect(page).not_to have_content 'size 1575078' + end + + step 'I should see buttons for allowed commands' do + expect(page).to have_content 'Raw' + expect(page).to have_content 'History' + expect(page).to have_content 'Permalink' + expect(page).not_to have_content 'Edit' + expect(page).not_to have_content 'Blame' + expect(page).not_to have_content 'Delete' + expect(page).not_to have_content 'Replace' + end + private def set_new_content diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index c114f342021..2329c246daa 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -33,5 +33,37 @@ describe Projects::RawController do expect(response.header['Content-Type']).to eq('image/jpeg') end end + + context 'lfs object' do + let(:id) { 'master/files/lfs/lfs_object.iso' } + let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') } + + context 'when project has access' do + before do + public_project.lfs_objects << lfs_object + end + + it 'serves the file' do + get(:show, + namespace_id: public_project.namespace.to_param, + project_id: public_project.to_param, + id: id) + + expect(response.status).to eq(200) + expect(response.header['Content-Type']).to eq('application/octet-stream') + end + end + + context 'when project does not have access' do + it 'does not serve the file' do + get(:show, + namespace_id: public_project.namespace.to_param, + project_id: public_project.to_param, + id: id) + + expect(response.status).to eq(404) + end + end + end end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 787670e9297..47c67475cd0 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -12,7 +12,7 @@ module TestEnv 'fix' => '48f0be4', 'improve/awesome' => '5937ac0', 'markdown' => '0ed8c6c', - 'master' => '5937ac0', + 'master' => 'be93687', "'test'" => 'e56497b', } -- cgit v1.2.1 From bf17609e2a4cc062a9bccd67e90dd6524f0389b2 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Mon, 7 Dec 2015 15:56:38 +0100 Subject: Rename blob helper, bump version of gitlab_git to 7.2.21. --- Gemfile.lock | 2 +- app/helpers/blob_helper.rb | 6 +++--- app/views/projects/blob/_actions.html.haml | 2 +- app/views/projects/diffs/_file.html.haml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cd1855758b2..e2fc414824e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -298,7 +298,7 @@ GEM posix-spawn (~> 0.3) gitlab_emoji (0.2.0) gemojione (~> 2.1) - gitlab_git (7.2.20) + gitlab_git (7.2.21) activesupport (~> 4.0) charlock_holmes (~> 0.7.3) github-linguist (~> 4.7.0) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index fa1b2522051..4a3d971f7c6 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -30,7 +30,7 @@ module BlobHelper nil end - if blob && blob.text? && !blob.lfs_pointer? + if blob_viewable?(blob) text = 'Edit' after = options[:after] || '' from_mr = options[:from_merge_request_id] @@ -72,8 +72,8 @@ module BlobHelper icon("#{file_type_icon_class('file', mode, name)} fw") end - def viewable?(blob) - blob.text? && !blob.lfs_pointer? + def blob_viewable?(blob) + blob && blob.text? && !blob.lfs_pointer? end def blob_size(blob) diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index c31f6442f12..0e54e59e953 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -3,7 +3,7 @@ = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-sm', target: '_blank' -# only show normal/blame view links for text files - - if viewable?(@blob) + - if blob_viewable?(@blob) - if current_page? namespace_project_blame_path(@project.namespace, @project, @id) = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id), class: 'btn btn-sm' diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index f6ba64d31b5..a930e9e5595 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -25,7 +25,7 @@ = "#{diff_file.diff.a_mode} → #{diff_file.diff.b_mode}" .diff-controls - - if viewable?(blob) + - if blob_viewable?(blob) = link_to '#', class: 'js-toggle-diff-comments btn btn-sm active has_tooltip', title: "Toggle comments for this file" do %i.fa.fa-comments   @@ -40,7 +40,7 @@ .diff-content.diff-wrap-lines -# Skipp all non non-supported blobs - return unless blob.respond_to?('text?') - - if viewable?(blob) + - if blob_viewable?(blob) - if diff_view == 'parallel' = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i - else -- cgit v1.2.1 From 8a0507d523293289ae4b7d61bb3fce8a873e3dcc Mon Sep 17 00:00:00 2001 From: "Michael A. Smith" Date: Mon, 7 Dec 2015 10:29:42 -0500 Subject: Update Docker Syntax --- doc/ci/docker/using_docker_images.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 64e52eba3a2..1feae62b1c7 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -190,7 +190,7 @@ This will create two service containers (MySQL and PostgreSQL). 1. Create a build container and execute script in its context: ``` -$ cat build_script | docker run -n build -i -l mysql:service-mysql -l postgres:service-postgres ruby:2.1 /bin/bash +$ docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script ``` This will create build container that has two service containers linked. The build_script is piped using STDIN to bash interpreter which executes the build script in container. -- cgit v1.2.1 From 8f817c7b08bcad23e1b047f84cc60d1748104e2a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 17:10:40 +0100 Subject: Add API group projects endpoint. --- doc/api/groups.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- lib/api/groups.rb | 12 ++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/doc/api/groups.md b/doc/api/groups.md index 0b9f6406d8d..808675d8605 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -1,6 +1,6 @@ # Groups -## List project groups +## List groups Get a list of groups. (As user: my groups, as admin: all groups) @@ -21,6 +21,70 @@ GET /groups You can search for groups by name or path, see below. + +## List a group's projects + +Get a list of projects in this group. + +``` +GET /groups/:id/projects +``` + +Parameters: + +- `archived` (optional) - if passed, limit by archived status +- `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` +- `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` +- `search` (optional) - Return list of authorized projects according to a search criteria +- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first + +```json +[ + { + "id": 4, + "description": null, + "default_branch": "master", + "public": false, + "visibility_level": 0, + "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", + "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", + "web_url": "http://example.com/diaspora/diaspora-client", + "tag_list": [ + "example", + "disapora client" + ], + "owner": { + "id": 3, + "name": "Diaspora", + "created_at": "2013-09-30T13: 46: 02Z" + }, + "name": "Diaspora Client", + "name_with_namespace": "Diaspora / Diaspora Client", + "path": "diaspora-client", + "path_with_namespace": "diaspora/diaspora-client", + "issues_enabled": true, + "merge_requests_enabled": true, + "builds_enabled": true, + "wiki_enabled": true, + "snippets_enabled": false, + "created_at": "2013-09-30T13: 46: 02Z", + "last_activity_at": "2013-09-30T13: 46: 02Z", + "creator_id": 3, + "namespace": { + "created_at": "2013-09-30T13: 46: 02Z", + "description": "", + "id": 3, + "name": "Diaspora", + "owner_id": 1, + "path": "diaspora", + "updated_at": "2013-09-30T13: 46: 02Z" + }, + "archived": false, + "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png" + } +] +``` + ## Details of a group Get all details of a group. @@ -186,7 +250,7 @@ To get more (up to 100), pass the following as an argument to the API call: /groups?per_page=100 ``` -And to switch pages add: +And to switch pages add: ``` /groups?per_page=100&page=2 -``` \ No newline at end of file +``` diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 024aeec2e14..1a14d870a4a 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -65,6 +65,18 @@ module API DestroyGroupService.new(group, current_user).execute end + # Get a list of projects in this group + # + # Example Request: + # GET /groups/:id/projects + get ":id/projects" do + group = find_group(params[:id]) + projects = group.projects + projects = filter_projects(projects) + projects = paginate projects + present projects, with: Entities::Project + end + # Transfer a project to the Group namespace # # Parameters: -- cgit v1.2.1 From cbcb8dbe702c0b1532327aeb2dc53caffb6e8ed9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 17:12:04 +0100 Subject: Use select2 dropdown for dashboard/group 'New X' buttons --- app/assets/javascripts/api.js.coffee | 31 ++++++++++++++++++++++ app/assets/javascripts/project_select.js.coffee | 26 ++++++++++++++++++ app/assets/stylesheets/framework/common.scss | 13 +++++++++ app/helpers/selects_helper.rb | 13 +++++++++ app/views/dashboard/issues.html.haml | 13 +-------- app/views/dashboard/merge_requests.html.haml | 13 +-------- app/views/dashboard/milestones/index.html.haml | 13 +-------- app/views/groups/issues.html.haml | 13 +-------- app/views/groups/merge_requests.html.haml | 13 +-------- .../shared/_new_project_item_select.html.haml | 20 ++++++++++++++ 10 files changed, 108 insertions(+), 60 deletions(-) create mode 100644 app/assets/javascripts/project_select.js.coffee create mode 100644 app/views/shared/_new_project_item_select.html.haml diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee index 9e5d594c861..746fa3cea87 100644 --- a/app/assets/javascripts/api.js.coffee +++ b/app/assets/javascripts/api.js.coffee @@ -2,6 +2,8 @@ groups_path: "/api/:version/groups.json" group_path: "/api/:version/groups/:id.json" namespaces_path: "/api/:version/namespaces.json" + group_projects_path: "/api/:version/groups/:id/projects.json" + projects_path: "/api/:version/projects.json" group: (group_id, callback) -> url = Api.buildUrl(Api.group_path) @@ -44,6 +46,35 @@ ).done (namespaces) -> callback(namespaces) + # Return projects list. Filtered by query + projects: (query, callback) -> + url = Api.buildUrl(Api.projects_path) + + $.ajax( + url: url + data: + private_token: gon.api_token + search: query + per_page: 20 + dataType: "json" + ).done (projects) -> + callback(projects) + + # Return group projects list. Filtered by query + groupProjects: (group_id, query, callback) -> + url = Api.buildUrl(Api.group_projects_path) + url = url.replace(':id', group_id) + + $.ajax( + url: url + data: + private_token: gon.api_token + search: query + per_page: 20 + dataType: "json" + ).done (projects) -> + callback(projects) + buildUrl: (url) -> url = gon.relative_url_root + url if gon.relative_url_root? return url.replace(':version', gon.api_version) diff --git a/app/assets/javascripts/project_select.js.coffee b/app/assets/javascripts/project_select.js.coffee new file mode 100644 index 00000000000..43b18a3da59 --- /dev/null +++ b/app/assets/javascripts/project_select.js.coffee @@ -0,0 +1,26 @@ +class @ProjectSelect + constructor: -> + $('.ajax-project-select').each (i, select) -> + @groupId = $(select).data('group-id') + + $(select).select2 + placeholder: "Search for project" + multiple: $(select).hasClass('multiselect') + minimumInputLength: 0 + query: (query) => + callback = (projects) -> + data = { results: projects } + query.callback(data) + + if @groupId + Api.groupProjects @groupId, query.term, callback + else + Api.projects query.term, callback + + id: (project) -> + project.web_url + + text: (project) -> + project.name_with_namespace + + dropdownCssClass: "ajax-project-dropdown" diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61ecd58e6c5..19b0868dfef 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -441,3 +441,16 @@ table { .alert, .progress { margin-bottom: $gl-padding; } + +.new-project-item-select-holder { + display: inline-block; + position: relative; + + .new-project-item-select { + position: absolute; + top: 0; + right: 0; + width: 250px !important; + visibility: hidden; + } +} diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb index 7e54d4d1b5b..418120a3f73 100644 --- a/app/helpers/selects_helper.rb +++ b/app/helpers/selects_helper.rb @@ -46,6 +46,19 @@ module SelectsHelper select2_tag(id, opts) end + def project_select_tag(id, opts = {}) + opts[:class] ||= '' + opts[:class] << ' ajax-project-select' + + unless opts.delete(:scope) == :all + if @group + opts['data-group-id'] = @group.id + end + end + + hidden_field_tag(id, opts[:selected], opts) + end + def select2_tag(id, opts = {}) css_class = '' css_class << 'multiselect ' if opts[:multiple] diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 829c3c83769..2d3da01178a 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -12,18 +12,7 @@ = link_to issues_dashboard_url(format: :atom, private_token: current_user.private_token), class: 'btn' do %i.fa.fa-rss - - if @projects.any? { |project| can?(current_user, :create_issue, project) } - .dropdown.inline.prepend-left-10 - %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} - %i.fa.fa-plus - New Issue - %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - - @projects.each do |project| - - if can?(current_user, :create_issue, project) - %li - = link_to new_namespace_project_issue_path(project.namespace, project) do - = project.name_with_namespace + = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue" = render 'shared/issuable/filter', type: :issues diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index 2e91c8dec8a..c5a5ec21f78 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -3,18 +3,7 @@ .project-issuable-filter .controls - - if @projects.any? { |project| can?(current_user, :create_merge_request, project) } - .dropdown.inline - %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} - %i.fa.fa-plus - New Merge Request - %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - - @projects.each do |project| - - if can?(current_user, :create_merge_request, project) - %li - = link_to new_namespace_project_merge_request_path(project.namespace, project) do - = project.name_with_namespace + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request" = render 'shared/issuable/filter', type: :merge_requests diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index 9aea75e50db..94ff259a338 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -3,18 +3,7 @@ .project-issuable-filter .controls - - if @projects.any? { |project| can?(current_user, :admin_milestone, project) } - .dropdown.inline - %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} - %i.fa.fa-plus - New Milestone - %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - - @projects.each do |project| - - if can?(current_user, :admin_milestone, project) - %li - = link_to new_namespace_project_milestone_path(project.namespace, project) do - = project.name_with_namespace + = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone" = render 'shared/milestones_filter' diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 5a9739a0cda..90ade1e1680 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -12,18 +12,7 @@ = link_to issues_group_url(@group, format: :atom, private_token: current_user.private_token), class: 'btn' do %i.fa.fa-rss - - if @projects.any? { |project| can?(current_user, :create_issue, project) } - .dropdown.inline.prepend-left-10 - %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} - %i.fa.fa-plus - New Issue - %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - - @projects.each do |project| - - if can?(current_user, :create_issue, project) - %li - = link_to new_namespace_project_issue_path(project.namespace, project) do - = project.name_with_namespace + = render 'shared/new_project_item_select', path: 'issues/new', label: "New Issue" = render 'shared/issuable/filter', type: :issues diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 95c503a3afa..f662f5a8c17 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -3,18 +3,7 @@ .project-issuable-filter .controls - - if @projects.any? { |project| can?(current_user, :create_merge_request, project) } - .dropdown.inline - %button.dropdown-toggle.btn.btn-new{type: 'button', 'data-toggle' => 'dropdown'} - %i.fa.fa-plus - New Merge Request - %b.caret - %ul.dropdown-menu.dropdown-menu-align-right - - @projects.each do |project| - - if can?(current_user, :create_merge_request, project) - %li - = link_to new_namespace_project_merge_request_path(project.namespace, project) do - = project.name_with_namespace + = render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New Merge Request" = render 'shared/issuable/filter', type: :merge_requests diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml new file mode 100644 index 00000000000..d7243b2d518 --- /dev/null +++ b/app/views/shared/_new_project_item_select.html.haml @@ -0,0 +1,20 @@ +- if @projects.any? + .prepend-left-10.new-project-item-select-holder + = project_select_tag :project_path, class: "new-project-item-select" + %a.btn.btn-new.new-project-item-select-button + = icon('plus') + = local_assigns[:label] + %b.caret + + :javascript + $('.new-project-item-select-button').on('click', function() { + $('.new-project-item-select').select2('open'); + }); + + var relativePath = '#{local_assigns[:path]}'; + + $('.new-project-item-select').on('click', function() { + window.location = $(this).val() + '/' + relativePath; + }); + + new ProjectSelect() -- cgit v1.2.1 From 6fb90a2ca37d2735cda3b904139af1d487bcf125 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 7 Dec 2015 17:24:15 +0100 Subject: Include groups in dashboard "New Milestone" select. --- app/assets/javascripts/project_select.js.coffee | 25 ++++++++++++++++------ app/views/dashboard/milestones/index.html.haml | 2 +- .../shared/_new_project_item_select.html.haml | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/project_select.js.coffee b/app/assets/javascripts/project_select.js.coffee index 43b18a3da59..0ae274f3363 100644 --- a/app/assets/javascripts/project_select.js.coffee +++ b/app/assets/javascripts/project_select.js.coffee @@ -2,25 +2,38 @@ class @ProjectSelect constructor: -> $('.ajax-project-select').each (i, select) -> @groupId = $(select).data('group-id') + @includeGroups = $(select).data('include-groups') + + placeholder = "Search for project" + placeholder += " or group" if @includeGroups $(select).select2 - placeholder: "Search for project" - multiple: $(select).hasClass('multiselect') + placeholder: placeholder minimumInputLength: 0 query: (query) => - callback = (projects) -> + finalCallback = (projects) -> data = { results: projects } query.callback(data) + if @includeGroups + projectsCallback = (projects) -> + groupsCallback = (groups) -> + data = groups.concat(projects) + finalCallback(data) + + Api.groups query.term, false, groupsCallback + else + projectsCallback = finalCallback + if @groupId - Api.groupProjects @groupId, query.term, callback + Api.groupProjects @groupId, query.term, projectsCallback else - Api.projects query.term, callback + Api.projects query.term, projectsCallback id: (project) -> project.web_url text: (project) -> - project.name_with_namespace + project.name_with_namespace || project.name dropdownCssClass: "ajax-project-dropdown" diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml index 94ff259a338..bec1692a4de 100644 --- a/app/views/dashboard/milestones/index.html.haml +++ b/app/views/dashboard/milestones/index.html.haml @@ -3,7 +3,7 @@ .project-issuable-filter .controls - = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone" + = render 'shared/new_project_item_select', path: 'milestones/new', label: "New Milestone", include_groups: true = render 'shared/milestones_filter' diff --git a/app/views/shared/_new_project_item_select.html.haml b/app/views/shared/_new_project_item_select.html.haml index d7243b2d518..c4431d66927 100644 --- a/app/views/shared/_new_project_item_select.html.haml +++ b/app/views/shared/_new_project_item_select.html.haml @@ -1,6 +1,6 @@ - if @projects.any? .prepend-left-10.new-project-item-select-holder - = project_select_tag :project_path, class: "new-project-item-select" + = project_select_tag :project_path, class: "new-project-item-select", data: { include_groups: local_assigns[:include_groups] } %a.btn.btn-new.new-project-item-select-button = icon('plus') = local_assigns[:label] -- cgit v1.2.1 From 5cc2315ee8beeed3af251e67108d86ae65d35109 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 7 Dec 2015 19:06:28 +0100 Subject: Fix migrations for postgres on test environment Signed-off-by: Dmitriy Zaporozhets --- db/migrate/20121220064453_init_schema.rb | 74 +++++++++++----------- .../20140122112253_create_merge_request_diffs.rb | 9 ++- db/migrate/20140903115954_migrate_to_new_shell.rb | 2 + 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/db/migrate/20121220064453_init_schema.rb b/db/migrate/20121220064453_init_schema.rb index 90f5eb08e8c..d7644b6847a 100644 --- a/db/migrate/20121220064453_init_schema.rb +++ b/db/migrate/20121220064453_init_schema.rb @@ -1,6 +1,6 @@ class InitSchema < ActiveRecord::Migration def up - + create_table "events", force: true do |t| t.string "target_type" t.integer "target_id" @@ -12,14 +12,14 @@ class InitSchema < ActiveRecord::Migration t.integer "action" t.integer "author_id" end - + add_index "events", ["action"], name: "index_events_on_action", using: :btree add_index "events", ["author_id"], name: "index_events_on_author_id", using: :btree add_index "events", ["created_at"], name: "index_events_on_created_at", using: :btree add_index "events", ["project_id"], name: "index_events_on_project_id", using: :btree add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree - + create_table "issues", force: true do |t| t.string "title" t.integer "assignee_id" @@ -33,7 +33,7 @@ class InitSchema < ActiveRecord::Migration t.text "description" t.integer "milestone_id" end - + add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree add_index "issues", ["author_id"], name: "index_issues_on_author_id", using: :btree add_index "issues", ["closed"], name: "index_issues_on_closed", using: :btree @@ -41,7 +41,7 @@ class InitSchema < ActiveRecord::Migration add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree add_index "issues", ["title"], name: "index_issues_on_title", using: :btree - + create_table "keys", force: true do |t| t.integer "user_id" t.datetime "created_at" @@ -51,11 +51,11 @@ class InitSchema < ActiveRecord::Migration t.string "identifier" t.integer "project_id" end - + add_index "keys", ["identifier"], name: "index_keys_on_identifier", using: :btree add_index "keys", ["project_id"], name: "index_keys_on_project_id", using: :btree add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree - + create_table "merge_requests", force: true do |t| t.string "target_branch", null: false t.string "source_branch", null: false @@ -66,13 +66,13 @@ class InitSchema < ActiveRecord::Migration t.boolean "closed", default: false, null: false t.datetime "created_at" t.datetime "updated_at" - t.text "st_commits", limit: 2147483647 - t.text "st_diffs", limit: 2147483647 + t.text "st_commits" + t.text "st_diffs" t.boolean "merged", default: false, null: false t.integer "state", default: 1, null: false t.integer "milestone_id" end - + add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["author_id"], name: "index_merge_requests_on_author_id", using: :btree add_index "merge_requests", ["closed"], name: "index_merge_requests_on_closed", using: :btree @@ -82,7 +82,7 @@ class InitSchema < ActiveRecord::Migration add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree add_index "merge_requests", ["target_branch"], name: "index_merge_requests_on_target_branch", using: :btree add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree - + create_table "milestones", force: true do |t| t.string "title", null: false t.integer "project_id", null: false @@ -92,10 +92,10 @@ class InitSchema < ActiveRecord::Migration t.datetime "created_at" t.datetime "updated_at" end - + add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree - + create_table "namespaces", force: true do |t| t.string "name", null: false t.string "path", null: false @@ -104,12 +104,12 @@ class InitSchema < ActiveRecord::Migration t.datetime "updated_at" t.string "type" end - + add_index "namespaces", ["name"], name: "index_namespaces_on_name", using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree - + create_table "notes", force: true do |t| t.text "note" t.string "noteable_type" @@ -122,13 +122,13 @@ class InitSchema < ActiveRecord::Migration t.string "commit_id" t.integer "noteable_id" end - + add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree - + create_table "projects", force: true do |t| t.string "name" t.string "path" @@ -144,17 +144,17 @@ class InitSchema < ActiveRecord::Migration t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" end - + add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["owner_id"], name: "index_projects_on_owner_id", using: :btree - + create_table "protected_branches", force: true do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at" t.datetime "updated_at" end - + create_table "services", force: true do |t| t.string "type" t.string "title" @@ -165,9 +165,9 @@ class InitSchema < ActiveRecord::Migration t.boolean "active", default: false, null: false t.string "project_url" end - + add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree - + create_table "snippets", force: true do |t| t.string "title" t.text "content" @@ -178,11 +178,11 @@ class InitSchema < ActiveRecord::Migration t.string "file_name" t.datetime "expires_at" end - + add_index "snippets", ["created_at"], name: "index_snippets_on_created_at", using: :btree add_index "snippets", ["expires_at"], name: "index_snippets_on_expires_at", using: :btree add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree - + create_table "taggings", force: true do |t| t.integer "tag_id" t.integer "taggable_id" @@ -192,14 +192,14 @@ class InitSchema < ActiveRecord::Migration t.string "context" t.datetime "created_at" end - + add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree - + create_table "tags", force: true do |t| t.string "name" end - + create_table "user_team_project_relationships", force: true do |t| t.integer "project_id" t.integer "user_team_id" @@ -207,7 +207,7 @@ class InitSchema < ActiveRecord::Migration t.datetime "created_at" t.datetime "updated_at" end - + create_table "user_team_user_relationships", force: true do |t| t.integer "user_id" t.integer "user_team_id" @@ -216,7 +216,7 @@ class InitSchema < ActiveRecord::Migration t.datetime "created_at" t.datetime "updated_at" end - + create_table "user_teams", force: true do |t| t.string "name" t.string "path" @@ -224,7 +224,7 @@ class InitSchema < ActiveRecord::Migration t.datetime "created_at" t.datetime "updated_at" end - + create_table "users", force: true do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false @@ -255,7 +255,7 @@ class InitSchema < ActiveRecord::Migration t.string "provider" t.string "username" end - + add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["blocked"], name: "index_users_on_blocked", using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree @@ -263,7 +263,7 @@ class InitSchema < ActiveRecord::Migration add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree - + create_table "users_projects", force: true do |t| t.integer "user_id", null: false t.integer "project_id", null: false @@ -271,11 +271,11 @@ class InitSchema < ActiveRecord::Migration t.datetime "updated_at" t.integer "project_access", default: 0, null: false end - + add_index "users_projects", ["project_access"], name: "index_users_projects_on_project_access", using: :btree add_index "users_projects", ["project_id"], name: "index_users_projects_on_project_id", using: :btree add_index "users_projects", ["user_id"], name: "index_users_projects_on_user_id", using: :btree - + create_table "web_hooks", force: true do |t| t.string "url" t.integer "project_id" @@ -284,7 +284,7 @@ class InitSchema < ActiveRecord::Migration t.string "type", default: "ProjectHook" t.integer "service_id" end - + create_table "wikis", force: true do |t| t.string "title" t.text "content" @@ -294,10 +294,10 @@ class InitSchema < ActiveRecord::Migration t.string "slug" t.integer "user_id" end - + add_index "wikis", ["project_id"], name: "index_wikis_on_project_id", using: :btree add_index "wikis", ["slug"], name: "index_wikis_on_slug", using: :btree - + end def down diff --git a/db/migrate/20140122112253_create_merge_request_diffs.rb b/db/migrate/20140122112253_create_merge_request_diffs.rb index ef592305a23..47bee269f9c 100644 --- a/db/migrate/20140122112253_create_merge_request_diffs.rb +++ b/db/migrate/20140122112253_create_merge_request_diffs.rb @@ -2,11 +2,16 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration def change create_table :merge_request_diffs do |t| t.string :state, null: false, default: 'collected' - t.text :st_commits, null: true, limit: 2147483647 - t.text :st_diffs, null: true, limit: 2147483647 + t.text :st_commits, null: true + t.text :st_diffs, null: true t.integer :merge_request_id, null: false t.timestamps end + + if ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/ + change_column :merge_request_diffs, :st_commits, :text, limit: 2147483647 + change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647 + end end end diff --git a/db/migrate/20140903115954_migrate_to_new_shell.rb b/db/migrate/20140903115954_migrate_to_new_shell.rb index 2d832109513..54cbe48960a 100644 --- a/db/migrate/20140903115954_migrate_to_new_shell.rb +++ b/db/migrate/20140903115954_migrate_to_new_shell.rb @@ -1,5 +1,7 @@ class MigrateToNewShell < ActiveRecord::Migration def change + return if Rails.env.test? + gitlab_shell_path = Gitlab.config.gitlab_shell.path if system("#{gitlab_shell_path}/bin/create-hooks") puts 'Repositories updated with new hooks' -- cgit v1.2.1 From 419d6fa6b19f2e2ee6429f7db453573155ca2d21 Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Mon, 7 Dec 2015 19:24:51 +0100 Subject: Simplify expression per feedback from @rspeicher --- app/helpers/projects_helper.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 22db8d860e5..d061136b7b8 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -183,9 +183,7 @@ module ProjectsHelper end def default_clone_protocol - if !current_user - "http" - elsif current_user.require_ssh_key? + if !current_user || current_user.require_ssh_key? "http" else "ssh" -- cgit v1.2.1 From 2cec90254f5753ac1a8b92931613aaa6c9f19cf7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 7 Dec 2015 19:37:05 +0100 Subject: Dont use cached collection for Repository find_branch and find_tag methods Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/models/repository.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 13207552e8e..f1ee21ec368 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ v 8.3.0 (unreleased) - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 - Run custom Git hooks when branch is created or deleted. + - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index c304955b0b3..1d43307e1e7 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -100,11 +100,11 @@ class Repository end def find_branch(name) - branches.find { |branch| branch.name == name } + raw_repository.branches.find { |branch| branch.name == name } end def find_tag(name) - tags.find { |tag| tag.name == name } + raw_repository.tags.find { |tag| tag.name == name } end def add_branch(user, branch_name, target) -- cgit v1.2.1 From 74d73bd953c0a083cbf8aaf919a7034067542382 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 7 Dec 2015 20:09:30 +0100 Subject: Fix random failing test Make sure we wait till page reloads after request was merged. Otherwise we get request running which fails next test Signed-off-by: Dmitriy Zaporozhets --- features/project/merge_requests/accept.feature | 6 ++++-- features/steps/project/merge_requests/acceptance.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature index 3e6e59a3808..9bc2b7c8eca 100644 --- a/features/project/merge_requests/accept.feature +++ b/features/project/merge_requests/accept.feature @@ -8,10 +8,12 @@ Feature: Project Merge Requests Acceptance Given I am on the Merge Request detail page When I click on "Remove source branch" option And I click on Accept Merge Request - Then I should not see the Remove Source Branch button + Then I should see merge request merged + And I should not see the Remove Source Branch button @javascript Scenario: Accepting the Merge Request without removing the source branch Given I am on the Merge Request detail page When I click on Accept Merge Request - Then I should see the Remove Source Branch button + Then I should see merge request merged + And I should see the Remove Source Branch button diff --git a/features/steps/project/merge_requests/acceptance.rb b/features/steps/project/merge_requests/acceptance.rb index 6adecaa8385..383c055c4ef 100644 --- a/features/steps/project/merge_requests/acceptance.rb +++ b/features/steps/project/merge_requests/acceptance.rb @@ -32,4 +32,8 @@ class Spinach::Features::ProjectMergeRequestsAcceptance < Spinach::FeatureSteps step 'I am signed in as a developer of the project' do login_as(@user) end + + step 'I should see merge request merged' do + expect(page).to have_content('The changes were merged into') + end end -- cgit v1.2.1 From a1d3b8d7fa61f757978e237dd95de8eeb186212c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Dec 2015 16:50:20 -0500 Subject: Fix spec failure introduced by 9d03bc6fa31f123e070bab4a58b67dbb008e75e9 --- spec/helpers/application_helper_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 0a64b70d6a6..5568f06639c 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -278,7 +278,7 @@ describe ApplicationHelper do el = element.next_element expect(el.name).to eq 'script' - expect(el.text).to include "$('.js-timeago').timeago()" + expect(el.text).to include "$('.js-timeago').last().timeago()" end it 'allows the script tag to be excluded' do -- cgit v1.2.1 From d5ea93469b4ec95916361c61876c949f60539211 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 18:45:36 -0500 Subject: Add custom UrlValidator --- app/models/application_setting.rb | 4 +-- app/models/ci/web_hook.rb | 3 +- app/models/hooks/web_hook.rb | 3 +- app/models/project.rb | 2 +- app/models/project_services/bamboo_service.rb | 7 ++--- app/models/project_services/drone_ci_service.rb | 29 ++++++++--------- .../project_services/external_wiki_service.rb | 6 ++-- app/models/project_services/teamcity_service.rb | 10 +++--- app/validators/url_validator.rb | 36 ++++++++++++++++++++++ spec/models/application_setting_spec.rb | 16 ++++++++++ 10 files changed, 79 insertions(+), 37 deletions(-) create mode 100644 app/validators/url_validator.rb diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 5ddcf3d9a0b..1880ad9f33c 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -43,12 +43,12 @@ class ApplicationSetting < ActiveRecord::Base validates :home_page_url, allow_blank: true, - format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, + url: true, if: :home_page_url_column_exist validates :after_sign_out_path, allow_blank: true, - format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } + url: true validates :admin_notification_email, allow_blank: true, diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb index 7ca16a1bde8..0dc15eb6683 100644 --- a/app/models/ci/web_hook.rb +++ b/app/models/ci/web_hook.rb @@ -20,8 +20,7 @@ module Ci # HTTParty timeout default_timeout 10 - validates :url, presence: true, - format: { with: URI::regexp(%w(http https)), message: "should be a valid url" } + validates :url, presence: true, url: true def execute(data) parsed_url = URI.parse(url) diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 2caf26cc8c9..715ec5908b7 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -31,8 +31,7 @@ class WebHook < ActiveRecord::Base # HTTParty timeout default_timeout Gitlab.config.gitlab.webhook_timeout - validates :url, presence: true, - format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } + validates :url, presence: true, url: true def execute(data, hook_name) parsed_url = URI.parse(url) diff --git a/app/models/project.rb b/app/models/project.rb index 6010770a5f2..af034a6692b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -152,7 +152,7 @@ class Project < ActiveRecord::Base validates_uniqueness_of :name, scope: :namespace_id validates_uniqueness_of :path, scope: :namespace_id validates :import_url, - format: { with: /\A#{URI.regexp(%w(ssh git http https))}\z/, message: 'should be a valid url' }, + url: { protocols: %w(ssh git http https) }, if: :external_import? validates :star_count, numericality: { greater_than_or_equal_to: 0 } validate :check_limit, on: :create diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index d31b12f539e..0a61ad96a0e 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -23,10 +23,7 @@ class BambooService < CiService prop_accessor :bamboo_url, :build_key, :username, :password - validates :bamboo_url, - presence: true, - format: { with: /\A#{URI.regexp}\z/ }, - if: :activated? + validates :bamboo_url, presence: true, url: true, if: :activated? validates :build_key, presence: true, if: :activated? validates :username, presence: true, @@ -84,7 +81,7 @@ class BambooService < CiService def supported_events %w(push) end - + def build_info(sha) url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}") diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 06c3922593c..08e5ccb3855 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -19,14 +19,11 @@ # class DroneCiService < CiService - + prop_accessor :drone_url, :token, :enable_ssl_verification - validates :drone_url, - presence: true, - format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated? - validates :token, - presence: true, - if: :activated? + + validates :drone_url, presence: true, url: true, if: :activated? + validates :token, presence: true, if: :activated? after_save :compose_service_hook, if: :activated? @@ -58,16 +55,16 @@ class DroneCiService < CiService end def merge_request_status_path(iid, sha = nil, ref = nil) - url = [drone_url, - "gitlab/#{project.namespace.path}/#{project.path}/pulls/#{iid}", + url = [drone_url, + "gitlab/#{project.namespace.path}/#{project.path}/pulls/#{iid}", "?access_token=#{token}"] URI.join(*url).to_s end def commit_status_path(sha, ref) - url = [drone_url, - "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}", + url = [drone_url, + "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}", "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"] URI.join(*url).to_s @@ -114,15 +111,15 @@ class DroneCiService < CiService end def merge_request_page(iid, sha, ref) - url = [drone_url, + url = [drone_url, "gitlab/#{project.namespace.path}/#{project.path}/redirect/pulls/#{iid}"] URI.join(*url).to_s end def commit_page(sha, ref) - url = [drone_url, - "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}", + url = [drone_url, + "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}", "?branch=#{URI::encode(ref.to_s)}"] URI.join(*url).to_s @@ -163,10 +160,10 @@ class DroneCiService < CiService end def push_valid?(data) - opened_merge_requests = project.merge_requests.opened.where(source_project_id: project.id, + opened_merge_requests = project.merge_requests.opened.where(source_project_id: project.id, source_branch: Gitlab::Git.ref_name(data[:ref])) - opened_merge_requests.empty? && data[:total_commits_count] > 0 && + opened_merge_requests.empty? && data[:total_commits_count] > 0 && !Gitlab::Git.blank_ref?(data[:after]) end diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index 9c46af7e721..74c57949b4d 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -22,10 +22,8 @@ class ExternalWikiService < Service include HTTParty prop_accessor :external_wiki_url - validates :external_wiki_url, - presence: true, - format: { with: /\A#{URI.regexp}\z/ }, - if: :activated? + + validates :external_wiki_url, presence: true, url: true, if: :activated? def title 'External Wiki' diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 0b022461250..29d4236745a 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -23,16 +23,16 @@ class TeamcityService < CiService prop_accessor :teamcity_url, :build_type, :username, :password - validates :teamcity_url, - presence: true, - format: { with: /\A#{URI.regexp}\z/ }, if: :activated? + validates :teamcity_url, presence: true, url: true, if: :activated? validates :build_type, presence: true, if: :activated? validates :username, presence: true, - if: ->(service) { service.password? }, if: :activated? + if: ->(service) { service.password? }, + if: :activated? validates :password, presence: true, - if: ->(service) { service.username? }, if: :activated? + if: ->(service) { service.username? }, + if: :activated? attr_accessor :response diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb new file mode 100644 index 00000000000..2848b9cd33d --- /dev/null +++ b/app/validators/url_validator.rb @@ -0,0 +1,36 @@ +# UrlValidator +# +# Custom validator for URLs. +# +# By default, only URLs for the HTTP(S) protocols will be considered valid. +# Provide a `:protocols` option to configure accepted protocols. +# +# Example: +# +# class User < ActiveRecord::Base +# validates :personal_url, url: true +# +# validates :ftp_url, url: { protocols: %w(ftp) } +# +# validates :git_url, url: { protocols: %w(http https ssh git) } +# end +# +class UrlValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless valid_url?(value) + record.errors.add(attribute, "must be a valid URL") + end + end + + private + + def default_options + @default_options ||= { protocols: %w(http https) } + end + + def valid_url?(value) + options = default_options.merge(self.options) + + value =~ /\A#{URI.regexp(options[:protocols])}\z/ + end +end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index dfbac7b4004..b67b84959d9 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -36,6 +36,22 @@ describe ApplicationSetting, models: true do it { expect(setting).to be_valid } + describe 'validations' do + let(:http) { 'http://example.com' } + let(:https) { 'https://example.com' } + let(:ftp) { 'ftp://example.com' } + + it { is_expected.to allow_value(nil).for(:home_page_url) } + it { is_expected.to allow_value(http).for(:home_page_url) } + it { is_expected.to allow_value(https).for(:home_page_url) } + it { is_expected.not_to allow_value(ftp).for(:home_page_url) } + + it { is_expected.to allow_value(nil).for(:after_sign_out_path) } + it { is_expected.to allow_value(http).for(:after_sign_out_path) } + it { is_expected.to allow_value(https).for(:after_sign_out_path) } + it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) } + end + context 'restricted signup domains' do it 'set single domain' do setting.restricted_signup_domains_raw = 'example.com' -- cgit v1.2.1 From b3200c8c44f2351d88d5d78d7ded3ac06001bd7c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 18:46:00 -0500 Subject: Move EmailValidator to app/validators --- app/validators/email_validator.rb | 21 +++++++++++++++++++++ lib/email_validator.rb | 21 --------------------- 2 files changed, 21 insertions(+), 21 deletions(-) create mode 100644 app/validators/email_validator.rb delete mode 100644 lib/email_validator.rb diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb new file mode 100644 index 00000000000..f509f0a5843 --- /dev/null +++ b/app/validators/email_validator.rb @@ -0,0 +1,21 @@ +# Based on https://github.com/balexand/email_validator +# +# Extended to use only strict mode with following allowed characters: +# ' - apostrophe +# +# See http://www.remote.org/jochen/mail/info/chars.html +# +class EmailValidator < ActiveModel::EachValidator + @@default_options = {} + + def self.default_options + @@default_options + end + + def validate_each(record, attribute, value) + options = @@default_options.merge(self.options) + unless value =~ /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i + record.errors.add(attribute, options[:message] || :invalid) + end + end +end diff --git a/lib/email_validator.rb b/lib/email_validator.rb deleted file mode 100644 index f509f0a5843..00000000000 --- a/lib/email_validator.rb +++ /dev/null @@ -1,21 +0,0 @@ -# Based on https://github.com/balexand/email_validator -# -# Extended to use only strict mode with following allowed characters: -# ' - apostrophe -# -# See http://www.remote.org/jochen/mail/info/chars.html -# -class EmailValidator < ActiveModel::EachValidator - @@default_options = {} - - def self.default_options - @@default_options - end - - def validate_each(record, attribute, value) - options = @@default_options.merge(self.options) - unless value =~ /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i - record.errors.add(attribute, options[:message] || :invalid) - end - end -end -- cgit v1.2.1 From e48391b813d3e5079238aa3f0662e7a46e1b4a54 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 18:53:44 -0500 Subject: Add custom ColorValidator --- app/models/broadcast_message.rb | 8 ++++---- app/models/label.rb | 4 +--- app/validators/color_validator.rb | 20 ++++++++++++++++++++ features/steps/admin/labels.rb | 2 +- features/steps/project/issues/labels.rb | 2 +- spec/models/broadcast_message_spec.rb | 15 +++++++++++++++ spec/requests/api/labels_spec.rb | 10 +++++----- 7 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 app/validators/color_validator.rb diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 05f5e979695..ad514706160 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -16,12 +16,12 @@ class BroadcastMessage < ActiveRecord::Base include Sortable - validates :message, presence: true + validates :message, presence: true validates :starts_at, presence: true - validates :ends_at, presence: true + validates :ends_at, presence: true - validates :color, format: { with: /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/ }, allow_blank: true - validates :font, format: { with: /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/ }, allow_blank: true + validates :color, allow_blank: true, color: true + validates :font, allow_blank: true, color: true def self.current where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last diff --git a/app/models/label.rb b/app/models/label.rb index bef6063fe88..220da10a6ab 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -27,9 +27,7 @@ class Label < ActiveRecord::Base has_many :label_links, dependent: :destroy has_many :issues, through: :label_links, source: :target, source_type: 'Issue' - validates :color, - format: { with: /\A#[0-9A-Fa-f]{6}\Z/ }, - allow_blank: false + validates :color, color: true, allow_blank: false validates :project, presence: true, unless: Proc.new { |service| service.template? } # Don't allow '?', '&', and ',' for label titles diff --git a/app/validators/color_validator.rb b/app/validators/color_validator.rb new file mode 100644 index 00000000000..571d0007aa2 --- /dev/null +++ b/app/validators/color_validator.rb @@ -0,0 +1,20 @@ +# ColorValidator +# +# Custom validator for web color codes. It requires the leading hash symbol and +# will accept RGB triplet or hexadecimal formats. +# +# Example: +# +# class User < ActiveRecord::Base +# validates :background_color, allow_blank: true, color: true +# end +# +class ColorValidator < ActiveModel::EachValidator + PATTERN = /\A\#[0-9A-Fa-f]{3}{1,2}+\Z/.freeze + + def validate_each(record, attribute, value) + unless value =~ PATTERN + record.errors.add(attribute, "must be a valid color code") + end + end +end diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb index 2ea5dffdc66..55ddcc25085 100644 --- a/features/steps/admin/labels.rb +++ b/features/steps/admin/labels.rb @@ -71,7 +71,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps step 'I should see label color error message' do page.within '.label-form' do - expect(page).to have_content 'Color is invalid' + expect(page).to have_content 'Color must be a valid color code' end end diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb index e273bb391b3..2ab8956867b 100644 --- a/features/steps/project/issues/labels.rb +++ b/features/steps/project/issues/labels.rb @@ -55,7 +55,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps step 'I should see label color error message' do page.within '.label-form' do - expect(page).to have_content 'Color is invalid' + expect(page).to have_content 'Color must be a valid color code' end end diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index d80748f23a4..2b325f44f64 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -20,6 +20,21 @@ describe BroadcastMessage do it { is_expected.to be_valid } + describe 'validations' do + let(:triplet) { '#000' } + let(:hex) { '#AABBCC' } + + it { is_expected.to allow_value(nil).for(:color) } + it { is_expected.to allow_value(triplet).for(:color) } + it { is_expected.to allow_value(hex).for(:color) } + it { is_expected.not_to allow_value('000').for(:color) } + + it { is_expected.to allow_value(nil).for(:font) } + it { is_expected.to allow_value(triplet).for(:font) } + it { is_expected.to allow_value(hex).for(:font) } + it { is_expected.not_to allow_value('000').for(:font) } + end + describe :current do it "should return last message if time match" do broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow) diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb index aff109a9424..667f0dbea5c 100644 --- a/spec/requests/api/labels_spec.rb +++ b/spec/requests/api/labels_spec.rb @@ -47,7 +47,7 @@ describe API::API, api: true do name: 'Foo', color: '#FFAA' expect(response.status).to eq(400) - expect(json_response['message']['color']).to eq(['is invalid']) + expect(json_response['message']['color']).to eq(['must be a valid color code']) end it 'should return 400 for too long color code' do @@ -55,7 +55,7 @@ describe API::API, api: true do name: 'Foo', color: '#FFAAFFFF' expect(response.status).to eq(400) - expect(json_response['message']['color']).to eq(['is invalid']) + expect(json_response['message']['color']).to eq(['must be a valid color code']) end it 'should return 400 for invalid name' do @@ -151,12 +151,12 @@ describe API::API, api: true do expect(json_response['message']['title']).to eq(['is invalid']) end - it 'should return 400 for invalid name' do + it 'should return 400 when color code is too short' do put api("/projects/#{project.id}/labels", user), name: 'label1', color: '#FF' expect(response.status).to eq(400) - expect(json_response['message']['color']).to eq(['is invalid']) + expect(json_response['message']['color']).to eq(['must be a valid color code']) end it 'should return 400 for too long color code' do @@ -164,7 +164,7 @@ describe API::API, api: true do name: 'Foo', color: '#FFAAFFFF' expect(response.status).to eq(400) - expect(json_response['message']['color']).to eq(['is invalid']) + expect(json_response['message']['color']).to eq(['must be a valid color code']) end end end -- cgit v1.2.1 From 96e51a0304022664c06a025f4a54c4a41c25edd2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 19:21:45 -0500 Subject: Minor EmailValidator refactor --- app/validators/email_validator.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb index f509f0a5843..b35af100803 100644 --- a/app/validators/email_validator.rb +++ b/app/validators/email_validator.rb @@ -1,3 +1,5 @@ +# EmailValidator +# # Based on https://github.com/balexand/email_validator # # Extended to use only strict mode with following allowed characters: @@ -6,15 +8,10 @@ # See http://www.remote.org/jochen/mail/info/chars.html # class EmailValidator < ActiveModel::EachValidator - @@default_options = {} - - def self.default_options - @@default_options - end + PATTERN = /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i.freeze def validate_each(record, attribute, value) - options = @@default_options.merge(self.options) - unless value =~ /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i + unless value =~ PATTERN record.errors.add(attribute, options[:message] || :invalid) end end -- cgit v1.2.1 From ad6a771dc680b52e4b46c73f20bc39340d08bf32 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 1 Dec 2015 19:30:01 -0500 Subject: Add custom LineCodeValidator --- app/models/note.rb | 2 +- app/models/sent_notification.rb | 2 +- app/validators/line_code_validator.rb | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 app/validators/line_code_validator.rb diff --git a/app/models/note.rb b/app/models/note.rb index 239a0f77f8e..8d433c57ceb 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -44,7 +44,7 @@ class Note < ActiveRecord::Base validates :note, :project, presence: true validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award } - validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true + validates :line_code, line_code: true, allow_blank: true # Attachments are deprecated and are handled by Markdown uploader validates :attachment, file_size: { maximum: :max_attachment_size } diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index d8fe65b06f6..f36eda1531b 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -21,7 +21,7 @@ class SentNotification < ActiveRecord::Base validates :reply_key, uniqueness: true validates :noteable_id, presence: true, unless: :for_commit? validates :commit_id, presence: true, if: :for_commit? - validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true + validates :line_code, line_code: true, allow_blank: true class << self def reply_key diff --git a/app/validators/line_code_validator.rb b/app/validators/line_code_validator.rb new file mode 100644 index 00000000000..ed29e5aeb67 --- /dev/null +++ b/app/validators/line_code_validator.rb @@ -0,0 +1,12 @@ +# LineCodeValidator +# +# Custom validator for GitLab line codes. +class LineCodeValidator < ActiveModel::EachValidator + PATTERN = /\A[a-z0-9]+_\d+_\d+\z/.freeze + + def validate_each(record, attribute, value) + unless value =~ PATTERN + record.errors.add(attribute, "must be a valid line code") + end + end +end -- cgit v1.2.1 From 9321d382bd5a0697e0e15a5065ec274e75541851 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Dec 2015 16:17:12 -0500 Subject: Add custom NamespaceValidator --- app/models/namespace.rb | 8 +++----- app/models/user.rb | 6 ++---- app/validators/namespace_validator.rb | 22 ++++++++++++++++++++++ spec/models/user_spec.rb | 18 +++++++++++++++++- spec/requests/api/users_spec.rb | 4 ++-- 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 app/validators/namespace_validator.rb diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 20b92e68d61..e07c676a9f3 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -30,12 +30,10 @@ class Namespace < ActiveRecord::Base validates :description, length: { within: 0..255 } validates :path, - uniqueness: { case_sensitive: false }, - presence: true, length: { within: 1..255 }, - exclusion: { in: Gitlab::Blacklist.path }, - format: { with: Gitlab::Regex.namespace_regex, - message: Gitlab::Regex.namespace_regex_message } + namespace: true, + presence: true, + uniqueness: { case_sensitive: false } delegate :name, to: :owner, allow_nil: true, prefix: true diff --git a/app/models/user.rb b/app/models/user.rb index 719b49b16fe..cfed797e725 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -148,11 +148,9 @@ class User < ActiveRecord::Base validates :bio, length: { maximum: 255 }, allow_blank: true validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 } validates :username, + namespace: true, presence: true, - uniqueness: { case_sensitive: false }, - exclusion: { in: Gitlab::Blacklist.path }, - format: { with: Gitlab::Regex.namespace_regex, - message: Gitlab::Regex.namespace_regex_message } + uniqueness: { case_sensitive: false } validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true validate :namespace_uniq, if: ->(user) { user.username_changed? } diff --git a/app/validators/namespace_validator.rb b/app/validators/namespace_validator.rb new file mode 100644 index 00000000000..4ab1706abda --- /dev/null +++ b/app/validators/namespace_validator.rb @@ -0,0 +1,22 @@ +# NamespaceValidator +# +# Custom validator for GitLab namespace values. +# +# Values are checked for formatting and exclusion from `Gitlab::Blacklist.path`. +class NamespaceValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value =~ Gitlab::Regex.namespace_regex + record.errors.add(attribute, Gitlab::Regex.namespace_regex_message) + end + + if blacklisted?(value) + record.errors.add(attribute, "#{value} is a reserved name") + end + end + + private + + def blacklisted?(value) + Gitlab::Blacklist.path.include?(value) + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4631b12faf1..a0f78d3b336 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -91,7 +91,23 @@ describe User do end describe 'validations' do - it { is_expected.to validate_presence_of(:username) } + describe 'username' do + it 'validates presence' do + expect(subject).to validate_presence_of(:username) + end + + it 'rejects blacklisted names' do + user = build(:user, username: 'dashboard') + + expect(user).not_to be_valid + expect(user.errors.values).to eq [['dashboard is a reserved name']] + end + + it 'validates uniqueness' do + expect(subject).to validate_uniqueness_of(:username) + end + end + it { is_expected.to validate_presence_of(:projects_limit) } it { is_expected.to validate_numericality_of(:projects_limit) } it { is_expected.to allow_value(0).for(:projects_limit) } diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index a9ef2fe5885..2f609c63330 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -153,7 +153,7 @@ describe API::API, api: true do expect(json_response['message']['projects_limit']). to eq(['must be greater than or equal to 0']) expect(json_response['message']['username']). - to eq([Gitlab::Regex.send(:namespace_regex_message)]) + to eq([Gitlab::Regex.namespace_regex_message]) end it "shouldn't available for non admin users" do @@ -296,7 +296,7 @@ describe API::API, api: true do expect(json_response['message']['projects_limit']). to eq(['must be greater than or equal to 0']) expect(json_response['message']['username']). - to eq([Gitlab::Regex.send(:namespace_regex_message)]) + to eq([Gitlab::Regex.namespace_regex_message]) end context "with existing user" do -- cgit v1.2.1 From 175f482c3cd584ba73c66e65aa180c1107e72913 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Dec 2015 16:17:24 -0500 Subject: Add custom NamespaceNameValidator --- app/models/namespace.rb | 6 +++--- app/validators/namespace_name_validator.rb | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 app/validators/namespace_name_validator.rb diff --git a/app/models/namespace.rb b/app/models/namespace.rb index e07c676a9f3..1c4e101cc10 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -23,10 +23,10 @@ class Namespace < ActiveRecord::Base validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, - presence: true, uniqueness: true, length: { within: 0..255 }, - format: { with: Gitlab::Regex.namespace_name_regex, - message: Gitlab::Regex.namespace_name_regex_message } + namespace_name: true, + presence: true, + uniqueness: true validates :description, length: { within: 0..255 } validates :path, diff --git a/app/validators/namespace_name_validator.rb b/app/validators/namespace_name_validator.rb new file mode 100644 index 00000000000..2e51af2982d --- /dev/null +++ b/app/validators/namespace_name_validator.rb @@ -0,0 +1,10 @@ +# NamespaceNameValidator +# +# Custom validator for GitLab namespace name strings. +class NamespaceNameValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value =~ Gitlab::Regex.namespace_name_regex + record.errors.add(attribute, Gitlab::Regex.namespace_name_regex_message) + end + end +end -- cgit v1.2.1 From 2379c8beeac600c3352e33fda0c2b4f4f39c8b84 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Dec 2015 16:29:39 -0500 Subject: Inline Gitlab::Blacklist in NamespaceValidator --- app/validators/namespace_validator.rb | 36 +++++++++++++++++++++++++++++++---- lib/gitlab/blacklist.rb | 34 --------------------------------- 2 files changed, 32 insertions(+), 38 deletions(-) delete mode 100644 lib/gitlab/blacklist.rb diff --git a/app/validators/namespace_validator.rb b/app/validators/namespace_validator.rb index 4ab1706abda..10e35ce665a 100644 --- a/app/validators/namespace_validator.rb +++ b/app/validators/namespace_validator.rb @@ -2,21 +2,49 @@ # # Custom validator for GitLab namespace values. # -# Values are checked for formatting and exclusion from `Gitlab::Blacklist.path`. +# Values are checked for formatting and exclusion from a list of reserved path +# names. class NamespaceValidator < ActiveModel::EachValidator + RESERVED = %w( + admin + all + assets + ci + dashboard + files + groups + help + hooks + issues + merge_requests + notes + profile + projects + public + repository + s + search + services + snippets + teams + u + unsubscribes + users + ).freeze + def validate_each(record, attribute, value) unless value =~ Gitlab::Regex.namespace_regex record.errors.add(attribute, Gitlab::Regex.namespace_regex_message) end - if blacklisted?(value) + if reserved?(value) record.errors.add(attribute, "#{value} is a reserved name") end end private - def blacklisted?(value) - Gitlab::Blacklist.path.include?(value) + def reserved?(value) + RESERVED.include?(value) end end diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb deleted file mode 100644 index 43145e0ee1b..00000000000 --- a/lib/gitlab/blacklist.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Gitlab - module Blacklist - extend self - - def path - %w( - admin - dashboard - files - groups - help - profile - projects - search - public - assets - u - s - teams - merge_requests - issues - users - snippets - services - repository - hooks - notes - unsubscribes - all - ci - ) - end - end -end -- cgit v1.2.1 From bdb4945dcf3c899c6a88fd42657a1b3f04baced1 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 7 Dec 2015 23:08:22 +0100 Subject: Fix random failing test - delete attachment Make sure we wait for AJAX request to finish before end test and cleanup database Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/notes.js.coffee | 4 ++-- spec/features/notes_on_merge_requests_spec.rb | 5 +++-- spec/support/wait_for_ajax.rb | 11 +++++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 spec/support/wait_for_ajax.rb diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index dd6cbcfc70b..533d00bfb0c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -369,8 +369,8 @@ class @Notes note = $(this).closest(".note") note.find(".note-attachment").remove() note.find(".note-body > .note-text").show() - note.find(".js-note-attachment-delete").hide() - note.find(".note-edit-form").hide() + note.find(".note-header").show() + note.find(".current-note-edit-form").remove() ### Called when clicking on the "reply" button for a diff line. diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index d7cb3b2e86e..f0fc6916c4d 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe 'Comments', feature: true do include RepoHelpers + include WaitForAjax describe 'On a merge request', js: true, feature: true do let!(:merge_request) { create(:merge_request) } @@ -123,8 +124,8 @@ describe 'Comments', feature: true do it 'removes the attachment div and resets the edit form' do find('.js-note-attachment-delete').click is_expected.not_to have_css('.note-attachment') - expect(find('.current-note-edit-form', visible: false)). - not_to be_visible + is_expected.not_to have_css('.current-note-edit-form') + wait_for_ajax end end end diff --git a/spec/support/wait_for_ajax.rb b/spec/support/wait_for_ajax.rb new file mode 100644 index 00000000000..692d219e9f1 --- /dev/null +++ b/spec/support/wait_for_ajax.rb @@ -0,0 +1,11 @@ +module WaitForAjax + def wait_for_ajax + Timeout.timeout(Capybara.default_wait_time) do + loop until finished_all_ajax_requests? + end + end + + def finished_all_ajax_requests? + page.evaluate_script('jQuery.active').zero? + end +end -- cgit v1.2.1 From 611912fe6870cac43fef9eea389f1229945f186a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 7 Dec 2015 19:40:46 -0500 Subject: Store the demodulized reference filter name in data attribute --- lib/gitlab/markdown/filter/reference_gatherer_filter.rb | 2 +- lib/gitlab/markdown/reference_filter.rb | 6 +++--- spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb | 14 +++++++------- .../markdown/filter/reference_gatherer_filter_spec.rb | 14 +++++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb b/lib/gitlab/markdown/filter/reference_gatherer_filter.rb index 00f983675e6..62f241b4967 100644 --- a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb +++ b/lib/gitlab/markdown/filter/reference_gatherer_filter.rb @@ -31,7 +31,7 @@ module Gitlab return unless node.has_attribute?('data-reference-filter') reference_type = node.attr('data-reference-filter') - reference_filter = reference_type.constantize + reference_filter = Gitlab::Markdown.const_get(reference_type) return if context[:reference_filter] && reference_filter != context[:reference_filter] diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index 1b741d647d1..3b83b8bd8f8 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -57,14 +57,14 @@ module Gitlab # Examples: # # data_attribute(project: 1, issue: 2) - # # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\"" + # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\"" # # data_attribute(project: 3, merge_request: 4) - # # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\"" + # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\"" # # Returns a String def data_attribute(attributes = {}) - attributes[:reference_filter] = self.class.name + attributes[:reference_filter] = self.class.name.demodulize attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ") end diff --git a/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb b/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb index c177d6950a0..7760a1c86fa 100644 --- a/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb +++ b/spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::Markdown::RedactorFilter do user = create(:user) project = create(:empty_project) - link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + link = reference_link(project: project.id, reference_filter: 'ReferenceFilter') doc = filter(link, current_user: user) expect(doc.css('a').length).to eq 0 @@ -31,14 +31,14 @@ describe Gitlab::Markdown::RedactorFilter do project = create(:empty_project) project.team << [user, :master] - link = reference_link(project: project.id, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + link = reference_link(project: project.id, reference_filter: 'ReferenceFilter') doc = filter(link, current_user: user) expect(doc.css('a').length).to eq 1 end it 'handles invalid Project references' do - link = reference_link(project: 12345, reference_filter: Gitlab::Markdown::ReferenceFilter.name) + link = reference_link(project: 12345, reference_filter: 'ReferenceFilter') expect { filter(link) }.not_to raise_error end @@ -51,7 +51,7 @@ describe Gitlab::Markdown::RedactorFilter do user = create(:user) group = create(:group) - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') doc = filter(link, current_user: user) expect(doc.css('a').length).to eq 0 @@ -62,14 +62,14 @@ describe Gitlab::Markdown::RedactorFilter do group = create(:group) group.add_developer(user) - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') doc = filter(link, current_user: user) expect(doc.css('a').length).to eq 1 end it 'handles invalid Group references' do - link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: 12345, reference_filter: 'UserReferenceFilter') expect { filter(link) }.not_to raise_error end @@ -79,7 +79,7 @@ describe Gitlab::Markdown::RedactorFilter do it 'allows any User reference' do user = create(:user) - link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(user: user.id, reference_filter: 'UserReferenceFilter') doc = filter(link) expect(doc.css('a').length).to eq 1 diff --git a/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb b/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb index 0884f53abe5..caa9d5f36cc 100644 --- a/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb +++ b/spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::Markdown::ReferenceGathererFilter do project = create(:empty_project) issue = create(:issue, project: project) - link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter') result = pipeline_result(link, current_user: user) expect(result[:references][:issue]).to be_empty @@ -28,14 +28,14 @@ describe Gitlab::Markdown::ReferenceGathererFilter do issue = create(:issue, project: project) project.team << [user, :master] - link = reference_link(project: project.id, issue: issue.id, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + link = reference_link(project: project.id, issue: issue.id, reference_filter: 'IssueReferenceFilter') result = pipeline_result(link, current_user: user) expect(result[:references][:issue]).to eq([issue]) end it 'handles invalid Project references' do - link = reference_link(project: 12345, issue: 12345, reference_filter: Gitlab::Markdown::IssueReferenceFilter.name) + link = reference_link(project: 12345, issue: 12345, reference_filter: 'IssueReferenceFilter') expect { pipeline_result(link) }.not_to raise_error end @@ -49,7 +49,7 @@ describe Gitlab::Markdown::ReferenceGathererFilter do user = create(:user) group = create(:group) - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') result = pipeline_result(link, current_user: user) expect(result[:references][:user]).to be_empty @@ -60,14 +60,14 @@ describe Gitlab::Markdown::ReferenceGathererFilter do group = create(:group) group.add_developer(user) - link = reference_link(group: group.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: group.id, reference_filter: 'UserReferenceFilter') result = pipeline_result(link, current_user: user) expect(result[:references][:user]).to eq([user]) end it 'handles invalid Group references' do - link = reference_link(group: 12345, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(group: 12345, reference_filter: 'UserReferenceFilter') expect { pipeline_result(link) }.not_to raise_error end @@ -77,7 +77,7 @@ describe Gitlab::Markdown::ReferenceGathererFilter do it 'allows any User reference' do user = create(:user) - link = reference_link(user: user.id, reference_filter: Gitlab::Markdown::UserReferenceFilter.name) + link = reference_link(user: user.id, reference_filter: 'UserReferenceFilter') result = pipeline_result(link) expect(result[:references][:user]).to eq([user]) -- cgit v1.2.1 From 9b561e7e15723a82e1f0dcf780aeb7fac5ec139b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 8 Dec 2015 02:35:34 +0100 Subject: Implement languages graph page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/graphs_controller.rb | 20 +++++++++++++++++ app/views/projects/graphs/_head.html.haml | 2 ++ app/views/projects/graphs/languages.html.haml | 32 +++++++++++++++++++++++++++ config/routes.rb | 1 + 4 files changed, 55 insertions(+) create mode 100644 app/views/projects/graphs/languages.html.haml diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 418b92040bc..c3942c52c6c 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -34,6 +34,26 @@ class Projects::GraphsController < Projects::ApplicationController @charts[:build_times] = Ci::Charts::BuildTime.new(ci_project) end + def languages + @languages = Linguist::Repository.new(@repository.rugged, @repository.rugged.head.target_id).languages + total = @languages.map(&:last).sum + + @languages = @languages.map do |language| + name, share = language + color = Digest::SHA256.hexdigest(name)[0...6] + { + value: (share.to_f * 100 / total).round(2), + label: name, + color: "##{color}", + highlight: "##{color}" + } + end + + @languages.sort! do |x, y| + y[:value] <=> x[:value] + end + end + private def fetch_graph diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index 03d0733f913..a47643bd09c 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -3,6 +3,8 @@ = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do = link_to 'Commits', commits_namespace_project_graph_path + = nav_link(action: :languages) do + = link_to 'Languages', languages_namespace_project_graph_path - if @project.builds_enabled? = nav_link(action: :ci) do = link_to ci_namespace_project_graph_path do diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml new file mode 100644 index 00000000000..a7fab5b6d72 --- /dev/null +++ b/app/views/projects/graphs/languages.html.haml @@ -0,0 +1,32 @@ +- page_title "Languages", "Graphs" += render "header_title" += render 'head' + +.gray-content-block.append-bottom-default + .oneline + Programming languages used in this repository + +.row + .col-md-8 + %canvas#languages-chart{ height: 400 } + .col-md-4 + %ul.bordered-list + - @languages.each do |language| + %li + %span{ style: "color: #{language[:color]}" } + = icon('circle') +   + = language[:label] + .pull-right + = language[:value] + \% + +:javascript + var data = #{@languages.to_json}; + var ctx = $("#languages-chart").get(0).getContext("2d"); + var options = { + scaleOverlay: true, + responsive: true, + maintainAspectRatio: false + } + var myPieChart = new Chart(ctx).Pie(data, options); diff --git a/config/routes.rb b/config/routes.rb index 3b151891a6b..59879c401a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -500,6 +500,7 @@ Rails.application.routes.draw do member do get :commits get :ci + get :languages end end -- cgit v1.2.1 From 9b20731d4aa2b6ffce614cd77606809ad0e2a832 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 8 Dec 2015 02:38:59 +0100 Subject: Add tests and changelog item for language graphs Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + features/project/graph.feature | 5 +++++ features/steps/project/graph.rb | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 604efe51a3a..7253aaef893 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -17,6 +17,7 @@ v 8.3.0 (unreleased) - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 - Run custom Git hooks when branch is created or deleted. - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch + - Add languages page to graphs v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/features/project/graph.feature b/features/project/graph.feature index 2acd65aea5f..63793d6f989 100644 --- a/features/project/graph.feature +++ b/features/project/graph.feature @@ -18,3 +18,8 @@ Feature: Project Graph Given project "Shop" has CI enabled When I visit project "Shop" CI graph page Then page should have CI graphs + + @javascript + Scenario: I should see project languages graphs + When I visit project "Shop" languages graph page + Then page should have languages graphs diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb index 98f31f3b76a..b09ec86e5df 100644 --- a/features/steps/project/graph.rb +++ b/features/steps/project/graph.rb @@ -14,6 +14,15 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps visit commits_namespace_project_graph_path(project.namespace, project, "master") end + step 'I visit project "Shop" languages graph page' do + visit languages_namespace_project_graph_path(project.namespace, project, "master") + end + + step 'page should have languages graphs' do + expect(page).to have_content "Ruby 66.63 %" + expect(page).to have_content "JavaScript 22.96 %" + end + step 'page should have commits graphs' do expect(page).to have_content "Commit statistics for master" expect(page).to have_content "Commits per day of month" -- cgit v1.2.1 From 2e270c0705895f7535ca3f62abc1c1a9cbff769d Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 1 Dec 2015 16:02:26 +0200 Subject: add explicit reference to rouge 1.10.1 --- Gemfile | 3 ++- Gemfile.lock | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 25e30e0211a..fc4d565fc84 100644 --- a/Gemfile +++ b/Gemfile @@ -99,7 +99,7 @@ gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' gem 'wikicloth', '0.8.1' gem 'asciidoctor', '~> 1.5.2' -gem 'net-ssh', '~> 3.0.1' +gem 'rouge', '~> 1.10.1' # Diffs gem 'diffy', '~> 3.0.3' @@ -205,6 +205,7 @@ gem 'raphael-rails', '~> 2.1.2' gem 'request_store', '~> 1.2.0' gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' +gem 'net-ssh', '~> 3.0.1' group :development do gem "foreman" diff --git a/Gemfile.lock b/Gemfile.lock index 9e7b93be3ab..5d70788d981 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -940,6 +940,7 @@ DEPENDENCIES request_store (~> 1.2.0) rerun (~> 0.10.0) responders (~> 2.0) + rouge (~> 1.10.1) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) rubocop (~> 0.28.0) -- cgit v1.2.1 From 79fb993a6598df4836e5c0ed4e27a72e844429fc Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 14 Nov 2015 20:19:38 +0100 Subject: Enable rubocop metrics This enables rubocop metrics like CyclomaticComplexity and ABCSize. Initial threshold values are high, should be probably decreased. --- .rubocop.yml | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index d59edbc8b17..fd382182abe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -735,23 +735,39 @@ Metrics/AbcSize: Description: >- A calculated magnitude based on number of assignments, branches, and conditions. - Enabled: false + Enabled: true + Max: 70 + +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongly correlated to the number + of test cases needed to validate a method. + Enabled: true + Max: 16 + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: true + Max: 16 + +Metrics/ParameterLists: + Description: 'Avoid parameter lists longer than three or four parameters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' + Enabled: true + Max: 8 Metrics/BlockNesting: Description: 'Avoid excessive block nesting' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' - Enabled: false + Enabled: true + Max: 4 Metrics/ClassLength: Description: 'Avoid classes longer than 100 lines of code.' Enabled: false -Metrics/CyclomaticComplexity: - Description: >- - A complexity metric that is strongly correlated to the number - of test cases needed to validate a method. - Enabled: false - Metrics/LineLength: Description: 'Limit lines to 80 characters.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' @@ -762,17 +778,6 @@ Metrics/MethodLength: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' Enabled: false -Metrics/ParameterLists: - Description: 'Avoid parameter lists longer than three or four parameters.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' - Enabled: false - -Metrics/PerceivedComplexity: - Description: >- - A complexity metric geared towards measuring complexity for a - human reader. - Enabled: false - #################### Lint ################################ ### Warnings -- cgit v1.2.1 From 8c6db54e1283348f64b46733875db7ffe08993a6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 17 Nov 2015 13:08:42 +0100 Subject: Extract repository_push_email to separate class --- app/mailers/emails/projects.rb | 96 ++++++----------------------- lib/gitlab/email/repository_push.rb | 116 ++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 77 deletions(-) create mode 100644 lib/gitlab/email/repository_push.rb diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index caba63006da..92bca4e6181 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -59,85 +59,27 @@ module Emails subject: subject("Project was moved")) end - def repository_push_email(project_id, recipient, author_id: nil, - ref: nil, - action: nil, - compare: nil, - reverse_compare: false, - send_from_committer_email: false, - disable_diffs: false) - unless author_id && ref && action - raise ArgumentError, "missing keywords: author_id, ref, action" - end - - @project = Project.find(project_id) - @current_user = @author = User.find(author_id) - @reverse_compare = reverse_compare - @compare = compare - @ref_name = Gitlab::Git.ref_name(ref) - @ref_type = Gitlab::Git.tag_ref?(ref) ? "tag" : "branch" - @action = action - @disable_diffs = disable_diffs - - if @compare - @commits = Commit.decorate(compare.commits, @project) - @diffs = compare.diffs - end - - @action_name = - case action - when :create - "pushed new" - when :delete - "deleted" - else - "pushed to" - end - - @subject = "[Git]" - @subject << "[#{@project.path_with_namespace}]" - @subject << "[#{@ref_name}]" if action == :push - @subject << " " - - if action == :push - if @commits.length > 1 - @target_url = namespace_project_compare_url(@project.namespace, - @project, - from: Commit.new(@compare.base, @project), - to: Commit.new(@compare.head, @project)) - @subject << "Deleted " if @reverse_compare - @subject << "#{@commits.length} commits: #{@commits.first.title}" - else - @target_url = namespace_project_commit_url(@project.namespace, - @project, @commits.first) - - @subject << "Deleted 1 commit: " if @reverse_compare - @subject << @commits.first.title - end - else - unless action == :delete - @target_url = namespace_project_tree_url(@project.namespace, - @project, @ref_name) - end - - subject_action = @action_name.dup - subject_action[0] = subject_action[0].capitalize - @subject << "#{subject_action} #{@ref_type} #{@ref_name}" - end - + def repository_push_email(project_id, recipient, opts = {}) + email = Gitlab::Email::RepositoryPush.new(project_id, recipient, opts) + + @project = email.project + @current_user = @author = email.author + @reverse_compare = email.reverse_compare + @compare = email.compare + @ref_name = email.ref_name + @ref_type = email.ref_type + @action = email.action + @disable_diffs = email.disable_diffs + @commits = email.commits + @diffs = email.diffs + @action_name = email.action_name + @target_url = email.target_url @disable_footer = true - reply_to = - if send_from_committer_email && can_send_from_user_email?(@author) - @author.email - else - Gitlab.config.gitlab.email_reply_to - end - - mail(from: sender(author_id, send_from_committer_email), - reply_to: reply_to, - to: recipient, - subject: @subject) + mail(from: sender(email.author_id, email.send_from_committer_email), + reply_to: email.reply_to, + to: email.recipient, + subject: email.subject) end end end diff --git a/lib/gitlab/email/repository_push.rb b/lib/gitlab/email/repository_push.rb new file mode 100644 index 00000000000..f484f3cb76d --- /dev/null +++ b/lib/gitlab/email/repository_push.rb @@ -0,0 +1,116 @@ +module Gitlab + module Email + class RepositoryPush + attr_reader :compare, :reverse_compare, :send_from_cmmitter_email, :disable_diffs, + :action, :ref, :author_id + + def initialize(project_id, recipient, opts = {}) + raise ArgumentError, 'Missing arguments: author_id, ref, action' unless + opts[:author_id] && opts[:ref] && opts[:action] + + @project_id = project_id + @recipient = recipient + + @author_id = opts[:author_id] + @ref = opts[:ref] + @action = opts[:action] + + @compare = opts[:compare] || nil + @reverse_compare = opts[:reverse_compare] || false + @send_from_committer_email = opts[:send_from_committer_email] || false + @disable_diffs = opts[:disable_diffs] || false + + @author = author + @project = project + @commits = commits + @diffs = diffs + @ref_name = ref_name + @ref_type = ref_type + @action_name = action_name + end + + def project + Project.find(@project_id) + end + + def author + User.find(@author_id) + end + + def commits + Commit.decorate(@compare.commits, @project) if @compare + end + + def diffs + @compare.diffs if @compare + end + + def action_name + case @action + when :create + 'pushed new' + when :delete + 'deleted' + else + 'pushed to' + end + end + + def subject + subject_text = '[Git]' + subject_text << "[#{@project.path_with_namespace}]" + subject_text << "[#{@ref_name}]" if @action == :push + subject_text << ' ' + + if @action == :push + if @commits.length > 1 + subject_text << "Deleted " if @reverse_compare + subject_text << "#{@commits.length} commits: #{@commits.first.title}" + else + subject_text << "Deleted 1 commit: " if @reverse_compare + subject_text << @commits.first.title + end + end + + subject_action = @action_name.dup + subject_action[0] = subject_action[0].capitalize + subject_text << "#{subject_action} #{@ref_type} #{@ref_name}" + end + + def ref_name + Gitlab::Git.ref_name(@ref) + end + + def ref_type + Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' + end + + def target_url + if action == :push + if @commits.length > 1 + namespace_project_compare_url(@project.namespace, + @project, + from: Commit.new(@compare.base, @project), + to: Commit.new(@compare.head, @project)) + else + namespace_project_commit_url(@project.namespace, + @project, @commits.first) + end + end + + if action != :delete && action != :push + namespace_project_tree_url(@project.namespace, + @project, @ref_name) + end + end + + def reply_to + if @send_from_committer_email && can_send_from_user_email?(@author) + @author.email + else + Gitlab.config.gitlab.email_reply_to + end + end + end + end +end -- cgit v1.2.1 From 45f7f01f19a6c5f977d71b094cbe5fedb44dc9e2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 18 Nov 2015 10:12:09 +0100 Subject: Make `can_send_from_user_email?` public in Notify --- app/mailers/notify.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 50a409c3754..0534eb025cd 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -33,13 +33,13 @@ class Notify < BaseMailer allowed_domains end - private - def can_send_from_user_email?(sender) sender_domain = sender.email.split("@").last self.class.allowed_email_domains.include?(sender_domain) end + private + # Return an email address that displays the name of the sender. # Only the displayed name changes; the actual email address is always the same. def sender(sender_id, send_from_user_email = false) -- cgit v1.2.1 From e2f937ce22e6b0eb458bbdb3fa93b06d80ecfd21 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 18 Nov 2015 10:13:34 +0100 Subject: Refactor RepositoryPush, move to Message namespace --- app/mailers/emails/projects.rb | 38 ++++----- lib/gitlab/email/message/repository_push.rb | 120 ++++++++++++++++++++++++++++ lib/gitlab/email/repository_push.rb | 116 --------------------------- 3 files changed, 140 insertions(+), 134 deletions(-) create mode 100644 lib/gitlab/email/message/repository_push.rb delete mode 100644 lib/gitlab/email/repository_push.rb diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 92bca4e6181..c303e5a1a9c 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -60,26 +60,28 @@ module Emails end def repository_push_email(project_id, recipient, opts = {}) - email = Gitlab::Email::RepositoryPush.new(project_id, recipient, opts) - - @project = email.project - @current_user = @author = email.author - @reverse_compare = email.reverse_compare - @compare = email.compare - @ref_name = email.ref_name - @ref_type = email.ref_type - @action = email.action - @disable_diffs = email.disable_diffs - @commits = email.commits - @diffs = email.diffs - @action_name = email.action_name - @target_url = email.target_url + repository_push = + Gitlab::Email::Message::RepositoryPush.new(self, project_id, recipient, opts) + + @project = repository_push.project + @current_user = @author = repository_push.author + @reverse_compare = repository_push.reverse_compare + @compare = repository_push.compare + @ref_name = repository_push.ref_name + @ref_type = repository_push.ref_type + @action = repository_push.action + @action_name = repository_push.action_name + @disable_diffs = repository_push.disable_diffs + @commits = repository_push.commits + @diffs = repository_push.diffs + @target_url = repository_push.target_url @disable_footer = true - mail(from: sender(email.author_id, email.send_from_committer_email), - reply_to: email.reply_to, - to: email.recipient, - subject: email.subject) + mail(from: sender(repository_push.author_id, + repository_push.send_from_committer_email), + reply_to: repository_push.reply_to, + to: repository_push.recipient, + subject: repository_push.subject) end end end diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb new file mode 100644 index 00000000000..bb92df9e1bb --- /dev/null +++ b/lib/gitlab/email/message/repository_push.rb @@ -0,0 +1,120 @@ +module Gitlab + module Email + module Message + class RepositoryPush + attr_reader :compare, :reverse_compare, :send_from_committer_email, + :disable_diffs, :action, :ref, :author_id + attr_accessor :recipient + + def initialize(notify, project_id, recipient, opts = {}) + raise ArgumentError, 'Missing arguments: author_id, ref, action' unless + opts[:author_id] && opts[:ref] && opts[:action] + + @notify = notify + @project_id = project_id + @recipient = recipient + + @author_id = opts[:author_id] + @ref = opts[:ref] + @action = opts[:action] + + @compare = opts[:compare] || nil + @reverse_compare = opts[:reverse_compare] || false + @send_from_committer_email = opts[:send_from_committer_email] || false + @disable_diffs = opts[:disable_diffs] || false + + @author = author + @project = project + @commits = commits + @diffs = diffs + @ref_name = ref_name + @ref_type = ref_type + @action_name = action_name + end + + def project + Project.find(@project_id) + end + + def author + User.find(@author_id) + end + + def commits + Commit.decorate(@compare.commits, @project) if @compare + end + + def diffs + @compare.diffs if @compare + end + + def action_name + case @action + when :create + 'pushed new' + when :delete + 'deleted' + else + 'pushed to' + end + end + + def subject + subject_text = '[Git]' + subject_text << "[#{@project.path_with_namespace}]" + subject_text << "[#{@ref_name}]" if @action == :push + subject_text << ' ' + + if @action == :push + if @commits.length > 1 + subject_text << "Deleted " if @reverse_compare + subject_text << "#{@commits.length} commits: #{@commits.first.title}" + else + subject_text << "Deleted 1 commit: " if @reverse_compare + subject_text << @commits.first.title + end + else + subject_action = @action_name.dup + subject_action[0] = subject_action[0].capitalize + subject_text << "#{subject_action} #{@ref_type} #{@ref_name}" + end + end + + def ref_name + Gitlab::Git.ref_name(@ref) + end + + def ref_type + Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' + end + + def target_url + if @action == :push + if @commits.length > 1 + @notify.namespace_project_compare_url(@project.namespace, + @project, + from: Commit.new(@compare.base, @project), + to: Commit.new(@compare.head, @project)) + else + @notify.namespace_project_commit_url(@project.namespace, + @project, @commits.first) + end + else + unless @action == :delete + @notify.namespace_project_tree_url(@project.namespace, + @project, @ref_name) + end + end + end + + def reply_to + if @send_from_committer_email && @notify.can_send_from_user_email?(@author) + @author.email + else + Gitlab.config.gitlab.email_reply_to + end + end + end + end + end +end diff --git a/lib/gitlab/email/repository_push.rb b/lib/gitlab/email/repository_push.rb deleted file mode 100644 index f484f3cb76d..00000000000 --- a/lib/gitlab/email/repository_push.rb +++ /dev/null @@ -1,116 +0,0 @@ -module Gitlab - module Email - class RepositoryPush - attr_reader :compare, :reverse_compare, :send_from_cmmitter_email, :disable_diffs, - :action, :ref, :author_id - - def initialize(project_id, recipient, opts = {}) - raise ArgumentError, 'Missing arguments: author_id, ref, action' unless - opts[:author_id] && opts[:ref] && opts[:action] - - @project_id = project_id - @recipient = recipient - - @author_id = opts[:author_id] - @ref = opts[:ref] - @action = opts[:action] - - @compare = opts[:compare] || nil - @reverse_compare = opts[:reverse_compare] || false - @send_from_committer_email = opts[:send_from_committer_email] || false - @disable_diffs = opts[:disable_diffs] || false - - @author = author - @project = project - @commits = commits - @diffs = diffs - @ref_name = ref_name - @ref_type = ref_type - @action_name = action_name - end - - def project - Project.find(@project_id) - end - - def author - User.find(@author_id) - end - - def commits - Commit.decorate(@compare.commits, @project) if @compare - end - - def diffs - @compare.diffs if @compare - end - - def action_name - case @action - when :create - 'pushed new' - when :delete - 'deleted' - else - 'pushed to' - end - end - - def subject - subject_text = '[Git]' - subject_text << "[#{@project.path_with_namespace}]" - subject_text << "[#{@ref_name}]" if @action == :push - subject_text << ' ' - - if @action == :push - if @commits.length > 1 - subject_text << "Deleted " if @reverse_compare - subject_text << "#{@commits.length} commits: #{@commits.first.title}" - else - subject_text << "Deleted 1 commit: " if @reverse_compare - subject_text << @commits.first.title - end - end - - subject_action = @action_name.dup - subject_action[0] = subject_action[0].capitalize - subject_text << "#{subject_action} #{@ref_type} #{@ref_name}" - end - - def ref_name - Gitlab::Git.ref_name(@ref) - end - - def ref_type - Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' - end - - def target_url - if action == :push - if @commits.length > 1 - namespace_project_compare_url(@project.namespace, - @project, - from: Commit.new(@compare.base, @project), - to: Commit.new(@compare.head, @project)) - else - namespace_project_commit_url(@project.namespace, - @project, @commits.first) - end - end - - if action != :delete && action != :push - namespace_project_tree_url(@project.namespace, - @project, @ref_name) - end - end - - def reply_to - if @send_from_committer_email && can_send_from_user_email?(@author) - @author.email - else - Gitlab.config.gitlab.email_reply_to - end - end - end - end -end -- cgit v1.2.1 From 4beba7494b096f2540b19017bb7c1c8e91679135 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 20 Nov 2015 15:29:47 +0100 Subject: Improve Messagee::RepositoryPush --- app/mailers/emails/projects.rb | 6 +- lib/gitlab/email/message/repository_push.rb | 126 ++++++++++++++-------------- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index c303e5a1a9c..bedadb583c7 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -65,20 +65,20 @@ module Emails @project = repository_push.project @current_user = @author = repository_push.author - @reverse_compare = repository_push.reverse_compare @compare = repository_push.compare @ref_name = repository_push.ref_name @ref_type = repository_push.ref_type @action = repository_push.action @action_name = repository_push.action_name - @disable_diffs = repository_push.disable_diffs @commits = repository_push.commits @diffs = repository_push.diffs @target_url = repository_push.target_url + @disable_diffs = repository_push.disable_diffs? + @reverse_compare = repository_push.reverse_compare? @disable_footer = true mail(from: sender(repository_push.author_id, - repository_push.send_from_committer_email), + repository_push.send_from_committer_email?), reply_to: repository_push.reply_to, to: repository_push.recipient, subject: repository_push.subject) diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index bb92df9e1bb..61962abf822 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -2,118 +2,122 @@ module Gitlab module Email module Message class RepositoryPush - attr_reader :compare, :reverse_compare, :send_from_committer_email, - :disable_diffs, :action, :ref, :author_id attr_accessor :recipient + attr_reader :author_id, :ref, :action def initialize(notify, project_id, recipient, opts = {}) - raise ArgumentError, 'Missing arguments: author_id, ref, action' unless + raise ArgumentError, 'Missing options: author_id, ref, action' unless opts[:author_id] && opts[:ref] && opts[:action] @notify = notify @project_id = project_id @recipient = recipient + @opts = opts - @author_id = opts[:author_id] - @ref = opts[:ref] - @action = opts[:action] - - @compare = opts[:compare] || nil - @reverse_compare = opts[:reverse_compare] || false - @send_from_committer_email = opts[:send_from_committer_email] || false - @disable_diffs = opts[:disable_diffs] || false - - @author = author - @project = project - @commits = commits - @diffs = diffs - @ref_name = ref_name - @ref_type = ref_type - @action_name = action_name + @author_id = opts.delete(:author_id) + @ref = opts.delete(:ref) + @action = opts.delete(:action) end def project - Project.find(@project_id) + @project ||= Project.find(@project_id) end def author - User.find(@author_id) + @author ||= User.find(@author_id) end def commits - Commit.decorate(@compare.commits, @project) if @compare + @commits ||= (Commit.decorate(compare.commits, project) if compare) end def diffs - @compare.diffs if @compare + @diffs ||= (compare.diffs if compare) end - def action_name - case @action - when :create - 'pushed new' - when :delete - 'deleted' - else - 'pushed to' - end + def compare + @opts[:compare] end - def subject - subject_text = '[Git]' - subject_text << "[#{@project.path_with_namespace}]" - subject_text << "[#{@ref_name}]" if @action == :push - subject_text << ' ' + def reverse_compare? + @opts[:reverse_compare] || false + end - if @action == :push - if @commits.length > 1 - subject_text << "Deleted " if @reverse_compare - subject_text << "#{@commits.length} commits: #{@commits.first.title}" + def disable_diffs? + @opts[:disable_diffs] || false + end + + def send_from_committer_email? + @opts[:send_from_committer_email] || false + end + + def action_name + @action_name ||= + case @action + when :create + 'pushed new' + when :delete + 'deleted' else - subject_text << "Deleted 1 commit: " if @reverse_compare - subject_text << @commits.first.title + 'pushed to' end - else - subject_action = @action_name.dup - subject_action[0] = subject_action[0].capitalize - subject_text << "#{subject_action} #{@ref_type} #{@ref_name}" - end end def ref_name - Gitlab::Git.ref_name(@ref) + @ref_name ||= Gitlab::Git.ref_name(@ref) end def ref_type - Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' + @ref_type ||= Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch' end def target_url if @action == :push - if @commits.length > 1 - @notify.namespace_project_compare_url(@project.namespace, - @project, - from: Commit.new(@compare.base, @project), - to: Commit.new(@compare.head, @project)) + if commits.length > 1 && compare + @notify.namespace_project_compare_url(project.namespace, + project, + from: Commit.new(compare.base, project), + to: Commit.new(compare.head, project)) else - @notify.namespace_project_commit_url(@project.namespace, - @project, @commits.first) + @notify.namespace_project_commit_url(project.namespace, + project, commits.first) end else unless @action == :delete - @notify.namespace_project_tree_url(@project.namespace, - @project, @ref_name) + @notify.namespace_project_tree_url(project.namespace, + project, ref_name) end end end def reply_to - if @send_from_committer_email && @notify.can_send_from_user_email?(@author) - @author.email + if send_from_committer_email? && @notify.can_send_from_user_email?(author) + author.email else Gitlab.config.gitlab.email_reply_to end end + + def subject + subject_text = '[Git]' + subject_text << "[#{project.path_with_namespace}]" + subject_text << "[#{ref_name}]" if @action == :push + subject_text << ' ' + + if @action == :push + if commits.length > 1 + subject_text << "Deleted " if reverse_compare? + subject_text << "#{commits.length} commits: #{commits.first.title}" + else + subject_text << "Deleted 1 commit: " if reverse_compare? + subject_text << commits.first.title + end + else + subject_action = action_name.dup + subject_action[0] = subject_action[0].capitalize + subject_text << "#{subject_action} #{ref_type} #{ref_name}" + end + end end end end -- cgit v1.2.1 From 9f2752e5dcc31dc4e9d91ee18caf1d36f1b7684e Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 21 Nov 2015 20:54:19 +0100 Subject: Remove obsolete variables in `repository_push_email` --- app/mailers/emails/projects.rb | 25 +++++---------------- app/views/notify/repository_push_email.html.haml | 28 +++++++++++++----------- app/views/notify/repository_push_email.text.haml | 24 ++++++++++---------- lib/gitlab/email/message/repository_push.rb | 17 +++++++++++--- 4 files changed, 46 insertions(+), 48 deletions(-) diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index bedadb583c7..35015ca34f8 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -60,28 +60,13 @@ module Emails end def repository_push_email(project_id, recipient, opts = {}) - repository_push = + @message = Gitlab::Email::Message::RepositoryPush.new(self, project_id, recipient, opts) - @project = repository_push.project - @current_user = @author = repository_push.author - @compare = repository_push.compare - @ref_name = repository_push.ref_name - @ref_type = repository_push.ref_type - @action = repository_push.action - @action_name = repository_push.action_name - @commits = repository_push.commits - @diffs = repository_push.diffs - @target_url = repository_push.target_url - @disable_diffs = repository_push.disable_diffs? - @reverse_compare = repository_push.reverse_compare? - @disable_footer = true - - mail(from: sender(repository_push.author_id, - repository_push.send_from_committer_email?), - reply_to: repository_push.reply_to, - to: repository_push.recipient, - subject: repository_push.subject) + mail(from: sender(@message.author_id, @message.send_from_committer_email?), + reply_to: @message.reply_to, + to: @message.recipient, + subject: @message.subject) end end end diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml index 12f83aae04b..4361f67a74d 100644 --- a/app/views/notify/repository_push_email.html.haml +++ b/app/views/notify/repository_push_email.html.haml @@ -1,30 +1,32 @@ -%h3 #{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} +%h3 + #{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} + at #{link_to(@message.project_name_with_namespace, namespace_project_url(@message.project_namespace, @message.project))} -- if @compare - - if @reverse_compare +- if @message.compare + - if @message.reverse_compare? %p %strong WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. %h4 - = @reverse_compare ? "Deleted commits:" : "Commits:" + = @message.reverse_compare? ? "Deleted commits:" : "Commits:" %ul - - @commits.each do |commit| + - @message.commits.each do |commit| %li - %strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)} + %strong #{link_to(commit.short_id, namespace_project_commit_url(@message.project_namespace, @message.project, commit))} %div %span by #{commit.author_name} %i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} %pre.commit-message = commit.safe_message - %h4 #{pluralize @diffs.count, "changed file"}: + %h4 #{pluralize @message.diffs_count, "changed file"}: %ul - - @diffs.each_with_index do |diff, i| + - @message.diffs.each_with_index do |diff, i| %li.file-stats - %a{href: "#{@target_url if @disable_diffs}#diff-#{i}" } + %a{href: "#{@message.target_url if @message.disable_diffs?}#diff-#{i}" } - if diff.deleted_file %span.deleted-file − @@ -40,11 +42,11 @@ - else = diff.new_path - - unless @disable_diffs + - unless @message.disable_diffs? %h4 Changes: - - @diffs.each_with_index do |diff, i| + - @message.diffs.each_with_index do |diff, i| %li{id: "diff-#{i}"} - %a{href: @target_url + "#diff-#{i}"} + %a{href: @message.target_url + "#diff-#{i}"} - if diff.deleted_file %strong = diff.old_path @@ -62,5 +64,5 @@ = color_email_diff(diff.diff) %br - - if @compare.timeout + - if @message.compare_timeout %h5 Huge diff. To prevent performance issues changes are hidden diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml index 97a176ed2a3..aa0e263b6df 100644 --- a/app/views/notify/repository_push_email.text.haml +++ b/app/views/notify/repository_push_email.text.haml @@ -1,21 +1,21 @@ -#{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{@project.name_with_namespace} -- if @compare +#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name} at #{@message.project_name_with_namespace} +- if @message.compare \ \ - - if @reverse_compare + - if @message.reverse_compare? WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below. \ \ - = @reverse_compare ? "Deleted commits:" : "Commits:" - - @commits.each do |commit| + = @message.reverse_compare? ? "Deleted commits:" : "Commits:" + - @message.commits.each do |commit| #{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")} #{commit.safe_message} \- - - - - \ \ - #{pluralize @diffs.count, "changed file"}: + #{pluralize @message.diffs_count, "changed file"}: \ - - @diffs.each do |diff| + - @message.diffs.each do |diff| - if diff.deleted_file \- − #{diff.old_path} - elsif diff.renamed_file @@ -24,11 +24,11 @@ \- + #{diff.new_path} - else \- #{diff.new_path} - - unless @disable_diffs + - unless @message.disable_diffs? \ \ Changes: - - @diffs.each do |diff| + - @message.diffs.each do |diff| \ \===================================== - if diff.deleted_file @@ -39,11 +39,11 @@ = diff.new_path \===================================== != diff.diff - - if @compare.timeout + - if @message.compare_timeout \ \ Huge diff. To prevent performance issues it was hidden - - if @target_url + - if @message.target_url \ \ - View it on GitLab: #{@target_url} + View it on GitLab: #{@message.target_url} diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index 61962abf822..b0af3feee9e 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -5,6 +5,9 @@ module Gitlab attr_accessor :recipient attr_reader :author_id, :ref, :action + delegate :namespace, :name_with_namespace, to: :project, prefix: :project + delegate :name, to: :author, prefix: :author + def initialize(notify, project_id, recipient, opts = {}) raise ArgumentError, 'Missing options: author_id, ref, action' unless opts[:author_id] && opts[:ref] && opts[:action] @@ -35,10 +38,18 @@ module Gitlab @diffs ||= (compare.diffs if compare) end + def diffs_count + diffs.count if diffs + end + def compare @opts[:compare] end + def compare_timeout + compare.timeout if compare + end + def reverse_compare? @opts[:reverse_compare] || false end @@ -74,17 +85,17 @@ module Gitlab def target_url if @action == :push if commits.length > 1 && compare - @notify.namespace_project_compare_url(project.namespace, + @notify.namespace_project_compare_url(project_namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) else - @notify.namespace_project_commit_url(project.namespace, + @notify.namespace_project_commit_url(project_namespace, project, commits.first) end else unless @action == :delete - @notify.namespace_project_tree_url(project.namespace, + @notify.namespace_project_tree_url(project_namespace, project, ref_name) end end -- cgit v1.2.1 From d835fbc79f6cd6f17fc7472af48074805622a573 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sun, 22 Nov 2015 20:59:31 +0100 Subject: Fix url helpers in RepositoryPush --- lib/gitlab/email/message/repository_push.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index b0af3feee9e..bc8aece733f 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -16,6 +16,7 @@ module Gitlab @project_id = project_id @recipient = recipient @opts = opts + @urls = Gitlab::Application.routes.url_helpers @author_id = opts.delete(:author_id) @ref = opts.delete(:ref) @@ -85,17 +86,17 @@ module Gitlab def target_url if @action == :push if commits.length > 1 && compare - @notify.namespace_project_compare_url(project_namespace, + @urls.namespace_project_compare_url(project_namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) else - @notify.namespace_project_commit_url(project_namespace, + @urls.namespace_project_commit_url(project_namespace, project, commits.first) end else unless @action == :delete - @notify.namespace_project_tree_url(project_namespace, + @urls.namespace_project_tree_url(project_namespace, project, ref_name) end end -- cgit v1.2.1 From 360a96a299397cbb5f8b15f916a9efd5962ff6be Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 23 Nov 2015 20:25:49 +0000 Subject: Fix specs by adding forgotten instance variable --- app/mailers/emails/projects.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 35015ca34f8..b96418679bd 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -63,6 +63,9 @@ module Emails @message = Gitlab::Email::Message::RepositoryPush.new(self, project_id, recipient, opts) + # used in notify layout + @target_url = @message.target_url + mail(from: sender(@message.author_id, @message.send_from_committer_email?), reply_to: @message.reply_to, to: @message.recipient, -- cgit v1.2.1 From 75c6b29f6b15e164717c27e6c3d7eecb84c923f8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 3 Dec 2015 14:16:16 +0100 Subject: Add `RepositoryPush` specs --- lib/gitlab/email/message/repository_push.rb | 10 +- .../gitlab/email/message/repository_push_spec.rb | 122 +++++++++++++++++++++ 2 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 spec/lib/gitlab/email/message/repository_push_spec.rb diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index bc8aece733f..3526eb2cad9 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -87,17 +87,17 @@ module Gitlab if @action == :push if commits.length > 1 && compare @urls.namespace_project_compare_url(project_namespace, - project, - from: Commit.new(compare.base, project), - to: Commit.new(compare.head, project)) + project, + from: Commit.new(compare.base, project), + to: Commit.new(compare.head, project)) else @urls.namespace_project_commit_url(project_namespace, - project, commits.first) + project, commits.first) end else unless @action == :delete @urls.namespace_project_tree_url(project_namespace, - project, ref_name) + project, ref_name) end end end diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb new file mode 100644 index 00000000000..56ae2a8d121 --- /dev/null +++ b/spec/lib/gitlab/email/message/repository_push_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe Gitlab::Email::Message::RepositoryPush do + include RepoHelpers + + let!(:group) { create(:group, name: 'my_group') } + let!(:project) { create(:project, name: 'my_project', namespace: group) } + let!(:author) { create(:author, name: 'Author') } + + let(:message) do + described_class.new(Notify, project.id, 'recipient@example.com', opts) + end + + context 'new commits have been pushed to repository' do + let(:opts) do + { author_id: author.id, ref: 'master', action: :push, compare: compare, + send_from_committer_email: true } + end + let(:compare) do + Gitlab::Git::Compare.new(project.repository.raw_repository, + sample_image_commit.id, sample_commit.id) + end + + describe '#project' do + subject { message.project } + it { is_expected.to eq project } + it { is_expected.to be_an_instance_of Project } + end + + describe '#project_namespace' do + subject { message.project_namespace } + it { is_expected.to eq group } + it { is_expected.to be_kind_of Namespace } + end + + describe '#project_name_with_namespace' do + subject { message.project_name_with_namespace } + it { is_expected.to eq 'my_group / my_project' } + end + + describe '#author' do + subject { message.author } + it { is_expected.to eq author } + it { is_expected.to be_an_instance_of User } + end + + describe '#author_name' do + subject { message.author_name } + it { is_expected.to eq 'Author' } + end + + describe '#commits' do + subject { message.commits } + it { is_expected.to be_kind_of Array } + it { is_expected.to all(be_instance_of Commit) } + end + + describe '#diffs' do + subject { message.diffs } + it { is_expected.to all(be_an_instance_of Gitlab::Git::Diff) } + end + + describe '#diffs_count' do + subject { message.diffs_count } + it { is_expected.to eq compare.diffs.count } + end + + describe '#compare' do + subject { message.compare } + it { is_expected.to be_an_instance_of Gitlab::Git::Compare } + end + + describe '#compare_timeout' do + subject { message.compare_timeout } + it { is_expected.to eq compare.timeout } + end + + describe '#reverse_compare?' do + subject { message.reverse_compare? } + it { is_expected.to eq false } + end + + describe '#disable_diffs?' do + subject { message.disable_diffs? } + it { is_expected.to eq false } + end + + describe '#send_from_committer_email?' do + subject { message.send_from_committer_email? } + it { is_expected.to eq true } + end + + describe '#action_name' do + subject { message.action_name } + it { is_expected.to eq 'pushed to' } + end + + describe '#ref_name' do + subject { message.ref_name } + it { is_expected.to eq 'master' } + end + + describe '#ref_type' do + subject { message.ref_type } + it { is_expected.to eq 'branch' } + end + + describe '#target_url' do + subject { message.target_url } + it { is_expected.to include 'compare' } + it { is_expected.to include compare.commits.first.parents.first.id } + it { is_expected.to include compare.commits.last.id } + end + + describe '#subject' do + subject { message.subject } + it { is_expected.to include "[Git][#{project.path_with_namespace}]" } + it { is_expected.to include "#{compare.commits.length} commits" } + it { is_expected.to include compare.commits.first.message.split("\n").first } + end + end +end -- cgit v1.2.1 From 7e9109c43bdaedb79b06ba8948cd729ac5f68deb Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 4 Dec 2015 13:07:42 +0100 Subject: Move `common_mentionable_setup` to shared context in specs --- spec/support/mentionable_shared_examples.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb index 33d2b14583c..fce91015fd4 100644 --- a/spec/support/mentionable_shared_examples.rb +++ b/spec/support/mentionable_shared_examples.rb @@ -4,7 +4,7 @@ # - let(:backref_text) { "the way that +subject+ should refer to itself in backreferences " } # - let(:set_mentionable_text) { lambda { |txt| "block that assigns txt to the subject's mentionable_text" } } -def common_mentionable_setup +shared_context 'mentionable context' do let(:project) { subject.project } let(:author) { subject.author } @@ -56,7 +56,7 @@ def common_mentionable_setup end shared_examples 'a mentionable' do - common_mentionable_setup + include_context 'mentionable context' it 'generates a descriptive back-reference' do expect(subject.gfm_reference).to eq(backref_text) @@ -88,7 +88,7 @@ shared_examples 'a mentionable' do end shared_examples 'an editable mentionable' do - common_mentionable_setup + include_context 'mentionable context' it_behaves_like 'a mentionable' -- cgit v1.2.1 From 591035968dc96acd27155ced4c0ae04649fcd113 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 4 Dec 2015 12:55:19 +0000 Subject: Duplicate options in `RepositoryPush` --- lib/gitlab/email/message/repository_push.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index 3526eb2cad9..ca89b67e580 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -15,12 +15,12 @@ module Gitlab @notify = notify @project_id = project_id @recipient = recipient - @opts = opts + @opts = opts.dup @urls = Gitlab::Application.routes.url_helpers - @author_id = opts.delete(:author_id) - @ref = opts.delete(:ref) - @action = opts.delete(:action) + @author_id = @opts.delete(:author_id) + @ref = @opts.delete(:ref) + @action = @opts.delete(:action) end def project -- cgit v1.2.1 From 66f658a9b543b1493f625b2f44f3f845d64b749d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 4 Dec 2015 14:11:17 +0100 Subject: Check if commits are available in `RepositoryPush` --- lib/gitlab/email/message/repository_push.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index ca89b67e580..c53148d2ed8 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -84,8 +84,8 @@ module Gitlab end def target_url - if @action == :push - if commits.length > 1 && compare + if @action == :push && commits + if commits.length > 1 @urls.namespace_project_compare_url(project_namespace, project, from: Commit.new(compare.base, project), @@ -116,7 +116,7 @@ module Gitlab subject_text << "[#{ref_name}]" if @action == :push subject_text << ' ' - if @action == :push + if @action == :push && commits if commits.length > 1 subject_text << "Deleted " if reverse_compare? subject_text << "#{commits.length} commits: #{commits.first.title}" -- cgit v1.2.1 From 652de0b820587983e0af76186db4570b536d7ce3 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 7 Dec 2015 10:43:07 +0100 Subject: Refactor CI YAML processor's validators --- lib/ci/gitlab_ci_yaml_processor.rb | 66 +++++++++++++++++----------- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 6 +-- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 3beafcad117..9c11fc3c81d 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -132,26 +132,36 @@ module Ci end def validate_job!(name, job) + validate_job_name!(name) + validate_job_keys!(name, job) + validate_job_types!(name, job) + + validate_job_stage!(name, job) if job[:stage] + validate_job_cache!(name, job) if job[:cache] + validate_job_artifacts!(name, job) if job[:artifacts] + end + + private + + def validate_job_name!(name) if name.blank? || !validate_string(name) raise ValidationError, "job name should be non-empty string" end + end + def validate_job_keys!(name, job) job.keys.each do |key| unless ALLOWED_JOB_KEYS.include? key raise ValidationError, "#{name} job: unknown parameter #{key}" end end + end + def validate_job_types!(name, job) if !validate_string(job[:script]) && !validate_array_of_strings(job[:script]) raise ValidationError, "#{name} job: script should be a string or an array of a strings" end - if job[:stage] - unless job[:stage].is_a?(String) && job[:stage].in?(stages) - raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" - end - end - if job[:image] && !validate_string(job[:image]) raise ValidationError, "#{name} job: image should be a string" end @@ -172,36 +182,40 @@ module Ci raise ValidationError, "#{name} job: except parameter should be an array of strings" end - if job[:cache] - if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked]) - raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean" - end - - if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths]) - raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings" - end + if job[:allow_failure] && !validate_boolean(job[:allow_failure]) + raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" end - if job[:artifacts] - if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) - raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" - end + if job[:when] && !job[:when].in?(%w(on_success on_failure always)) + raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always" + end + end - if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths]) - raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings" - end + def validate_job_stage!(name, job) + unless job[:stage].is_a?(String) && job[:stage].in?(stages) + raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}" end + end - if job[:allow_failure] && !validate_boolean(job[:allow_failure]) - raise ValidationError, "#{name} job: allow_failure parameter should be an boolean" + def validate_job_cache!(name, job) + if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked]) + raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean" end - if job[:when] && !job[:when].in?(%w(on_success on_failure always)) - raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always" + if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths]) + raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings" end end - private + def validate_job_artifacts!(name, job) + if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked]) + raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean" + end + + if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths]) + raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings" + end + end def validate_array_of_strings(values) values.is_a?(Array) && values.all? { |value| validate_string(value) } diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 6f287719ba6..0d50f20ae34 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -532,21 +532,21 @@ module Ci end it "returns errors if job stage is not a string" do - config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } }) + config = YAML.dump({ rspec: { script: "test", type: 1 } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a pre-defined stage" do - config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + config = YAML.dump({ rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy") end it "returns errors if job stage is not a defined stage" do - config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } }) + config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance" } }) expect do GitlabCiYamlProcessor.new(config, path) end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test") -- cgit v1.2.1 From 6ffe8a06fdf1ffc40e60a487a730b50c4699907d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 7 Dec 2015 10:52:22 +0100 Subject: Bump cyclomatic and perceived complexity threshold by one --- .rubocop.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index fd382182abe..b4ca11c8343 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -743,14 +743,14 @@ Metrics/CyclomaticComplexity: A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Enabled: true - Max: 16 + Max: 17 Metrics/PerceivedComplexity: Description: >- A complexity metric geared towards measuring complexity for a human reader. Enabled: true - Max: 16 + Max: 17 Metrics/ParameterLists: Description: 'Avoid parameter lists longer than three or four parameters.' -- cgit v1.2.1 From 9bf51ae47d344c5939f275bb5ae429023456d30e Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 8 Dec 2015 10:58:15 +0100 Subject: Fix specs caused by update of gitlab-test repo. --- features/steps/project/source/browse_files.rb | 1 + spec/controllers/projects/raw_controller_spec.rb | 2 +- spec/support/test_env.rb | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index 2edbca9b7fe..02d46e28237 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -306,6 +306,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I click on "files/lfs/lfs_object.iso" file in repo' do + visit namespace_project_tree_path(@project.namespace, @project, "lfs") click_link 'files' click_link "lfs" click_link "lfs_object.iso" diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 2329c246daa..cd42a592759 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -35,7 +35,7 @@ describe Projects::RawController do end context 'lfs object' do - let(:id) { 'master/files/lfs/lfs_object.iso' } + let(:id) { 'be93687/files/lfs/lfs_object.iso' } let!(:lfs_object) { create(:lfs_object, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') } context 'when project has access' do diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 47c67475cd0..985bd141e69 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -12,7 +12,8 @@ module TestEnv 'fix' => '48f0be4', 'improve/awesome' => '5937ac0', 'markdown' => '0ed8c6c', - 'master' => 'be93687', + 'lfs' => 'be93687', + 'master' => '5937ac0', "'test'" => 'e56497b', } -- cgit v1.2.1 From 13f44822d97e643b55047cf619f7bb1f354f165b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:29:48 +0100 Subject: Move CombinedPipeline methods around --- lib/gitlab/markdown/combined_pipeline.rb | 12 +++++++----- lib/gitlab/markdown/pipeline/full_pipeline.rb | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/markdown/combined_pipeline.rb b/lib/gitlab/markdown/combined_pipeline.rb index a3d717550f5..6b08a5e9f72 100644 --- a/lib/gitlab/markdown/combined_pipeline.rb +++ b/lib/gitlab/markdown/combined_pipeline.rb @@ -7,16 +7,18 @@ module Gitlab Class.new(Pipeline) do const_set :PIPELINES, pipelines + def self.pipelines + self::PIPELINES + end + def self.filters pipelines.flat_map(&:filters) end def self.transform_context(context) - pipelines.reduce(context) { |context, pipeline| pipeline.transform_context(context) } - end - - def self.pipelines - self::PIPELINES + pipelines.reduce(context) do |context, pipeline| + pipeline.transform_context(context) + end end end end diff --git a/lib/gitlab/markdown/pipeline/full_pipeline.rb b/lib/gitlab/markdown/pipeline/full_pipeline.rb index 553e9367c1c..b3b7a3c27c0 100644 --- a/lib/gitlab/markdown/pipeline/full_pipeline.rb +++ b/lib/gitlab/markdown/pipeline/full_pipeline.rb @@ -3,7 +3,7 @@ require 'gitlab/markdown' module Gitlab module Markdown class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline) - + end end end -- cgit v1.2.1 From c64a881a40b86d7ed1018daf7d59ffa1bda99ea1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:53:02 +0100 Subject: Allow center top menu to span multiple lines --- app/assets/stylesheets/framework/common.scss | 12 +++++++++--- app/assets/stylesheets/framework/mobile.scss | 3 --- app/assets/stylesheets/pages/builds.scss | 5 ----- app/views/projects/builds/show.html.haml | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index d2f491daf78..cafcfc308e4 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -333,7 +333,7 @@ table { } .well { - margin-bottom: 0; + margin-bottom: $gl-padding; } .search_box { @@ -379,9 +379,8 @@ table { text-align: center; margin-top: 5px; margin-bottom: $gl-padding; - height: 56px; + height: auto; margin-top: -$gl-padding; - padding-top: $gl-padding; &.no-bottom { margin-bottom: 0; @@ -390,6 +389,13 @@ table { &.no-top { margin-top: 0; } + + li a { + display: inline-block; + padding-top: $gl-padding; + padding-bottom: 11px; + margin-bottom: -1px; + } } .center-middle-menu { diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss index cea47fba192..6f44c323732 100644 --- a/app/assets/stylesheets/framework/mobile.scss +++ b/app/assets/stylesheets/framework/mobile.scss @@ -82,9 +82,6 @@ } .center-top-menu { - height: 45px; - margin-bottom: 30px; - li a { font-size: 14px; padding: 19px 10px; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index da9965f007a..3c2997c1d5a 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -67,9 +67,4 @@ color: #3084bb !important; } } - - .build-top-menu { - margin-top: 0; - margin-bottom: 2px; - } } diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 907e1ce10bd..5a55cb8f8d3 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -11,7 +11,7 @@ #up-build-trace - if @commit.matrix_for_ref?(@build.ref) - %ul.center-top-menu.build-top-menu + %ul.center-top-menu.no-top.no-bottom - @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 -- cgit v1.2.1 From 507fdb4b2bf67419613abff150de5b4283faaeff Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:53:57 +0100 Subject: Use consistent padding in panel heading --- app/assets/stylesheets/framework/panels.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss index 406aff3d72c..61053aff91a 100644 --- a/app/assets/stylesheets/framework/panels.scss +++ b/app/assets/stylesheets/framework/panels.scss @@ -1,9 +1,11 @@ .panel { margin-bottom: $gl-padding; - + .panel-heading { - padding: 10px $gl-padding; + padding: 7px $gl-padding; + line-height: 42px !important; } + .panel-body { padding: $gl-padding; -- cgit v1.2.1 From badeb82f63e416eb7a7c3bf7def7fc3d5ba10222 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:54:13 +0100 Subject: Use consistent padding for .gitlab-ci.yml info callouts --- app/assets/stylesheets/framework/callout.scss | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/stylesheets/framework/callout.scss b/app/assets/stylesheets/framework/callout.scss index f3ce4e3c219..20a9bfb9816 100644 --- a/app/assets/stylesheets/framework/callout.scss +++ b/app/assets/stylesheets/framework/callout.scss @@ -7,8 +7,8 @@ /* Common styles for all types */ .bs-callout { - margin: 20px 0; - padding: 20px; + margin: $gl-padding 0; + padding: $gl-padding; border-left: 3px solid $border-color; color: $text-color; background: $background-color; @@ -42,4 +42,3 @@ border-color: #5cA64d; color: #3c763d; } - -- cgit v1.2.1 From 3eb209123de65fceec260914fc59f772043b174d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:54:32 +0100 Subject: Render monospace text at 90% of the parent font-size --- app/assets/stylesheets/framework/typography.scss | 1 + app/assets/stylesheets/pages/merge_requests.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index aef338cfa56..c3e4ad0ad00 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -220,6 +220,7 @@ pre { .monospace { font-family: $monospace_font; + font-size: 90%; } code { diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index f21ad694d06..6a1d3bd19d3 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -136,7 +136,7 @@ font-family: $monospace_font; font-weight: bold; overflow: hidden; - font-size: 14px; + font-size: 90%; margin: 0 3px; } -- cgit v1.2.1 From 86a09cfaf1f1b8106f1538e8bf5a1aac5f086554 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:55:38 +0100 Subject: `builds_enabled` rather than `ci_enabled` --- app/controllers/projects/application_controller.rb | 2 +- app/controllers/projects/graphs_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index d3f926b62bc..c2aaf094e68 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -28,7 +28,7 @@ class Projects::ApplicationController < ApplicationController private - def ci_enabled + def builds_enabled return render_404 unless @project.builds_enabled? end diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 418b92040bc..734697839c6 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -5,7 +5,7 @@ class Projects::GraphsController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_download_code! - before_action :ci_enabled, only: :ci + before_action :builds_enabled, only: :ci def show respond_to do |format| -- cgit v1.2.1 From 53a6f0b19472e86a8571a1ee35eb3a2b86264a3d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:55:53 +0100 Subject: Fix profile private token form actions --- app/views/profiles/accounts/show.html.haml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 319bdd57c39..17e47c622ce 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -26,11 +26,11 @@ - else %span You don`t have one yet. Click generate to fix it. - .form-actions - - if current_user.private_token - = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default btn-build-token" - - else - = f.submit 'Generate', class: "btn btn-default btn-build-token" + .form-actions + - if current_user.private_token + = f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default" + - else + = f.submit 'Generate', class: "btn btn-default" - unless current_user.ldap_user? .panel.panel-default -- cgit v1.2.1 From f8fdec687f29b2d3ae6405725696fea65967214e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:56:12 +0100 Subject: Fix padding around protected branches well --- app/views/projects/protected_branches/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 2541105b007..cfd7e1534ca 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -3,7 +3,7 @@ %p.light Keep stable branches secure and force developers to use Merge Requests %hr -.well.append-bottom-20 +.well %p Protected branches are designed to %ul %li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"} -- cgit v1.2.1 From f523c3c6856ea3cee883579dfe5a9ad63274fd89 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:56:27 +0100 Subject: Use gray content block for network graph header --- app/views/projects/network/_head.html.haml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml index 415c98ec6a6..9e0e0dc6bb0 100644 --- a/app/views/projects/network/_head.html.haml +++ b/app/views/projects/network/_head.html.haml @@ -1,3 +1,6 @@ -.append-bottom-20 - = render partial: 'shared/ref_switcher', locals: {destination: 'graph'} - .pull-right.visible-lg.light You can move around the graph by using the arrow keys. +.gray-content-block.top-block.append-bottom-default + .tree-ref-holder + = render partial: 'shared/ref_switcher', locals: {destination: 'graph'} + + .oneline + You can move around the graph by using the arrow keys. -- cgit v1.2.1 From 3f1e72a0cc18ac20481224c41cb2ad30dfbe7ab1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:57:03 +0100 Subject: Memoize ci_yaml_file. --- 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 971e899de84..cb90b0de63d 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -199,7 +199,7 @@ module Ci end def ci_yaml_file - gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data + @ci_yaml_file ||= gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data rescue nil end -- cgit v1.2.1 From 90a46535935115cc3556d6bc0d282938758cd29a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:57:30 +0100 Subject: Remove top border from build page --- app/views/projects/builds/show.html.haml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index 5a55cb8f8d3..dc6f9a82b50 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -2,10 +2,9 @@ = render "header_title" .build-page - .gray-content-block + .gray-content-block.top-block Build ##{@build.id} for commit - %strong.monospace - = link_to @build.commit.short_sha, ci_status_path(@build.commit) + %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit) from = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref) @@ -22,7 +21,6 @@ - else = build.id - - if @build.retried? %li.active %a @@ -31,7 +29,7 @@ %i.fa.fa-warning This build was retried. - .gray-content-block.second-block + .gray-content-block.middle-block .build-head .clearfix = ci_status_with_icon(@build.status) @@ -140,7 +138,7 @@ %h4.title Commit .pull-right - %small + %small = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" %p %span.attr-name Branch: @@ -162,7 +160,7 @@ - if @builds.present? .build-widget - %h4.title #{pluralize(@builds.count(:id), "other build")} for + %h4.title #{pluralize(@builds.count(:id), "other build")} for = succeed ":" do = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace" %table.table.builds -- cgit v1.2.1 From 1b5302f8e7050c5c4cd4e7d4cc63be3cd3fbbcb5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:57:49 +0100 Subject: Reduce MR/issue duplication --- app/views/projects/issues/_discussion.html.haml | 5 +---- app/views/projects/merge_requests/_discussion.html.haml | 4 ++-- app/views/projects/merge_requests/show/_participants.html.haml | 4 ---- app/views/shared/issuable/_participants.html.haml | 5 +++++ 4 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 app/views/projects/merge_requests/show/_participants.html.haml create mode 100644 app/views/shared/issuable/_participants.html.haml diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index b5f522f2079..21eee70b424 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -12,10 +12,7 @@ .col-md-9 .votes-holder.pull-right #votes= render 'votes/votes_block', votable: @issue - .participants - %span= pluralize(@participants.count, 'participant') - - @participants.each do |participant| - = link_to_member(@project, participant, name: false, size: 24) + = render "shared/issuable/participants" .col-md-3 .input-group.cross-project-reference %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index ea462561668..3d7bd78dce3 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -12,7 +12,7 @@ .col-md-9 .votes-holder.pull-right #votes= render 'votes/votes_block', votable: @merge_request - = render "projects/merge_requests/show/participants" + = render "shared/issuable/participants" .col-md-3 .input-group.cross-project-reference %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} @@ -21,7 +21,7 @@ .row %section.col-md-9 - = render "projects/notes/notes_with_form" + .voting_notes#notes= render "projects/notes/notes_with_form" %aside.col-md-3 .issuable-affix .context diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/projects/merge_requests/show/_participants.html.haml deleted file mode 100644 index c67afe963e7..00000000000 --- a/app/views/projects/merge_requests/show/_participants.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -.participants - %span #{@participants.count} participants - - @participants.each do |participant| - = link_to_member(@project, participant, name: false, size: 24) diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml new file mode 100644 index 00000000000..b4e0def48b6 --- /dev/null +++ b/app/views/shared/issuable/_participants.html.haml @@ -0,0 +1,5 @@ +.participants + %span + = pluralize @participants.count, "participant" + - @participants.each do |participant| + = link_to_member(@project, participant, name: false, size: 24) -- cgit v1.2.1 From b53769aedc30d40730ba73928c6dbd61cd358ef0 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:58:30 +0100 Subject: Change "Cancel all" to "Cancel running" --- app/views/projects/builds/index.html.haml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index dab7164153f..742676305a9 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -3,10 +3,10 @@ .project-issuable-filter .controls - - if @ci_project && current_user && can?(current_user, :manage_builds, @project) + - if @ci_project && can?(current_user, :manage_builds, @project) .pull-left.hidden-xs - if @all_builds.running_or_pending.any? - = link_to 'Cancel all', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post + = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post %ul.center-top-menu %li{class: ('active' if @scope.nil?)} @@ -50,4 +50,3 @@ = render 'projects/commit_statuses/commit_status', commit_status: build, commit_sha: true, stage: true, allow_retry: true = paginate @builds, theme: 'gitlab' - -- cgit v1.2.1 From 526e64e0c4f43c21cdaa17969be30f2ff0c3793b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:58:51 +0100 Subject: Show "#123" instead of "Builds #123" to make column smaller --- app/views/projects/commit_statuses/_commit_status.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index 9a0e7bff3f1..615296a575b 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -5,9 +5,9 @@ %td.commit_status-link - if commit_status.target_url = link_to commit_status.target_url do - %strong Build ##{commit_status.id} + %strong ##{commit_status.id} - else - %strong Build ##{commit_status.id} + %strong ##{commit_status.id} - if commit_status.show_warning? %i.fa.fa-warning.text-warning -- cgit v1.2.1 From 35fd7112bfc65d5773a64359f108d3c252885479 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 12:59:13 +0100 Subject: Link builds list status to build details --- app/views/projects/commit_statuses/_commit_status.html.haml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index 615296a575b..a527bb2f84a 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -1,6 +1,11 @@ %tr.commit_status %td.status - = ci_status_with_icon(commit_status.status) + - if commit_status.target_url + = link_to commit_status.target_url, class: "ci-status ci-#{commit_status.status}" do + = ci_icon_for_status(commit_status.status) + = commit_status.status + - else + = ci_status_with_icon(commit_status.status) %td.commit_status-link - if commit_status.target_url -- cgit v1.2.1 From 9907a7e6ed7dbfac4c927cefd16ac8e4b3c681f6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:02:13 +0100 Subject: Get ci_commit in MR controller --- app/controllers/projects/merge_requests_controller.rb | 2 ++ app/views/projects/merge_requests/widget/_heading.html.haml | 9 ++++----- app/views/projects/merge_requests/widget/open/_accept.html.haml | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 3f47f2ddb2c..c5fb49de46a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -264,6 +264,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request_diff = @merge_request.merge_request_diff + @ci_commit = @merge_request.ci_commit + if @merge_request.locked_long_ago? @merge_request.unlock_mr @merge_request.close diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index ba5ad22bca7..49aab961712 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,13 +1,12 @@ -- ci_commit = @merge_request.ci_commit -- if ci_commit - - status = ci_commit.status +- if @ci_commit + - status = @ci_commit.status .mr-widget-heading .ci_widget{class: "ci-#{status}"} - = ci_status_icon(ci_commit) + = ci_status_icon(@ci_commit) %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_status_path(ci_commit) + = link_to "View build details", ci_status_path(@ci_commit) - elsif @merge_request.has_ci? - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml index 9b31014b581..6d12af16140 100644 --- a/app/views/projects/merge_requests/widget/open/_accept.html.haml +++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml @@ -1,4 +1,4 @@ -- status_class = @merge_request.ci_commit ? " ci-#{@merge_request.ci_commit.status}" : nil +- status_class = @ci_commit ? " ci-#{@ci_commit.status}" : nil = form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-requires-input' } do |f| = hidden_field_tag :authenticity_token, form_authenticity_token -- cgit v1.2.1 From a17ba43bfd05cd49bab18d6c7f80226004870bc2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:03:28 +0100 Subject: Move commit builds to partial --- app/controllers/projects/commit_controller.rb | 8 ++-- app/views/projects/commit/_builds.html.haml | 67 ++++++++++++++++++++++++++ app/views/projects/commit/_ci_menu.html.haml | 2 +- app/views/projects/commit/builds.html.haml | 68 +-------------------------- 4 files changed, 73 insertions(+), 72 deletions(-) create mode 100644 app/views/projects/commit/_builds.html.haml diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 3f137440e28..e8af205b788 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -37,7 +37,7 @@ class Projects::CommitController < Projects::ApplicationController def cancel_builds ci_commit.builds.running_or_pending.each(&:cancel) - redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha) + redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha) end def retry_builds @@ -47,7 +47,7 @@ class Projects::CommitController < Projects::ApplicationController end end - redirect_to builds_namespace_project_commit_path(project.namespace, project, commit.sha) + redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha) end def branches @@ -74,8 +74,8 @@ class Projects::CommitController < Projects::ApplicationController end @notes_count = commit.notes.count - - @builds = ci_commit.builds if ci_commit + + @statuses = ci_commit.statuses if ci_commit end def authorize_manage_builds! diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml new file mode 100644 index 00000000000..e4d81182c1a --- /dev/null +++ b/app/views/projects/commit/_builds.html.haml @@ -0,0 +1,67 @@ +.gray-content-block.middle-block + .pull-right + - if @ci_project && can?(current_user, :manage_builds, @ci_commit.gl_project) + - if @ci_commit.builds.latest.failed.any?(&:retryable?) + = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post + + - if @ci_commit.builds.running_or_pending.any? + = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post + + .oneline + = pluralize @statuses.count(:id), "build" + - if defined?(link_to_commit) && link_to_commit + for commit + = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: "monospace" + - if @ci_commit.duration > 0 + in + = time_interval_in_words @ci_commit.duration + +- if @ci_commit.yaml_errors.present? + .bs-callout.bs-callout-danger + %h4 Found errors in your .gitlab-ci.yml: + %ul + - @ci_commit.yaml_errors.split(",").each do |error| + %li= error + +- if @ci_commit.gl_project.builds_enabled? && !@ci_commit.ci_yaml_file + .bs-callout.bs-callout-warning + \.gitlab-ci.yml not found in this commit + +.table-holder + %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, + locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true } + +- if @ci_commit.retried.any? + .gray-content-block.second-block + Retried builds + + .table-holder + %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 + = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, + locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true } diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml index 76dc87a8824..f74f8b427ec 100644 --- a/app/views/projects/commit/_ci_menu.html.haml +++ b/app/views/projects/commit/_ci_menu.html.haml @@ -6,4 +6,4 @@ = nav_link(path: 'commit#builds') do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do Builds - %span.badge= @builds.count(:id) + %span.badge= @statuses.count diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml index 00cf9c76102..99d62503a94 100644 --- a/app/views/projects/commit/builds.html.haml +++ b/app/views/projects/commit/builds.html.haml @@ -3,70 +3,4 @@ = render "commit_box" = render "ci_menu" - -- if @ci_commit.yaml_errors.present? - .bs-callout.bs-callout-danger - %h4 Found errors in your .gitlab-ci.yml: - %ul - - @ci_commit.yaml_errors.split(",").each do |error| - %li= error - -- unless @ci_commit.ci_yaml_file - .bs-callout.bs-callout-warning - \.gitlab-ci.yml not found in this commit - -.gray-content-block.second-block - Latest builds - - .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.latest.failed.any?(&:retryable?) - = link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-primary', method: :post - - - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-xs btn-danger', method: :post - -.table-holder - %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, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true } - -- if @ci_commit.retried.any? - .gray-content-block.second-block - Retried builds - - .table-holder - %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 - = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true } += render "builds" -- cgit v1.2.1 From 1567572e79bbeace4a68f00c01e64ed0dad9106a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:04:24 +0100 Subject: Add Builds tab to MR detail page --- .../javascripts/merge_request_tabs.js.coffee | 22 +++++++++++- .../projects/merge_requests_controller.rb | 41 +++++++++++++++------- .../projects/merge_requests/_new_submit.html.haml | 8 +++++ app/views/projects/merge_requests/_show.html.haml | 10 ++++-- .../projects/merge_requests/show/_builds.html.haml | 1 + .../merge_requests/widget/_heading.html.haml | 2 +- config/routes.rb | 1 + 7 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 app/views/projects/merge_requests/show/_builds.html.haml diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 593a8f42130..69a12fdd045 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -43,6 +43,7 @@ # class @MergeRequestTabs diffsLoaded: false + buildsLoaded: false commitsLoaded: false constructor: (@opts = {}) -> @@ -54,6 +55,12 @@ class @MergeRequestTabs bindEvents: -> $(document).on 'shown.bs.tab', '.merge-request-tabs a[data-toggle="tab"]', @tabShown + $(document).on 'click', '.js-show-tab', @showTab + + showTab: (event) => + event.preventDefault() + + @activateTab $(event.target).data('action') tabShown: (event) => $target = $(event.target) @@ -61,6 +68,8 @@ class @MergeRequestTabs if action == 'commits' @loadCommits($target.attr('href')) + else if action == 'builds' + @loadBuilds($target.attr('href')) else if action == 'diffs' @loadDiff($target.attr('href')) @@ -101,7 +110,7 @@ class @MergeRequestTabs action = 'notes' if action == 'show' # Remove a trailing '/commits' or '/diffs' - new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '') + new_state = @_location.pathname.replace(/\/(commits|builds|diffs)(\.html)?\/?$/, '') # Append the new action if we're on a tab other than 'notes' unless action == 'notes' @@ -129,6 +138,17 @@ class @MergeRequestTabs @commitsLoaded = true @scrollToElement("#commits") + loadBuilds: (source) -> + return if @buildsLoaded + + @_get + url: "#{source}.json" + success: (data) => + document.getElementById('builds').innerHTML = data.html + $('.js-timeago').timeago() + @buildsLoaded = true + @scrollToElement("#builds") + loadDiff: (source) -> return if @diffsLoaded diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 3f47f2ddb2c..04642294cd3 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -1,13 +1,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :module_enabled before_action :merge_request, only: [ - :edit, :update, :show, :diffs, :commits, :merge, :merge_check, + :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check, :ci_status, :toggle_subscription ] - before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits] - before_action :validates_merge_request, only: [:show, :diffs, :commits] - before_action :define_show_vars, only: [:show, :diffs, :commits] - before_action :ensure_ref_fetched, only: [:show, :commits, :diffs] + before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds] + before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] + before_action :define_show_vars, only: [:show, :diffs, :commits, :builds] + before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds] # Allow read any merge_request before_action :authorize_read_merge_request! @@ -79,6 +79,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + def builds + @ci_project = @merge_request.source_project.gitlab_ci_project + + respond_to do |format| + format.html { render 'show' } + format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } } + end + end + def new params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute @@ -91,20 +100,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController @target_project = merge_request.target_project @source_project = merge_request.source_project - @commits = @merge_request.compare_commits + @commits = @merge_request.compare_commits.reverse @commit = @merge_request.last_commit @first_commit = @merge_request.first_commit @diffs = @merge_request.compare_diffs + + @ci_project = @source_project.gitlab_ci_project + @ci_commit = @merge_request.ci_commit + @statuses = @ci_commit.statuses if @ci_commit + @note_counts = Note.where(commit_id: @commits.map(&:id)). group(:commit_id).count end - def edit - @source_project = @merge_request.source_project - @target_project = @merge_request.target_project - @target_branches = @merge_request.target_project.repository.branch_names - end - def create @target_branches ||= [] @merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute @@ -118,6 +126,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + def edit + @source_project = @merge_request.source_project + @target_project = @merge_request.target_project + @target_branches = @merge_request.target_project.repository.branch_names + end + def update @merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request) @@ -264,6 +278,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request_diff = @merge_request.merge_request_diff + @ci_commit = @merge_request.ci_commit + @statuses = @ci_commit.statuses if @ci_commit + if @merge_request.locked_long_ago? @merge_request.unlock_mr @merge_request.close diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 156922cea41..f0a821d2d9f 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -23,6 +23,11 @@ = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size + - if @ci_commit + %li.builds-tab.active + = link_to url_for(params), data: {target: '#builds', action: 'builds', toggle: 'tab'} do + Builds + %span.badge= @statuses.size %li.diffs-tab.active = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do Changes @@ -31,6 +36,9 @@ .tab-content #commits.commits.tab-pane = render "projects/merge_requests/show/commits" + - if @ci_commit + #builds.builds.tab-pane + = render "projects/merge_requests/show/builds" #diffs.diffs.tab-pane.active - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index f5aff0877e7..79d71a784c1 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -26,8 +26,7 @@ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) .normal %span Request to merge - %span.label-branch - = source_branch_with_namespace(@merge_request) + %span.label-branch= source_branch_with_namespace(@merge_request) %span into = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do = @merge_request.target_branch @@ -51,6 +50,11 @@ = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size + - if @ci_commit + %li.builds-tab + = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do + Builds + %span.badge= @statuses.size %li.diffs-tab = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do Changes @@ -61,6 +65,8 @@ = render "projects/merge_requests/discussion" #commits.commits.tab-pane - # This tab is always loaded via AJAX + #builds.builds.tab-pane + - # This tab is always loaded via AJAX #diffs.diffs.tab-pane - # This tab is always loaded via AJAX diff --git a/app/views/projects/merge_requests/show/_builds.html.haml b/app/views/projects/merge_requests/show/_builds.html.haml new file mode 100644 index 00000000000..307a75d02ca --- /dev/null +++ b/app/views/projects/merge_requests/show/_builds.html.haml @@ -0,0 +1 @@ += render "projects/commit/builds", link_to_commit: true diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index ba5ad22bca7..f277014f840 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -7,7 +7,7 @@ %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_status_path(ci_commit) + = link_to "View build details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'} - elsif @merge_request.has_ci? - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX diff --git a/config/routes.rb b/config/routes.rb index 3b151891a6b..38f0b16a412 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -570,6 +570,7 @@ Rails.application.routes.draw do resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do member do get :diffs + get :builds get :commits post :merge get :merge_check -- cgit v1.2.1 From 14969ac87fb83a647572e32c4a0bf0b5d08a487e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:17:05 +0100 Subject: Add '#' to build page title --- app/views/projects/builds/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index dc6f9a82b50..d5e81f84b56 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -1,4 +1,4 @@ -- page_title "#{@build.name} (#{@build.id})", "Builds" +- page_title "#{@build.name} (##{@build.id})", "Builds" = render "header_title" .build-page -- cgit v1.2.1 From c767d0e6bd95e73217a92040d568b34e14b65949 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:04:47 +0100 Subject: Change text of MR widget heading --- app/assets/stylesheets/pages/merge_requests.scss | 4 ++++ .../projects/merge_requests/widget/_heading.html.haml | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index f21ad694d06..ab7df978768 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -81,6 +81,10 @@ &.ci-error { color: $gl-danger; } + + a.monospace { + color: inherit; + } } .mr-widget-body, diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index f277014f840..e94b07eaeaa 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -4,10 +4,12 @@ .mr-widget-heading .ci_widget{class: "ci-#{status}"} = ci_status_icon(ci_commit) - %span CI build #{status} - for #{@merge_request.last_commit_short_sha}. + %span Build #{status} + for + = succeed "." do + = link_to @ci_commit.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @ci_commit.sha), class: "monospace" %span.ci-coverage - = link_to "View build details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'} + = link_to "View details", builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "js-show-tab", data: {action: 'builds'} - elsif @merge_request.has_ci? - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX @@ -21,10 +23,13 @@ - else = icon("circle") %span CI build #{status} - for #{@merge_request.last_commit_short_sha}. + for + - commit = @merge_request.last_commit + = succeed "." do + = link_to commit.short_id, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, commit), class: "monospace" %span.ci-coverage - if ci_build_details_path(@merge_request) - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + = link_to "View details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" .ci_widget = icon("spinner spin") -- cgit v1.2.1 From af6b5437421106caf34719e37d359808b88eb45c Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:04:59 +0100 Subject: Make commit and MR ref filters aware of /builds path --- lib/gitlab/markdown/commit_reference_filter.rb | 11 +++++++++++ lib/gitlab/markdown/merge_request_reference_filter.rb | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb index b4036578e60..e3066a89b04 100644 --- a/lib/gitlab/markdown/commit_reference_filter.rb +++ b/lib/gitlab/markdown/commit_reference_filter.rb @@ -47,6 +47,17 @@ module Gitlab def object_link_title(commit) commit.link_title end + + def object_link_text_extras(object, matches) + extras = super + + path = matches[:path] if matches.names.include?("path") + if path == '/builds' + extras.unshift "builds" + end + + extras + end end end end diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index de71fc76a9b..79d67870b14 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -24,8 +24,14 @@ module Gitlab def object_link_text_extras(object, matches) extras = super - if matches.names.include?("path") && matches[:path] && matches[:path] == '/diffs' + path = matches[:path] if matches.names.include?("path") + case path + when '/diffs' extras.unshift "diffs" + when '/builds' + extras.unshift "builds" + when '/commits' + extras.unshift "commits" end extras -- cgit v1.2.1 From ea5b462a1408f9fcd77dfd2961049057fc8ccad9 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 8 Dec 2015 13:33:12 +0100 Subject: Stub the calls to disk and check what send_file returns. --- spec/controllers/projects/raw_controller_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index cd42a592759..42c6d4f3f88 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -41,16 +41,18 @@ describe Projects::RawController do context 'when project has access' do before do public_project.lfs_objects << lfs_object + allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true) + allow(controller).to receive(:send_file) { controller.render :nothing => true } end it 'serves the file' do + expect(controller).to receive(:send_file).with("#{Gitlab.config.shared.path}/lfs-objects/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", :filename => "lfs_object.iso", :disposition => 'attachment') get(:show, namespace_id: public_project.namespace.to_param, project_id: public_project.to_param, id: id) expect(response.status).to eq(200) - expect(response.header['Content-Type']).to eq('application/octet-stream') end end -- cgit v1.2.1 From 0c3f70acf4838194f517c02874b8423303c21b48 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:34:09 +0100 Subject: Add API group projects specs --- spec/requests/api/groups_spec.rb | 59 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 13cced81875..4cfa49d1566 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -10,6 +10,8 @@ describe API::API, api: true do let(:avatar_file_path) { File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') } let!(:group1) { create(:group, avatar: File.open(avatar_file_path)) } let!(:group2) { create(:group) } + let!(:project1) { create(:project, namespace: group1) } + let!(:project2) { create(:project, namespace: group2) } before do group1.add_owner(user1) @@ -67,7 +69,7 @@ describe API::API, api: true do it "should return any existing group" do get api("/groups/#{group2.id}", admin) expect(response.status).to eq(200) - json_response['name'] == group2.name + expect(json_response['name']).to eq(group2.name) end it "should not return a non existing group" do @@ -80,7 +82,7 @@ describe API::API, api: true do it 'should return any existing group' do get api("/groups/#{group1.path}", admin) expect(response.status).to eq(200) - json_response['name'] == group2.name + expect(json_response['name']).to eq(group1.name) end it 'should not return a non existing group' do @@ -95,6 +97,59 @@ describe API::API, api: true do end end + describe "GET /groups/:id/projects" do + context "when authenticated as user" do + it "should return the group's projects" do + get api("/groups/#{group1.id}/projects", user1) + expect(response.status).to eq(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project1.name) + end + + it "should not return a non existing group" do + get api("/groups/1328/projects", user1) + expect(response.status).to eq(404) + end + + it "should not return a group not attached to user1" do + get api("/groups/#{group2.id}/projects", user1) + expect(response.status).to eq(403) + end + end + + context "when authenticated as admin" do + it "should return any existing group" do + get api("/groups/#{group2.id}/projects", admin) + expect(response.status).to eq(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project2.name) + end + + it "should not return a non existing group" do + get api("/groups/1328/projects", admin) + expect(response.status).to eq(404) + end + end + + context 'when using group path in URL' do + it 'should return any existing group' do + get api("/groups/#{group1.path}/projects", admin) + expect(response.status).to eq(200) + expect(json_response.first['name']).to eq(project1.name) + end + + it 'should not return a non existing group' do + get api('/groups/unknown/projects', admin) + expect(response.status).to eq(404) + end + + it 'should not return a group not attached to user1' do + get api("/groups/#{group2.path}/projects", user1) + expect(response.status).to eq(403) + end + end + end + describe "POST /groups" do context "when authenticated as user without group permissions" do it "should not create group" do -- cgit v1.2.1 From 1a10945066d0da1801bb4cf89ce5f54996f1756f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 13:40:23 +0100 Subject: Fix RedactorFilter --- lib/gitlab/markdown/filter/redactor_filter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/markdown/filter/redactor_filter.rb b/lib/gitlab/markdown/filter/redactor_filter.rb index bea714a01e7..33ef7ce18b5 100644 --- a/lib/gitlab/markdown/filter/redactor_filter.rb +++ b/lib/gitlab/markdown/filter/redactor_filter.rb @@ -27,7 +27,7 @@ module Gitlab def user_can_reference?(node) if node.has_attribute?('data-reference-filter') reference_type = node.attr('data-reference-filter') - reference_filter = reference_type.constantize + reference_filter = Gitlab::Markdown.const_get(reference_type) reference_filter.user_can_reference?(current_user, node, context) else -- cgit v1.2.1 From f36fe92fcbda02ec130889cc508cab5812fd2b20 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 04:43:17 -0800 Subject: Bump gollum-lib to 4.1.0 and fix dependency mismatch with rouge Closes #3767 --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 604efe51a3a..0eb5e63a1a7 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.3.0 (unreleased) + - Bump gollum-lib to 4.1.0 (Stan Hu) - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Handle and report SSL errors in Web hook test (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) diff --git a/Gemfile b/Gemfile index fc4d565fc84..91ad4b6fb4d 100644 --- a/Gemfile +++ b/Gemfile @@ -52,7 +52,7 @@ gem "gitlab_git", '~> 7.2.20' gem 'gitlab_omniauth-ldap', '~> 1.2.1', require: "omniauth-ldap" # Git Wiki -gem 'gollum-lib', '~> 4.0.2' +gem 'gollum-lib', '~> 4.1.0' # Language detection gem "github-linguist", "~> 4.7.0", require: "linguist" diff --git a/Gemfile.lock b/Gemfile.lock index 5d70788d981..d8858bd1f09 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -329,11 +329,11 @@ GEM activesupport (>= 4.1.0) gollum-grit_adapter (1.0.0) gitlab-grit (~> 2.7, >= 2.7.1) - gollum-lib (4.0.3) + gollum-lib (4.1.0) github-markup (~> 1.3.3) gollum-grit_adapter (~> 1.0) nokogiri (~> 1.6.4) - rouge (~> 1.10.1) + rouge (~> 1.9) sanitize (~> 2.1.0) stringex (~> 2.5.1) gon (6.0.1) @@ -884,7 +884,7 @@ DEPENDENCIES gitlab_git (~> 7.2.20) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) - gollum-lib (~> 4.0.2) + gollum-lib (~> 4.1.0) gon (~> 6.0.1) grape (~> 0.13.0) grape-entity (~> 0.4.2) -- cgit v1.2.1 From 6245be083d985df3dd5daebb78ecf300bacff7b6 Mon Sep 17 00:00:00 2001 From: Marin Jankovski Date: Tue, 8 Dec 2015 14:00:15 +0100 Subject: All for you rubocop. --- spec/controllers/projects/raw_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 42c6d4f3f88..1caa476d37d 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -42,11 +42,11 @@ describe Projects::RawController do before do public_project.lfs_objects << lfs_object allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true) - allow(controller).to receive(:send_file) { controller.render :nothing => true } + allow(controller).to receive(:send_file) { controller.render nothing: true } end it 'serves the file' do - expect(controller).to receive(:send_file).with("#{Gitlab.config.shared.path}/lfs-objects/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", :filename => "lfs_object.iso", :disposition => 'attachment') + expect(controller).to receive(:send_file).with("#{Gitlab.config.shared.path}/lfs-objects/91/ef/f75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897", filename: "lfs_object.iso", disposition: 'attachment') get(:show, namespace_id: public_project.namespace.to_param, project_id: public_project.to_param, -- cgit v1.2.1 From b2b548de9d74b01816baca822d39f9dd543bbbf7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 14:02:34 +0100 Subject: Rewrite docs --- doc/workflow/README.md | 1 + .../disable_merge_when_build_succeeds.png | Bin 20551 -> 0 bytes .../enable_merge_when_build_succeeds.png | Bin 13150 -> 0 bytes doc/workflow/merge_when_build_succeeds.md | 19 +++++++------------ doc/workflow/merge_when_build_succeeds/enable.png | Bin 0 -> 151112 bytes doc/workflow/merge_when_build_succeeds/status.png | Bin 0 -> 180318 bytes 6 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 doc/workflow/merge_requests/disable_merge_when_build_succeeds.png delete mode 100644 doc/workflow/merge_requests/enable_merge_when_build_succeeds.png create mode 100644 doc/workflow/merge_when_build_succeeds/enable.png create mode 100644 doc/workflow/merge_when_build_succeeds/status.png diff --git a/doc/workflow/README.md b/doc/workflow/README.md index a6b4d951188..d2642495c9a 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -17,4 +17,5 @@ - [Milestones](milestones.md) - [Merge Requests](merge_requests.md) - ["Work In Progress" Merge Requests](wip_merge_requests.md) +- [Merge When Build Succeeds](merge_when_build_succeeds.md) - [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md) diff --git a/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png b/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png deleted file mode 100644 index a45a4890b62..00000000000 Binary files a/doc/workflow/merge_requests/disable_merge_when_build_succeeds.png and /dev/null differ diff --git a/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png b/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png deleted file mode 100644 index 62a46c9508b..00000000000 Binary files a/doc/workflow/merge_requests/enable_merge_when_build_succeeds.png and /dev/null differ diff --git a/doc/workflow/merge_when_build_succeeds.md b/doc/workflow/merge_when_build_succeeds.md index 9bf6ddcc569..3c055650c49 100644 --- a/doc/workflow/merge_when_build_succeeds.md +++ b/doc/workflow/merge_when_build_succeeds.md @@ -1,20 +1,15 @@ # Merge When Build Succeeds -Select a Merge Request to be merged if the build succeeds so the user does not have to wait for the build to finish and revisit the Merge Request to merge it after the build is done. +When reviewing a merge request that looks ready to merge but still has one or more CI builds running, you can set it to be merged automatically when the build succeeds. This way, you don't have to wait for the build to finish and remember to merge the merge request then. -## Enabling for a Merge Request +![Enable](merge_when_build_succeeds/enable.png) -Given an active build for a Merge Request, thus pending or running, a `Merge When Build Succeeds` button will appear to any user which can merge it. Once clicked, it ensures this merge request is merged when the build is successful. -When clicking the button, the merge parameters are also saved to allow the merge user to edit the commit message and remove the source branch if he can remove that branch. +When you hit the "Merge When Build Succeeds" button, the status of the Merge Request will be updated to represent the impending merge. If you cannot wait for the build to succeed and want to build immediately, this option is available in the dropdown menu on the right of the main button. -When this feature is enabled, a message will appear to notify other users. Also a note is posted on the thread. +Both team developers and the author of the merge request have the option to cancel the automatic merge when they find a reason it shouldn't be merged after all. -![Enable Merge When Build Succceeds](merge_requests/enable_merge_when_build_succeeds.png) +![Status](merge_when_build_succeeds/status.png) -## Canceling +When the build succeeds, the merge request will automatically be merged. When the build fails, the author gets a chance to retry any failed builds, or to push new commits to fix the failure. -The automatic merge can be disabled by clicking the `Cancel Automatic Merge` button, or when a new commit is added to the Merge Request. In the former case a note is posted. In the latter case a user able to merge can enable the feature again. - -![Disable the automatic merge](merge_requests/disable_merge_when_build_succeeds.png) - -A failed build does not reset the automatic build so a build can be retried. +When the builds are retried and succeed on the second try, the merge request will automatically be merged after all. When the merge request is updated with new commits, the automatic merge is automatically canceled to allow the new changes to be reviewed. diff --git a/doc/workflow/merge_when_build_succeeds/enable.png b/doc/workflow/merge_when_build_succeeds/enable.png new file mode 100644 index 00000000000..633efa1246f Binary files /dev/null and b/doc/workflow/merge_when_build_succeeds/enable.png differ diff --git a/doc/workflow/merge_when_build_succeeds/status.png b/doc/workflow/merge_when_build_succeeds/status.png new file mode 100644 index 00000000000..c856c7d14dc Binary files /dev/null and b/doc/workflow/merge_when_build_succeeds/status.png differ -- cgit v1.2.1 From ab85d9694dff12eb5646e5468cdcc820a647b396 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 14:13:03 +0100 Subject: Fix spec --- spec/features/builds_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 5213ce1099f..1f99a808f87 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -19,7 +19,7 @@ describe "Builds" do end it { expect(page).to have_content 'Running' } - it { expect(page).to have_content 'Cancel all' } + it { expect(page).to have_content 'Cancel running' } it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } @@ -32,7 +32,7 @@ describe "Builds" do end it { expect(page).to have_content 'No builds to show' } - it { expect(page).to have_content 'Cancel all' } + it { expect(page).to have_content 'Cancel running' } end context "All builds" do @@ -45,7 +45,7 @@ describe "Builds" do it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } - it { expect(page).to_not have_content 'Cancel all' } + it { expect(page).to_not have_content 'Cancel running' } end end @@ -53,11 +53,11 @@ describe "Builds" do before do @build.run! visit namespace_project_builds_path(@gl_project.namespace, @gl_project) - click_link "Cancel all" + click_link "Cancel running" end it { expect(page).to have_content 'No builds to show' } - it { expect(page).to_not have_content 'Cancel all' } + it { expect(page).to_not have_content 'Cancel running' } end describe "GET /:project/builds/:id" do -- cgit v1.2.1 From 5beacba038c097e513b46f24ee26d5065ad419c9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 14:13:57 +0100 Subject: Fix spec --- features/steps/project/commits/commits.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index e5b3f27135d..0d6a9a8fc66 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 "Latest builds" + expect(page).to have_content "1 build" end end -- cgit v1.2.1 From 41a4785b855a082197b3c22004cb8af96e5453ee Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 14:41:19 +0100 Subject: Fix signin with OmniAuth providers --- config/initializers/omniauth.rb | 2 +- lib/omni_auth/request_forgery_protection.rb | 63 +++++------------------------ 2 files changed, 10 insertions(+), 55 deletions(-) diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 70ed10e8275..4c164119fff 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -16,7 +16,7 @@ OmniAuth.config.allowed_request_methods = [:post] #In case of auto sign-in, the GET method is used (users don't get to click on a button) OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? OmniAuth.config.before_request_phase do |env| - OmniAuth::RequestForgeryProtection.new(env).call + OmniAuth::RequestForgeryProtection.call(env) end if Gitlab.config.omniauth.enabled diff --git a/lib/omni_auth/request_forgery_protection.rb b/lib/omni_auth/request_forgery_protection.rb index 3557522d3c9..69155131d8d 100644 --- a/lib/omni_auth/request_forgery_protection.rb +++ b/lib/omni_auth/request_forgery_protection.rb @@ -1,66 +1,21 @@ # Protects OmniAuth request phase against CSRF. module OmniAuth - # Based on ActionController::RequestForgeryProtection. - class RequestForgeryProtection - def initialize(env) - @env = env - end - - def request - @request ||= ActionDispatch::Request.new(@env) - end - - def session - request.session - end - - def reset_session - request.reset_session - end - - def params - request.params - end - - def call - verify_authenticity_token - end + module RequestForgeryProtection + class Controller < ActionController::Base + protect_from_forgery with: :exception - def verify_authenticity_token - if !verified_request? - Rails.logger.warn "Can't verify CSRF token authenticity" if Rails.logger - handle_unverified_request + def index + head :ok end end - private - - def protect_against_forgery? - ApplicationController.allow_forgery_protection - end - - def request_forgery_protection_token - ApplicationController.request_forgery_protection_token - end - - def forgery_protection_strategy - ApplicationController.forgery_protection_strategy - end - - def verified_request? - !protect_against_forgery? || request.get? || request.head? || - form_authenticity_token == params[request_forgery_protection_token] || - form_authenticity_token == request.headers['X-CSRF-Token'] - end - - def handle_unverified_request - forgery_protection_strategy.new(self).handle_unverified_request + def self.app + @app ||= Controller.action(:index) end - # Sets the token value for the current session. - def form_authenticity_token - session[:_csrf_token] ||= SecureRandom.base64(32) + def self.call(env) + app.call(env) end end end -- cgit v1.2.1 From f3ca92a062424e0cda2c077d9c30a4edbd6bf4c8 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 8 Dec 2015 15:08:22 +0100 Subject: Add 'resume' capability to parallel-rsync-repos --- bin/parallel-rsync-repos | 43 ++++++++++++++++++++---------- doc/operations/moving_repositories.md | 50 ++++++++++++++++++++++++++++++----- 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/bin/parallel-rsync-repos b/bin/parallel-rsync-repos index b777056c95f..21921148fa0 100755 --- a/bin/parallel-rsync-repos +++ b/bin/parallel-rsync-repos @@ -1,29 +1,31 @@ -#!/bin/sh -# this script should run as the 'git' user, not root, because of mkdir +#!/usr/bin/env bash +# this script should run as the 'git' user, not root, because 'root' should not +# own intermediate directories created by rsync. # # Example invocation: # find /var/opt/gitlab/git-data/repositories -maxdepth 2 | \ -# parallel-rsync-repos /var/opt/gitlab/git-data/repositories /mnt/gitlab/repositories +# parallel-rsync-repos transfer-success.log /var/opt/gitlab/git-data/repositories /mnt/gitlab/repositories # # You can also rsync to a remote destination. # -# parallel-rsync-repos /var/opt/gitlab/git-data/repositories user@host:/mnt/gitlab/repositories +# parallel-rsync-repos transfer-success.log /var/opt/gitlab/git-data/repositories user@host:/mnt/gitlab/repositories # # If you need to pass extra options to rsync, set the RSYNC variable # -# env RSYNC='rsync --rsh="foo bar"' parallel-rsync-repos /src dest +# env RSYNC='rsync --rsh="foo bar"' parallel-rsync-repos transfer-success.log /src dest # -SRC=$1 -DEST=$2 +LOGFILE=$1 +SRC=$2 +DEST=$3 -if [ -z "$JOBS" ] ; then - JOBS=10 +if [ -z "$LOGFILE" ] || [ -z "$SRC" ] || [ -z "$DEST" ] ; then + echo "Usage: $0 LOGFILE SRC DEST" + exit 1 fi -if [ -z "$SRC" ] || [ -z "$DEST" ] ; then - echo "Usage: $0 SRC DEST" - exit 1 +if [ -z "$JOBS" ] ; then + JOBS=10 fi if [ -z "$RSYNC" ] ; then @@ -35,5 +37,18 @@ if ! cd $SRC ; then exit 1 fi -sed "s|$SRC|./|" |\ - parallel -j$JOBS --progress "mkdir -p $DEST/{} && $RSYNC --delete -a {}/. $DEST/{}/" +rsyncjob() { + relative_dir="./${1#$SRC}" + + if ! $RSYNC --delete --relative -a "$relative_dir" "$DEST" ; then + echo "rsync $1 failed" + return 1 + fi + + echo "$1" >> $LOGFILE +} + +export LOGFILE SRC DEST RSYNC +export -f rsyncjob + +parallel -j$JOBS --progress rsyncjob diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md index a89602b367f..39086b7a251 100644 --- a/doc/operations/moving_repositories.md +++ b/doc/operations/moving_repositories.md @@ -96,25 +96,59 @@ after switching to the new repository storage directory. ### Parallel rsync for all repositories known to GitLab -This will sync repositories with 10 rsync processes at a time. +This will sync repositories with 10 rsync processes at a time. We keep +track of progress so that the transfer can be restarted if necessary. + +First we create a new directory, owned by 'git', to hold transfer +logs. We assume the directory is empty before we start the transfer +procedure, and that we are the only ones writing files in it. ``` # Omnibus -sudo gitlab-rake gitlab:list_repos |\ - sudo -u git \ +sudo mkdir /var/opt/gitlab/transfer-logs +sudo chown git:git /var/opt/gitlab/transfer-logs + +# Source +sudo -u git -H mkdir /home/git/transfer-logs +``` + +We seed the process with a list of the directories we want to copy. + +``` +# Omnibus +sudo -u git sh -c 'gitlab-rake gitlab:list_repos > /var/opt/gitlab/transfer-logs/all-repos-$(date +%s).txt' + +# Source +cd /home/git/gitlab +sudo -u git -H sh -c 'bundle exec rake gitlab:list_repos > /home/git/transfer-logs/all-repos-$(date +%s).txt' +``` + +Now we can start the transfer. The command below is idempotent, and +the number of jobs done by GNU Parallel should converge to zero. If it +does not some repositories listed in all-repos-1234.txt may have been +deleted/renamed before they could be copied. + +``` +# Omnibus +sudo -u git sh -c ' +cat /var/opt/gitlab/transfer-logs/* | sort | uniq -u |\ /usr/bin/env JOBS=10 \ - /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repoos \ + /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \ + /var/opt/gitlab/transfer-logs/succes-$(date +%s).log \ /var/opt/gitlab/git-data/repositories \ /mnt/gitlab/repositories +' # Source cd /home/git/gitlab -sudo -u git -H bundle exec rake gitlab:list_repos |\ - sudo -u git -H \ +sudo -u git -H sh -c ' +cat /home/git/transfer-logs/* | sort | uniq -u |\ /usr/bin/env JOBS=10 \ bin/parallel-rsync-repos \ + /home/git/transfer-logs/succes-$(date +%s).log \ /home/git/repositories \ /mnt/gitlab/repositories +` ``` ### Parallel rsync only for repositories with recent activity @@ -129,7 +163,8 @@ gitlab:list_repos' to only print repositories with recent activity. sudo gitlab-rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\ sudo -u git \ /usr/bin/env JOBS=10 \ - /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repoos \ + /opt/gitlab/embedded/service/gitlab-rails/bin/parallel-rsync-repos \ + succes-$(date +%s).log \ /var/opt/gitlab/git-data/repositories \ /mnt/gitlab/repositories @@ -139,6 +174,7 @@ sudo -u git -H bundle exec rake gitlab:list_repos SINCE='2015-10-1 12:00 UTC' |\ sudo -u git -H \ /usr/bin/env JOBS=10 \ bin/parallel-rsync-repos \ + succes-$(date +%s).log \ /home/git/repositories \ /mnt/gitlab/repositories ``` -- cgit v1.2.1 From 23f383ef69889c9829ad36afa53b5abfbf4b5511 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 8 Dec 2015 16:06:06 +0100 Subject: Detect project and namespace changes in list:repos --- lib/tasks/gitlab/list_repos.rake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake index 1377e1ea910..c7596e7abcb 100644 --- a/lib/tasks/gitlab/list_repos.rake +++ b/lib/tasks/gitlab/list_repos.rake @@ -3,9 +3,10 @@ namespace :gitlab do scope = Project if ENV['SINCE'] date = Time.parse(ENV['SINCE']) - warn "Listing repositories with activity since #{date}" - project_ids = Project.where(['last_activity_at > ?', date]).pluck(:id) - scope = scope.where(id: project_ids) + warn "Listing repositories with activity or changes since #{date}" + project_ids = Project.where('last_activity_at > ? OR updated_at > ?', date, date).pluck(:id).sort + namespace_ids = Namespace.where(['updated_at > ?', date]).pluck(:id).sort + scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids) end scope.find_each do |project| base = File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace) -- cgit v1.2.1 From d00d3444a45f81a5e79a1161acb44fca2df3c1c7 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Tue, 8 Dec 2015 18:23:30 +0300 Subject: Remove the prepended v on GitLab Workhorse upgrade doc --- doc/update/patch_versions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index 957354decb7..c19ee49f9e0 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -47,7 +47,7 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch -sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` +sudo -u git -H git checkout `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` ``` ### 5. Install libs, migrations, etc. -- cgit v1.2.1 From df6750d3d6b562c8a6a0a57c12dfd694da38a0e8 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 16:42:10 +0100 Subject: Default target branch to patch-n when editing file in protected branch --- app/controllers/projects/blob_controller.rb | 12 ++++++++++-- app/helpers/branches_helper.rb | 2 +- app/helpers/tree_helper.rb | 13 ++++++++++--- app/models/repository.rb | 11 +++++++++++ app/views/shared/_new_commit_form.html.haml | 2 +- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 31a33bfd237..62163682936 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -162,12 +162,20 @@ class Projects::BlobController < Projects::ApplicationController end def sanitized_new_branch_name - @new_branch ||= sanitize(strip_tags(params[:new_branch])) + sanitize(strip_tags(params[:new_branch])) end def editor_variables @current_branch = @ref - @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref + + @new_branch = + if params[:new_branch].present? + sanitized_new_branch_name + elsif ::Gitlab::GitAccess.new(current_user, @project).can_push_to_branch?(@ref) + @ref + else + @repository.next_patch_branch + end @file_path = if action_name.to_s == 'create' diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb index d6eaa7d57bc..e39548e17e1 100644 --- a/app/helpers/branches_helper.rb +++ b/app/helpers/branches_helper.rb @@ -11,7 +11,7 @@ module BranchesHelper def can_push_branch?(project, branch_name) return false unless project.repository.branch_names.include?(branch_name) - + ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name) end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 6afa1aacc5b..ac75c9a14b7 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -48,10 +48,17 @@ module TreeHelper def allowed_tree_edit?(project = nil, ref = nil) project ||= @project - ref ||= @ref - return false unless project.repository.branch_names.include?(ref) + can?(current_user, :push_code, project) + end - ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref) + def tree_edit_branch(project = @project, ref = @ref) + if allowed_tree_edit? + if can_push_branch?(project, ref) + ref + else + project.repository.next_patch_branch + end + end end def can_delete_or_replace?(blob) diff --git a/app/models/repository.rb b/app/models/repository.rb index 1d43307e1e7..1edec52c09e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -329,6 +329,17 @@ class Repository commit(sha) end + def next_patch_branch + patch_branch_ids = self.branch_names.map do |n| + result = n.match(/\Apatch-([0-9]+)\z/) + result[1].to_i if result + end.compact + + highest_patch_branch_id = patch_branch_ids.max || 0 + + "patch-#{highest_patch_branch_id + 1}" + end + # Remove archives older than 2 hours def branches_sorted_by(value) case value diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml index 31b02ed93d0..074080e44c9 100644 --- a/app/views/shared/_new_commit_form.html.haml +++ b/app/views/shared/_new_commit_form.html.haml @@ -4,7 +4,7 @@ .form-group.branch = label_tag 'new_branch', 'Target branch', class: 'control-label' .col-sm-10 - = text_field_tag 'new_branch', @new_branch || @ref, required: true, class: "form-control js-new-branch" + = text_field_tag 'new_branch', @new_branch || tree_edit_branch, required: true, class: "form-control js-new-branch" .form-group.js-create-merge-request-form-group .col-sm-offset-2.col-sm-10 -- cgit v1.2.1 From 3d654cc0faa67e5aee41fda31de095060714ecc4 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 05:19:39 -0800 Subject: Fix broken group avatar upload Closes #3918 --- CHANGELOG | 1 + app/assets/javascripts/dispatcher.js.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index f4f9332b502..c3aaa186ceb 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.3.0 (unreleased) - Bump gollum-lib to 4.1.0 (Stan Hu) + - Fix broken group avatar upload under "New group" (Stan Hu) - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Handle and report SSL errors in Web hook test (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 4059fc39c67..599b4c49540 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -83,7 +83,7 @@ class Dispatcher when 'projects:project_members:index' new ProjectMembers() new UsersSelect() - when 'groups:new', 'groups:edit', 'admin:groups:edit' + when 'groups:new', 'groups:edit', 'admin:groups:edit', 'admin:groups:new' new GroupAvatar() when 'projects:tree:show' new TreeView() -- cgit v1.2.1 From 82ddd738b672afd4ed28f12fb58f512fa117d2be Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 17:10:07 +0100 Subject: Show tooltip when trying to edit file when not on branch --- app/helpers/blob_helper.rb | 38 ++++++++++++-------------- app/helpers/tree_helper.rb | 13 +++++---- app/services/files/base_service.rb | 2 +- app/views/projects/blob/_actions.html.haml | 14 ++++++---- app/views/projects/blob/show.html.haml | 2 +- app/views/projects/tree/_tree_header.html.haml | 4 +++ 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4a3d971f7c6..65bd543bf1a 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -30,26 +30,24 @@ module BlobHelper nil end - if blob_viewable?(blob) - text = 'Edit' - after = options[:after] || '' - from_mr = options[:from_merge_request_id] - link_opts = {} - link_opts[:from_merge_request_id] = from_mr if from_mr - cls = 'btn btn-small' - if allowed_tree_edit?(project, ref) - link_to(text, - namespace_project_edit_blob_path(project.namespace, project, - tree_join(ref, path), - link_opts), - class: cls - ) - else - content_tag :span, text, class: cls + ' disabled' - end + after.html_safe - else - '' - end + return unless blob && blob_editable?(blob) + + text = 'Edit' + after = options[:after] || '' + from_mr = options[:from_merge_request_id] + link_opts = {} + link_opts[:from_merge_request_id] = from_mr if from_mr + cls = 'btn btn-small' + link_to(text, + namespace_project_edit_blob_path(project.namespace, project, + tree_join(ref, path), + link_opts), + class: cls + ) + after.html_safe + end + + def blob_editable?(blob, project = @project, ref = @ref) + allowed_tree_edit?(project, ref) && !blob.lfs_pointer? end def leave_edit_message diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index ac75c9a14b7..886a1e734b5 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -46,13 +46,20 @@ module TreeHelper File.join(*args) end + def on_top_of_branch?(project = @project, ref = @ref) + project.repository.branch_names.include?(ref) + end + def allowed_tree_edit?(project = nil, ref = nil) project ||= @project + ref ||= @ref + return false unless on_top_of_branch?(project, ref) + can?(current_user, :push_code, project) end def tree_edit_branch(project = @project, ref = @ref) - if allowed_tree_edit? + if allowed_tree_edit?(project, ref) if can_push_branch?(project, ref) ref else @@ -61,10 +68,6 @@ module TreeHelper end end - def can_delete_or_replace?(blob) - allowed_tree_edit? && !blob.lfs_pointer? - end - def tree_breadcrumbs(tree, max_links = 2) if @path.present? part_path = "" diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index f50aaf2eb52..309abcd6d0b 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -53,7 +53,7 @@ module Files unless project.empty_repo? unless repository.branch_names.include?(@current_branch) - raise_error("You can only create files if you are on top of a branch") + raise_error("You can only create or edit files when you are on top of a branch") end if @current_branch != @target_branch diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 0e54e59e953..41d8765623e 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,5 +1,4 @@ .btn-group.tree-btn-group - = edit_blob_link(@project, @ref, @path) = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id), class: 'btn btn-sm', target: '_blank' -# only show normal/blame view links for text files @@ -12,11 +11,16 @@ class: 'btn btn-sm' unless @blob.empty? = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-sm' - - if @ref != @commit.sha - = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, - tree_join(@commit.sha, @path)), class: 'btn btn-sm' + = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project, + tree_join(@commit.sha, @path)), class: 'btn btn-sm' -- if can_delete_or_replace?(@blob) +- if blob_editable?(@blob) .btn-group{ role: "group" } + = edit_blob_link(@project, @ref, @path) %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete +- elsif !on_top_of_branch? + .btn-group{ role: "group" } + %button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on top of a branch.", data: {container: 'body'}} Edit + %button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on top of a branch.", data: {container: 'body'}} Replace + %button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on top of a branch.", data: {container: 'body'}} Delete diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml index 09d6fc18e3e..3f8d11ed8c8 100644 --- a/app/views/projects/blob/show.html.haml +++ b/app/views/projects/blob/show.html.haml @@ -6,7 +6,7 @@ %div#tree-holder.tree-holder = render 'blob', blob: @blob -- if can_delete_or_replace?(@blob) +- if blob_editable?(@blob) = render 'projects/blob/remove' - title = "Replace #{@blob.name}" diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 1115ca6b4ca..4767f824ac1 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -30,3 +30,7 @@ = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do = icon('folder fw') New directory + - elsif !on_top_of_branch? + %li + %span.btn.add-to-tree.disabled.has_tooltip{href: '#', title: "You can only add files when you are on top of a branch.", data: {container: 'body'}} + = icon('plus') -- cgit v1.2.1 From 7aeb7077cb906933c59e5b890fd4027005e34d3d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 17:11:23 +0100 Subject: Only show Edit button for text blobs. --- app/helpers/blob_helper.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 65bd543bf1a..68e5d5be600 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -30,7 +30,7 @@ module BlobHelper nil end - return unless blob && blob_editable?(blob) + return unless blob && blob.text? && blob_editable?(blob) text = 'Edit' after = options[:after] || '' @@ -47,7 +47,7 @@ module BlobHelper end def blob_editable?(blob, project = @project, ref = @ref) - allowed_tree_edit?(project, ref) && !blob.lfs_pointer? + !blob.lfs_pointer? && allowed_tree_edit?(project, ref) end def leave_edit_message -- cgit v1.2.1 From 4f074aaa14faa8a866f18a80f58b66cd023a141f Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 25 Nov 2015 14:41:14 +0100 Subject: Introduce CI documentation for services and languages --- doc/README.md | 12 +++ doc/ci/README.md | 12 +++ doc/ci/docker/using_docker_images.md | 115 +++++++++------------- doc/ci/languages/README.md | 3 + doc/ci/languages/php.md | 178 +++++++++++++++++++++++++++++++++++ doc/ci/services/README.md | 6 ++ doc/ci/services/docker-services.md | 5 + doc/ci/services/mysql.md | 72 ++++++++++++++ doc/ci/services/postgres.md | 70 ++++++++++++++ doc/ci/services/redis.md | 40 ++++++++ doc/ci/ssh_keys/README.md | 114 ++++++++++++++++++++++ 11 files changed, 559 insertions(+), 68 deletions(-) create mode 100644 doc/ci/languages/README.md create mode 100644 doc/ci/languages/php.md create mode 100644 doc/ci/services/README.md create mode 100644 doc/ci/services/docker-services.md create mode 100644 doc/ci/services/mysql.md create mode 100644 doc/ci/services/postgres.md create mode 100644 doc/ci/services/redis.md create mode 100644 doc/ci/ssh_keys/README.md diff --git a/doc/README.md b/doc/README.md index 58ab5dd08e0..a7025f7af10 100644 --- a/doc/README.md +++ b/doc/README.md @@ -24,9 +24,21 @@ - [Using Docker Images](ci/docker/using_docker_images.md) - [Using Docker Build](ci/docker/using_docker_build.md) - [Using Variables](ci/variables/README.md) +- [Using SSH keys](ci/ssh_keys/README.md) - [User permissions](ci/permissions/README.md) - [API](ci/api/README.md) +### CI Languages + ++ [Testing PHP](ci/languages/php.md) + +## CI Services + ++ [Using MySQL](ci/services/mysql.md) ++ [Using PostgreSQL](ci/services/postgres.md) ++ [Using Redis](ci/services/redis.md) ++ [Using Other Services](ci/docker/using_docker_images.html#how-to-use-other-images-as-services) + ### CI Examples - [Test and deploy Ruby applications to Heroku](ci/examples/test-and-deploy-ruby-application-to-heroku.md) diff --git a/doc/ci/README.md b/doc/ci/README.md index 97325069ceb..ae921b6f988 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -9,6 +9,18 @@ + [Using Docker Images](docker/using_docker_images.md) + [Using Docker Build](docker/using_docker_build.md) + [Using Variables](variables/README.md) ++ [Using SSH keys](ssh_keys/README.md) + +### Languages + ++ [Testing PHP](languages/php.md) + +### Services + ++ [Using MySQL](services/mysql.md) ++ [Using PostgreSQL](services/postgres.md) ++ [Using Redis](services/redis.md) ++ [Using Other Services](docker/using_docker_images.html#how-to-use-other-images-as-services) ### Examples diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 1feae62b1c7..2f0ca19cd0f 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -42,7 +42,44 @@ So, **to access your database service you have to connect to host: `mysql` inste ### How to use other images as services? You are not limited to have only database services. -You can hand modify `config.toml` to add any image as service found at [Docker Hub](https://registry.hub.docker.com/). +You can add the services to `.gitlab-ci.yml` or hand modify the `config.toml`. +You can use any image as service found at [Docker Hub](https://registry.hub.docker.com/). + +### Define image and services from `.gitlab-ci.yml` +You can simply define image or list services that you want to use for the build time. +``` +image: ruby:2.2 +services: + - postgres:9.3 +before_install: + - bundle install + +test: + script: + - bundle exec rake spec +``` + +It's possible to define image and service per-job: +``` +before_install: + - bundle install + +test:2.1: + image: ruby:2.1 + services: + - postgres:9.3 + script: + - bundle exec rake spec + +test:2.2: + image: ruby:2.2 + services: + - postgres:9.4 + script: + - bundle exec rake spec +``` + +### Define image and services in `config.toml` Look for `[runners.docker]` section: ``` [runners.docker] @@ -50,13 +87,16 @@ Look for `[runners.docker]` section: services = ["mysql:latest", "postgres:latest"] ``` +The image and services defined these way will be added to all builds run by that runner. + +### Accessing the services For example you need `wordpress` instance to test some API integration with `Wordpress`. -You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/). -This is image that have fully preconfigured `wordpress` and have `MySQL` server built-in: +You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/). + ``` -[runners.docker] - image = "ruby:2.1" - services = ["mysql:latest", "postgres:latest", "tutum/wordpress:latest"] +# .gitlab-ci.yml +services: +- tutum/wordpress:latest ``` Next time when you run your application the `tutum/wordpress` will be started @@ -64,7 +104,7 @@ and you will have access to it from your build container under hostname: `tutum_ Alias hostname for the service is made from the image name: 1. Everything after `:` is stripped, -2. '/' is replaced with `__`. +2. '/' is replaced to `__`. ### Configuring services Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment. @@ -99,67 +139,6 @@ or README page for any other Docker image. **Note: All variables will passed to all service containers. It's not designed to distinguish which variable should go where.** -### Overwrite image and services -It's possible to overwrite `docker-image` and specify services from `.gitlab-ci.yml`. -If you add to your YAML the `image` and the `services` these parameters -be used instead of the ones that were specified during runner's registration. -``` -image: ruby:2.2 -services: - - postgres:9.3 -before_install: - - bundle install - -test: - script: - - bundle exec rake spec -``` - -It's possible to define image and service per-job: -``` -before_install: - - bundle install - -test:2.1: - image: ruby:2.1 - services: - - postgres:9.3 - script: - - bundle exec rake spec - -test:2.2: - image: ruby:2.2 - services: - - postgres:9.4 - script: - - bundle exec rake spec -``` - -#### How to enable overwriting? -To enable overwriting you have to **enable it first** (it's disabled by default for security reasons). -You can do that by hand modifying runner configuration: `config.toml`. -Please go to section where is `[runners.docker]` definition for your runner. -Add `allowed_images` and `allowed_services` to specify what images are allowed to be picked from `.gitlab-ci.yml`: -``` -[runners.docker] - image = "ruby:2.1" - allowed_images = ["ruby:*", "python:*"] - allowed_services = ["mysql:*", "redis:*"] -``` -This enables you to use in your `.gitlab-ci.yml` any image that matches above wildcards. -You will be able to pick only `ruby` and `python` images. -The same rule can be applied to limit services. - -If you are courageous enough, you can make it fully open and accept everything: -``` -[runners.docker] - image = "ruby:2.1" - allowed_images = ["*", "*/*"] - allowed_services = ["*", "*/*"] -``` - -**It the feature is not enabled, or image isn't allowed the error message will be put into the build log.** - ### How Docker integration works 1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`. 1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example). diff --git a/doc/ci/languages/README.md b/doc/ci/languages/README.md new file mode 100644 index 00000000000..375adf58d18 --- /dev/null +++ b/doc/ci/languages/README.md @@ -0,0 +1,3 @@ +### Languages + ++ [Testing PHP](php.md) diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md new file mode 100644 index 00000000000..e0589182003 --- /dev/null +++ b/doc/ci/languages/php.md @@ -0,0 +1,178 @@ +## Testing PHP projects + +This guide covers basic of building PHP projects. + +Is it possible to test PHP apps on any system. +However, it will require manual configuration. +The simplest is to use Docker executor as described below. + +### PHP projects on Docker executor +It's possible to official [PHP](https://hub.docker.com/_/php/) repositories on Docker Hub. +They allow to test PHP projects against different versions of the runtime. +However, they require additional configuration. + +To build PHP project you need to create valid `.gitlab-ci.yml` describing the build environment: +1. First you need to specify PHP image as described here: http://doc.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-image. To your `.gitlab-ci.yml` add: + + image: php:5.6 + +2. The official images are great, but they are lacking a few useful tools for testing. We need to install them first in build environment. Create `ci/docker_install.sh` file with following content: + + #!/bin/bash + + # We need to install dependencies only for Docker + [[ ! -e /.dockerinit ]] && exit 0 + + set -xe + + # Install git (the php image doesn't have it) which is required by composer + apt-get update -yqq + apt-get install git -yqq + + # Install phpunit, the tool that we will use for testing + curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar + chmod +x /usr/local/bin/phpunit + + # Install mysql driver + # Here you can install any other extension that you need + docker-php-ext-install pdo_mysql + +3. From your `.gitlab-ci.yml` run the created script: + + before_script: + - bash ci/docker_install.sh > /dev/null + +4. Now you can run your tests. Usually it will be `phpunit` with arguments: + + test:app: + script: + - phpunit --configuration phpunit_myapp.xml --coverage-text + +5. Commit your files, and push them to GitLab to see if it works. With GitLab Runner 1.0 you can also test the changes locally. From your terminal execute: + + # Check using docker executor + gitlab-runner exec docker test:app + + # Check using shell executor + gitlab-runner exec shell test:app + +The final `.gitlab-ci.yml` should look similar to this: + + # Select image from https://hub.docker.com/_/php/ + image: php:5.6 + + before_script: + # Install dependencies + - ci/docker_install.sh > /dev/null + + test:app: + script: + - phpunit --configuration phpunit_myapp.xml --coverage-text + +#### Test against different PHP versions in Docker builds + +You can also test against multiple version of PHP runtime: + + before_script: + # Install dependencies + - ci/docker_install.sh > /dev/null + + # We test PHP5.6 + test:5.6: + image: php:5.6 + script: + - phpunit --configuration phpunit_myapp.xml --coverage-text + + # We test PHP7.0 + test:7.0: + image: php:7.0 + script: + - phpunit --configuration phpunit_myapp.xml --coverage-text + +#### Custom PHP configuration in Docker builds + +You can customise your PHP environment by putting your .ini file into `/usr/local/etc/php/conf.d/`: + + before_script: + - cp my_php.ini /usr/local/etc/php/conf.d/test.ini + +### Test PHP projects using Shell + +Shell executor runs your builds in terminal session of your server. Thus in order to test your projects you need to have all dependencies installed as root. + +1. Install PHP dependencies: + + sudo apt-get update -qy + sudo apt-get install phpunit php5-mysql -y + + This will install the PHP version available for your distribution. + +2. Now you can run your tests. Usually it will be `phpunit` with arguments: + + test:app: + script: + - phpunit --configuration phpunit_myapp.xml --coverage-text + +#### Test against different PHP versions in Shell builds + +The [phpenv](https://github.com/phpenv/phpenv) allows you to easily manage different PHP with they own configs. +This is specially usefull when testing PHP project with Shell executor. + +Login as `gitlab-runner` user and follow [the installation guide](https://github.com/phpenv/phpenv#installation). + +Using phpenv also allows to easily configure PHP environment with: `phpenv config-add my_config.ini`. + +#### Install custom extensions + +Since we have pretty bare installation of our PHP environment you may need some extensions that are not present on your installation. + +To install additional extensions simply execute.: + + pecl install + + It's not advised to add this to the `.gitlab-ci.yml`. + You should execute this command once, only to setup the build environment. + +### Extend your tests + +#### Using atoum + +Instead of PHPUnit, you can use any other tool to run unit tests. For example [atoum](https://github.com/atoum/atoum): + + before_script: + - wget http://downloads.atoum.org/nightly/mageekguy.atoum.phar + + test:atoum: + script: + - php mageekguy.atoum.phar + +#### Using Composer + +Majority of the PHP projects use Composer for managing the packages. +It's very simple to execute the Composer before running your tests. +To your `.gitlab-ci.yml` add: + + # The composer stores all downloaded packages in vendor/ + # Remove it if you committed the vendor/ directory + cache: + paths: + - vendor/ + + before_script: + # Install composer dependencies + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + +### Access private packages / dependencies + +You need to configure [the SSH keys](../ssh_keys/README.md) in order to checkout the repositories. + +### Use databases or other services + +Please checkout the docs about configuring [the CI services](../services/README.md). + +### Example project + +You maybe interested in our [Example Project](https://gitlab.com/gitlab-examples/php) that runs on [GitLab.com](https://gitlab.com) using our publically available shared runners. + +Want to hack it? Simply fork it, commit and push changes. Within a few moments the changes will be picked and rebuilt by public runners. diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md new file mode 100644 index 00000000000..0550e9435a3 --- /dev/null +++ b/doc/ci/services/README.md @@ -0,0 +1,6 @@ +## GitLab CI Services + ++ [Using MySQL](mysql.md) ++ [Using PostgreSQL](postgres.md) ++ [Using Redis](redis.md) ++ [Using Other Services](../docker/using_docker_images.html#how-to-use-other-images-as-services) diff --git a/doc/ci/services/docker-services.md b/doc/ci/services/docker-services.md new file mode 100644 index 00000000000..df36ebaf7d4 --- /dev/null +++ b/doc/ci/services/docker-services.md @@ -0,0 +1,5 @@ +## GitLab CI Services + ++ [Using MySQL](mysql.md) ++ [Using PostgreSQL](postgres.md) ++ [Using Redis](redis.md) diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md new file mode 100644 index 00000000000..3155af6b3e1 --- /dev/null +++ b/doc/ci/services/mysql.md @@ -0,0 +1,72 @@ +## Using MySQL + +It's possible to use MySQL database test your apps during builds. + +### Use MySQL with Docker executor + +If you are using our Docker integration you basically have everything already. + +1. Add this to your `.gitlab-ci.yml`: + + services: + - mysql + + variables: + # Configure mysql service (https://hub.docker.com/_/mysql/) + MYSQL_DATABASE: hello_world_test + MYSQL_ROOT_PASSWORD: mysql + +2. Configure your application to use the database: + + Host: mysql + User: root + Password: mysql + Database: hello_world_test + +3. You can also use any other available on [DockerHub](https://hub.docker.com/_/mysql/). For example: `mysql:5.5`. + +Example: https://gitlab.com/gitlab-examples/mysql/blob/master/.gitlab-ci.yml + +### Use MySQL with Shell executor + +It's possible to use MySQL on manually configured servers that are using GitLab Runner with Shell executor. + +1. First install the MySQL server: + + sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev + + # Pick a MySQL root password (can be anything), type it and press enter + # Retype the MySQL root password and press enter + +2. Create an user: + + mysql -u root -p + + # Create a user which will be used by your apps + # do not type the 'mysql>', this is part of the prompt + # change $password in the command below to a real password you pick + mysql> CREATE USER 'runner'@'localhost' IDENTIFIED BY '$password'; + + # Ensure you can use the InnoDB engine which is necessary to support long indexes + # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" + mysql> SET storage_engine=INNODB; + + # Create the database + mysql> CREATE DATABASE IF NOT EXISTS `hello_world_test` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; + + # Grant necessary permissions on the database + mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `hello_world_test`.* TO 'runner'@'localhost'; + + # Quit the database session + mysql> \q + +3. Try to connect to database: + + sudo -u gitlab-runner -H mysql -u runner -p -D hello_world_test + +4. Configure your application to use the database: + + Host: localhost + User: runner + Password: $password + Database: hello_world_test diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md new file mode 100644 index 00000000000..e57f8c5944a --- /dev/null +++ b/doc/ci/services/postgres.md @@ -0,0 +1,70 @@ +## Using PostgreSQL + +It's possible to use PostgreSQL database test your apps during builds. + +### Use PostgreSQL with Docker executor + +If you are using our Docker integration you basically have everything already. + +1. Add this to your `.gitlab-ci.yml`: + + services: + - postgres + + variables: + # Configure postgres service (https://hub.docker.com/_/postgres/) + POSTGRES_DB: hello_world_test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: "" + +2. Configure your application to use the database: + + Host: postgres + User: postgres + Password: postgres + Database: hello_world_test + +3. You can also use any other available on [DockerHub](https://hub.docker.com/_/postgres/). For example: `postgres:9.3`. + +Example: https://gitlab.com/gitlab-examples/postgres/blob/master/.gitlab-ci.yml + +### Use PostgreSQL with Shell executor + +It's possible to use PostgreSQL on manually configured servers that are using GitLab Runner with Shell executor. + +1. First install the PostgreSQL server: + + sudo apt-get install -y postgresql postgresql-client libpq-dev + +2. Create an user: + + # Install the database packages + sudo apt-get install -y postgresql postgresql-client libpq-dev + + # Login to PostgreSQL + sudo -u postgres psql -d template1 + + # Create a user for runner + # Do not type the 'template1=#', this is part of the prompt + template1=# CREATE USER runner CREATEDB; + + # Create the database & grant all privileges on database + template1=# CREATE DATABASE hello_world_test OWNER runner; + + # Quit the database session + template1=# \q + +3. Try to connect to database: + + # Try connecting to the new database with the new user + sudo -u gitlab-runner -H psql -d hello_world_test + + # Quit the database session + hello_world_test> \q + +4. Configure your application to use the database: + + Host: localhost + User: runner + Password: + Database: hello_world_test diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md new file mode 100644 index 00000000000..523634a457e --- /dev/null +++ b/doc/ci/services/redis.md @@ -0,0 +1,40 @@ +## Using Redis + +It's possible to use Redis database test your apps during builds. + +### Use Redis with Docker executor + +If you are using our Docker integration you basically have everything already. + +1. Add this to your `.gitlab-ci.yml`: + + services: + - redis + +2. Configure your application to use the database: + + Host: redis + +3. You can also use any other available on [DockerHub](https://hub.docker.com/_/redis/). For example: `redis:2.6`. + +Example: https://gitlab.com/gitlab-examples/redis/blob/master/.gitlab-ci.yml + +### Use Redis with Shell executor + +It's possible to use Redis on manually configured servers that are using GitLab Runner with Shell executor. + +1. First install the Redis server: + + sudo apt-get install redis-server + +2. Try to connect to the server: + + # Try connecting the the Redis server + sudo -u gitlab-runner -H redis-cli + + # Quit the session + 127.0.0.1:6379> quit + +4. Configure your application to use the database: + + Host: localhost diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md new file mode 100644 index 00000000000..515194e5f5e --- /dev/null +++ b/doc/ci/ssh_keys/README.md @@ -0,0 +1,114 @@ +# Using SSH keys + +GitLab currently doesn't have built-in support for SSH keys in build environment. + +The SSH keys can be useful when: +1. You want to checkout internal submodules, +2. You want to download private packages using your package manager (ie. bundler), +3. You want to deploy your app (ex. to Heroku or own server), +4. You want to execute ssh commands from build environment on remote server, +5. You want to rsync files from your build to remote server. + +If anyone of the above holds true, then you most likely need SSH key. + +There are two possibilities to add SSH keys to build environment. + +## Inject keys in your build environment +The most widely supported is to inject SSH key into your build environment by extending your .gitlab-ci.yml. +This is the universal solution which works with any type of executor (docker, shell, etc.). + +### How it works? +1. We create a new SSH private key with [ssh-keygen](http://linux.die.net/man/1/ssh-keygen). +2. We add the private key as the Secure Variable to project. +3. We run the [ssh-agent](http://linux.die.net/man/1/ssh-agent) during build to load the private key. + +The example [.gitlab-ci.yml](https://gitlab.com/gitlab-examples/ssh-private-key/blob/master/.gitlab-ci.yml) looks like this. + +### Make it work? +1. First, go to terminal and generate a new SSH key: +```bash +$ ssh-keygen -t rsa -f my_key + +Generating public/private rsa key pair. +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in my_key. +Your public key has been saved in my_key.pub. +The key fingerprint is: +SHA256:tBJEfyJUGTMNmPCiPg4UHywHs67MxlM2iEBAlI/W+TY fingeprint +The key's randomart image is: ++---[RSA 2048]----+ +|=*. .o++*= | +|..= +o..o. | +|.+++o + + . | +|+o*=.. + + | +|o+.=. . S | +|*.o .E . | +|o*o . . | +|.o.. | +| . | ++----[SHA256]-----+ +``` + +2. Create a new **Secure Variable** in your project settings on GitLab and name it: `SSH_PRIVATE_KEY`. + +3. Copy the content of `my_key` and paste it as a **Value** of **SSH_PRIVATE_KEY**. + +4. Next you need to modify your `.gitlab-ci.yml` and at the top of the file add: +``` +before_script: +# install ssh-agent (it is required for Docker, change apt-get to yum if you use CentOS-based image) +- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' + +# run ssh-agent (in build environment) +- eval $(ssh-agent -s) + +# add ssh key stored in SSH_PRIVATE_KEY variable to the agent store +- ssh-add <(echo "$SSH_PRIVATE_KEY") + +# for Docker builds disable host key checking, by adding that you are suspectible to man-in-the-middle attack +- mkdir -p ~/.ssh +- '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config` +``` + +5. Add the public key from `my_key.pub` to services that you want to have an access from build. + +6. If your builds are run using `shell` executor, you may need to login to server and execute the `ssh ` to store the fingerprint of remote server. + +## SSH keys when using Shell executor +If use `shell`, not `docker` it can be easier to have the SSH key. + +We can generate the SSH key for the machine that holds `gitlab-runner` and use that key for all projects that are run on this machine. + +1. First, login to server that runs your builds. + +2. From terminal login as `gitlab-runner` user and generate the SSH private key: +```bash +$ ssh-keygen -t rsa +Generating public/private rsa key pair. +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in ~/.ssh/id_rsa. +Your public key has been saved in ~/.ssh/id_rsa.pub. +The key fingerprint is: +SHA256:tBJEfyJUGTMNmPCiPg4UHywHs67MxlM2iEBAlI/W+TY fingeprint +The key's randomart image is: ++---[RSA 2048]----+ +|=*. .o++*= | +|..= +o..o. | +|.+++o + + . | +|+o*=.. + + | +|o+.=. . S | +|*.o .E . | +|o*o . . | +|.o.. | +| . | ++----[SHA256]-----+ +``` + +3. Add the public key from `~/.ssh/id_rsa.pub` to services that you want to have an access from build. + +4. Try to login for the first time and accept fingerprint: +```bash +ssh Date: Wed, 2 Dec 2015 17:47:18 +0200 Subject: Fix heading --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index a7025f7af10..f85a464a4f0 100644 --- a/doc/README.md +++ b/doc/README.md @@ -32,7 +32,7 @@ + [Testing PHP](ci/languages/php.md) -## CI Services +### CI Services + [Using MySQL](ci/services/mysql.md) + [Using PostgreSQL](ci/services/postgres.md) -- cgit v1.2.1 From dcdc49fe429c0e8a762f1a411e1d5c0a7294c91f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 2 Dec 2015 19:24:15 +0200 Subject: Use .md instead of .html --- doc/README.md | 2 +- doc/ci/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/README.md b/doc/README.md index f85a464a4f0..a3098094210 100644 --- a/doc/README.md +++ b/doc/README.md @@ -37,7 +37,7 @@ + [Using MySQL](ci/services/mysql.md) + [Using PostgreSQL](ci/services/postgres.md) + [Using Redis](ci/services/redis.md) -+ [Using Other Services](ci/docker/using_docker_images.html#how-to-use-other-images-as-services) ++ [Using Other Services](ci/docker/using_docker_images.md#how-to-use-other-images-as-services) ### CI Examples diff --git a/doc/ci/README.md b/doc/ci/README.md index ae921b6f988..5d9d7a81db3 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -20,7 +20,7 @@ + [Using MySQL](services/mysql.md) + [Using PostgreSQL](services/postgres.md) + [Using Redis](services/redis.md) -+ [Using Other Services](docker/using_docker_images.html#how-to-use-other-images-as-services) ++ [Using Other Services](docker/using_docker_images.md#how-to-use-other-images-as-services) ### Examples -- cgit v1.2.1 From 05267b64d224c19f1b6843a715f666aea2bd106a Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 00:36:45 +0200 Subject: Clean up using_docker_images.md --- doc/ci/docker/using_docker_images.md | 268 ++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 83 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 2f0ca19cd0f..6551d47b697 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -1,16 +1,25 @@ # Using Docker Images -GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects. -Docker is an open-source project that allows to use predefined images to run applications -in independent "containers" that are run within a single Linux instance. -[Docker Hub](https://registry.hub.docker.com/) have rich database of built images that can be used to build applications. +GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects. -Docker when used with GitLab CI runs each build in separate and isolated container using predefined image and always from scratch. -It makes it easier to have simple and reproducible build environment that can also be run on your workstation. -This allows you to test all commands from your shell, rather than having to test them on a CI server. +Docker is an open-source project that allows to use predefined images to run +applications in independent "containers" that are run within a single Linux +instance. [Docker Hub][hub] has a rich database of prebuilt images that can be +used to test and build your applications. -### Register Docker runner -To use GitLab Runner with Docker you need to register new runner to use `docker` executor: +Docker, when used with GitLab CI, runs each build in a separate and isolated +container using the predefined image that is set up in `.gitlab-ci.yml`. The +container is always euphemeral which means you get only one container per build. + +This makes it easier to have a simple and reproducible build environment that +can also run on your workstation. The added benefit is that you can test all +the commands that we will explore later from your shell, rather than having to +test them on a CI server. + +### Register docker runner + +To use GitLab Runner with docker you need to register a new runner to use the +`docker` executor: ```bash gitlab-ci-multi-runner register \ @@ -23,45 +32,70 @@ gitlab-ci-multi-runner register \ --docker-mysql latest ``` -**The registered runner will use `ruby:2.1` image and will run two services (`postgres:latest` and `mysql:latest`) that will be accessible for time of the build.** +The registered runner will use the `ruby:2.1` docker image and will run two +services, `postgres:latest` and `mysql:latest`, both of which will be +accessible during the build process. + +### What is image + +The `image` keyword is the name of the docker image that is present in the +local Docker Engine (list all images with `docker images`) or any image that +can be found at [Docker Hub][hub]. For more information about images and Docker +Hub please read the [Docker Fundamentals][] documentation. + +In short, with `image` we refer to the docker image, which will be used to +create a container on which your build will run. + +### What is service -### What is image? -The image is the name of any repository that is present in local Docker Engine or any repository that can be found at [Docker Hub](https://registry.hub.docker.com/). -For more information about the image and Docker Hub please read the [Docker Fundamentals](https://docs.docker.com/introduction/understanding-docker/). +The `service` keyword defines just another docker image that is run during +your build and is linked to the docker image that the `image` keyword defines. +This allows you to access the service image during build time. -### What is service? -Service is just another image that is run for time of your build and is linked to your build. This allows you to access the service image during build time. -The service image can run any application, but most common use case is to run some database container, ie.: `mysql`. -It's easier and faster to use existing image, run it as additional container than install `mysql` every time project is built. +The service image can run any application, but the most common use case is to +run a database container, eg. `mysql`. It's easier and faster to use an +existing image and run it as an additional container than install `mysql` every +time the project is built. -#### How is service linked to the build? -There's good document that describes how Docker linking works: [Linking containers together](https://docs.docker.com/userguide/dockerlinks/). -To summarize: if you add `mysql` as service to your application, the image will be used to create container that is linked to build container. -The service container for MySQL will be accessible under hostname `mysql`. -So, **to access your database service you have to connect to host: `mysql` instead of socket or `localhost`**. +#### How is service linked to the build -### How to use other images as services? -You are not limited to have only database services. -You can add the services to `.gitlab-ci.yml` or hand modify the `config.toml`. -You can use any image as service found at [Docker Hub](https://registry.hub.docker.com/). +To better undestand how the container linking works, read +[Linking containers together](https://docs.docker.com/userguide/dockerlinks/). + +To summarize, if you add `mysql` as service to your application, the image will +then be used to create a container that is linked to the build container. + +The service container for MySQL will be accessible under the hostname `mysql`. +So, in order to access your database service you have to connect to the host +named `mysql` instead of a socket or `localhost`. + +### How to use other images as services + +You are not limited to have only database services. You can add as many +services you need to `.gitlab-ci.yml` or manually modify `config.toml`. +Any image found at [Docker Hub][hub] can be used as a service. ### Define image and services from `.gitlab-ci.yml` -You can simply define image or list services that you want to use for the build time. -``` + +You can simply define an image that will be used for all jobs and a list of +services that you want to use during build time. + +```yaml image: ruby:2.2 services: - postgres:9.3 -before_install: +before_script: - bundle install - + test: script: - bundle exec rake spec ``` -It's possible to define image and service per-job: -``` -before_install: +It is also possible to define different images and services per job: + +```yaml +before_script: - bundle install test:2.1: @@ -80,68 +114,106 @@ test:2.2: ``` ### Define image and services in `config.toml` -Look for `[runners.docker]` section: + +Look for the `[runners.docker]` section: + ``` +... + [runners.docker] image = "ruby:2.1" services = ["mysql:latest", "postgres:latest"] + +... ``` -The image and services defined these way will be added to all builds run by that runner. +The image and services defined this way will be added to all builds run by +that runner. ### Accessing the services -For example you need `wordpress` instance to test some API integration with `Wordpress`. -You can for example use this image: [tutum/wordpress](https://registry.hub.docker.com/u/tutum/wordpress/). -``` -# .gitlab-ci.yml +Let's say that you need a Wordpress instance to test some API integration with +your application. + +You can then use for example the [tutum/wordpress][] image in your +`.gitlab-ci.yml`: + +```yaml +... + services: - tutum/wordpress:latest + +... ``` -Next time when you run your application the `tutum/wordpress` will be started -and you will have access to it from your build container under hostname: `tutum__wordpress`. +When the build is run, `tutum/wordpress` will be started and you will have +access to it from your build container under the hostname `tutum_wordpress`. + +The alias hostname for the service is made from the image name following these +rules: -Alias hostname for the service is made from the image name: -1. Everything after `:` is stripped, -2. '/' is replaced to `__`. +1. Everything after `:` is stripped +2. Backslash (`/`) is replaced with double underscores (`__`) ### Configuring services -Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment. -GitLab Runner 0.5.0 and up passes all YAML-defined variables to created service containers. +Many services accept environment variables which allow you to easily change +database names or set account names depending on the environment. -1. To configure database name for [postgres](https://registry.hub.docker.com/u/library/postgres/) service, -you need to set POSTGRES_DB. +GitLab Runner 0.5.0 and up passes all YAML-defined variables to the created +service containers. - ```yaml - services: - - postgres - - variables: - POSTGRES_DB: gitlab - ``` +For all possible configuration variables check the documentation of each image +provided in their corresponding Docker hub page. -1. To use [mysql](https://registry.hub.docker.com/u/library/mysql/) service with empty password for time of build, -you need to set MYSQL_ALLOW_EMPTY_PASSWORD. +*Note: All variables will be passed to all service containers. It's not designed + to distinguish which variable should go where.* - ```yaml - services: - - mysql - - variables: - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - ``` +#### PostgreSQL service example -For other possible configuration variables check the -https://registry.hub.docker.com/u/library/mysql/ or https://registry.hub.docker.com/u/library/postgres/ -or README page for any other Docker image. +To configure the database name for [postgres][postgres-hub] service, you need +to set the `POSTGRES_DB` variable: -**Note: All variables will passed to all service containers. It's not designed to distinguish which variable should go where.** +```yaml +... + +services: +- postgres + +variables: + POSTGRES_DB: gitlab + +... +``` + +For a real example visit . + +#### MySQL service example + +To use the [mysql][mysql-hub] service with an empty password during the time of +build, you need to set the `MYSQL_ALLOW_EMPTY_PASSWORD` variable: + +```yaml +... + +services: +- mysql + +variables: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + +... +``` ### How Docker integration works + +Below is a high level overview of the steps performed by docker during build +time. + 1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`. -1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example). +1. Create cache container to store all volumes as defined in `config.toml` and + `Dockerfile` of build image (`ruby:2.1` as in above example). 1. Create build container and link any service container to build container. 1. Start build container and send build script to the container. 1. Run build script. @@ -151,32 +223,62 @@ or README page for any other Docker image. 1. Remove build container and all created service containers. ### How to debug a build locally -1. Create a file with build script: + +*Note: The following commands are run without root privileges. You should be +able to run docker with your regular user account.* + +First start with creating a file named `build script`: + ```bash -$ cat < build_script +cat < build_script git clone https://gitlab.com/gitlab-org/gitlab-ci-multi-runner.git /builds/gitlab-org/gitlab-ci-multi-runner cd /builds/gitlab-org/gitlab-ci-multi-runner -make <- or any other build step +make EOF ``` -1. Create service containers: +Here we use as an example the GitLab Runner repository which contains a +Makefile, so running `make` will execute the commands defined in the Makefile. +Your mileage may vary, so instead of `make` you could run the command which +is specific to your project. + +Then create some service containers: + ``` -$ docker run -d -n service-mysql mysql:latest -$ docker run -d -n service-postgres postgres:latest +docker run -d -n service-mysql mysql:latest +docker run -d -n service-postgres postgres:latest ``` -This will create two service containers (MySQL and PostgreSQL). -1. Create a build container and execute script in its context: +This will create two service containers, named `service-mysql` and +`service-postgres` which use the latest MySQL and PostgreSQL images +respectively. They will both run in the background (`-d`). + +Finally, create a build container by executing the `build_script` file we +created earlier: + ``` -$ docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script +docker run --name build -i --link=service-mysql:mysql --link=service-postgres:postgres ruby:2.1 /bin/bash < build_script ``` -This will create build container that has two service containers linked. -The build_script is piped using STDIN to bash interpreter which executes the build script in container. -1. At the end remove all containers: +The above command will create a container named `build` that is spawned from +the `ruby:2.1` image and has two services linked to it. The `build_script` is +piped using STDIN to the bash interpreter which in turn executes the +`build_script` in the `build` container. + +When you finish testing and no longer need the containers, you can remove them +with: + ``` docker rm -f -v build service-mysql service-postgres ``` -This will forcefully (the `-f` switch) remove build container and service containers -and all volumes (the `-v` switch) that were created with the container creation. + +This will forcefully (`-f`) remove the `build` container, the two service +containers as well as all volumes (`-v`) that were created with the container +creation. + +[Docker Fundamentals]: https://docs.docker.com/engine/introduction/understanding-docker/ +[hub]: https://hub.docker.com/ +[linking-containers]: https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/ +[tutum/wordpress]: https://registry.hub.docker.com/u/tutum/wordpress/ +[postgres-hub]: https://registry.hub.docker.com/u/library/postgres/ +[mysql-hub]: https://registry.hub.docker.com/u/library/mysql/ -- cgit v1.2.1 From 223a02757909b406c1d5ebe42ca4bc3340318a2e Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 15:43:06 +0200 Subject: Bring back removed heading and point to other section --- doc/ci/docker/using_docker_images.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 6551d47b697..63cfa436333 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -69,6 +69,10 @@ The service container for MySQL will be accessible under the hostname `mysql`. So, in order to access your database service you have to connect to the host named `mysql` instead of a socket or `localhost`. +### Overwrite image and services + +See the section below. + ### How to use other images as services You are not limited to have only database services. You can add as many -- cgit v1.2.1 From 71519d650d0317785f86ed935508d1dd5966cd6e Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 15:43:29 +0200 Subject: Fix wrong example --- doc/ci/docker/using_docker_images.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 63cfa436333..488479418b1 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -152,7 +152,7 @@ services: ``` When the build is run, `tutum/wordpress` will be started and you will have -access to it from your build container under the hostname `tutum_wordpress`. +access to it from your build container under the hostname `tutum__wordpress`. The alias hostname for the service is made from the image name following these rules: -- cgit v1.2.1 From bb75dfe38b481607952234212a15d29cd17b7cef Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 15:46:27 +0200 Subject: Add intro about languages --- doc/ci/languages/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/ci/languages/README.md b/doc/ci/languages/README.md index 375adf58d18..54b2343e08b 100644 --- a/doc/ci/languages/README.md +++ b/doc/ci/languages/README.md @@ -1,3 +1,7 @@ ### Languages +This is a list of languages you can test with GitLab CI. Each section has +comprehensive documentation and comes with a test repository hosted on +GitLab.com + + [Testing PHP](php.md) -- cgit v1.2.1 From a08cc70232b1c5071f199106a582c77ba9541a83 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 18:13:19 +0200 Subject: Clean up PHP CI example [ci skip] --- doc/ci/languages/php.md | 325 +++++++++++++++++++++++++++++++----------------- 1 file changed, 211 insertions(+), 114 deletions(-) diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md index e0589182003..c71ae40786e 100644 --- a/doc/ci/languages/php.md +++ b/doc/ci/languages/php.md @@ -1,178 +1,275 @@ -## Testing PHP projects +# Testing PHP projects -This guide covers basic of building PHP projects. +This guide covers basic building instructions for PHP projects. -Is it possible to test PHP apps on any system. -However, it will require manual configuration. -The simplest is to use Docker executor as described below. +There are covered two cases: testing using the Docker executor and testing +using the Shell executor. -### PHP projects on Docker executor -It's possible to official [PHP](https://hub.docker.com/_/php/) repositories on Docker Hub. -They allow to test PHP projects against different versions of the runtime. -However, they require additional configuration. +## Test PHP projects using the Docker executor -To build PHP project you need to create valid `.gitlab-ci.yml` describing the build environment: -1. First you need to specify PHP image as described here: http://doc.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-image. To your `.gitlab-ci.yml` add: +While it is possible to test PHP apps on any system, this would require manual +configuration from the developer. To overcome this we will be using the +official [PHP docker image][php-hub] that can be found in Docker Hub. - image: php:5.6 +This will allow us to test PHP projects against different versions of PHP. +However, not everything is plug 'n' play, you still need to onfigure some +things manually. -2. The official images are great, but they are lacking a few useful tools for testing. We need to install them first in build environment. Create `ci/docker_install.sh` file with following content: +As with every build, you need to create a valid `.gitlab-ci.yml` describing the +build environment. - #!/bin/bash +Let's first specify the PHP image that will be used for the build process +(you can read more about what an image means in the Runner's lingo reading +about [Using Docker images](../docker/using_docker_images.md#what-is-image)). - # We need to install dependencies only for Docker - [[ ! -e /.dockerinit ]] && exit 0 +Start by adding the image to your `.gitlab-ci.yml`: - set -xe +```yaml +image: php:5.6 +``` - # Install git (the php image doesn't have it) which is required by composer - apt-get update -yqq - apt-get install git -yqq +The official images are great, but they lack a few useful tools for testing. +We need to first prepare the build environment. A way to overcome this is to +create a script which installs all prerequisites prior the actual testing is +done. - # Install phpunit, the tool that we will use for testing - curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar - chmod +x /usr/local/bin/phpunit +Let's create a `ci/docker_install.sh` file in the root directory of our +repository with the following content: - # Install mysql driver - # Here you can install any other extension that you need - docker-php-ext-install pdo_mysql +```bash +#!/bin/bash -3. From your `.gitlab-ci.yml` run the created script: +# We need to install dependencies only for Docker +[[ ! -e /.dockerinit ]] && exit 0 - before_script: - - bash ci/docker_install.sh > /dev/null +set -xe -4. Now you can run your tests. Usually it will be `phpunit` with arguments: +# Install git (the php image doesn't have it) which is required by composer +apt-get update -yqq +apt-get install git -yqq - test:app: - script: - - phpunit --configuration phpunit_myapp.xml --coverage-text +# Install phpunit, the tool that we will use for testing +curl -o /usr/local/bin/phpunit https://phar.phpunit.de/phpunit.phar +chmod +x /usr/local/bin/phpunit -5. Commit your files, and push them to GitLab to see if it works. With GitLab Runner 1.0 you can also test the changes locally. From your terminal execute: +# Install mysql driver +# Here you can install any other extension that you need +docker-php-ext-install pdo_mysql +``` - # Check using docker executor - gitlab-runner exec docker test:app +You might wonder what `docker-php-ext-install` is. In short, it is a script +provided by the official php docker image that you can use to easilly install +extensions. For more information read the the documentation at +. - # Check using shell executor - gitlab-runner exec shell test:app +Now that we created the script that contains all prerequisites for our build +environment, let's add it in `.gitlab-ci.yml`: + +```yaml +... + +before_script: +- bash ci/docker_install.sh > /dev/null + +... +``` + +Last step, run the actual tests using `phpunit`: + +```yaml +... + +test:app: + script: + - phpunit --configuration phpunit_myapp.xml + +... +``` + +Finally, commit your files and push them to GitLab to see your build succeeding +(or failing). The final `.gitlab-ci.yml` should look similar to this: - # Select image from https://hub.docker.com/_/php/ - image: php:5.6 +```yaml +# Select image from https://hub.docker.com/_/php/ +image: php:5.6 + +before_script: +# Install dependencies +- ci/docker_install.sh > /dev/null + +test:app: + script: + - phpunit --configuration phpunit_myapp.xml +``` + +### Test against different PHP versions in Docker builds + +Testing against multiple versions of PHP is super easy. Just add another job +with a different docker image version and the runner will do the rest: + +```yaml +before_script: +# Install dependencies +- ci/docker_install.sh > /dev/null + +# We test PHP5.6 +test:5.6: + image: php:5.6 + script: + - phpunit --configuration phpunit_myapp.xml + +# We test PHP7.0 (good luck with that) +test:7.0: + image: php:7.0 + script: + - phpunit --configuration phpunit_myapp.xml +``` + +### Custom PHP configuration in Docker builds + +There are times where you will need to customise your PHP environment by +putting your `.ini` file into `/usr/local/etc/php/conf.d/`. For that purpose +add a `before_script` action: + +```yaml +before_script: +- cp my_php.ini /usr/local/etc/php/conf.d/test.ini +``` - before_script: - # Install dependencies - - ci/docker_install.sh > /dev/null +Of course, `my_php.ini` must be present in the root directory of your repository. - test:app: - script: - - phpunit --configuration phpunit_myapp.xml --coverage-text +## Test PHP projects using the Shell executor -#### Test against different PHP versions in Docker builds +The shell executor runs your builds in a terminal session on your server. +Thus, in order to test your projects you first need to make sure that all +dependencies are installed. -You can also test against multiple version of PHP runtime: +For example, in a VM running Debian 8 we first update the cache, then we +install `phpunit` and `php5-mysql`: - before_script: - # Install dependencies - - ci/docker_install.sh > /dev/null +```bash +sudo apt-get update -y +sudo apt-get install -y phpunit php5-mysql +``` - # We test PHP5.6 - test:5.6: - image: php:5.6 - script: - - phpunit --configuration phpunit_myapp.xml --coverage-text +Next, add the following snippet to your `.gitlab-ci.yml`: - # We test PHP7.0 - test:7.0: - image: php:7.0 - script: - - phpunit --configuration phpunit_myapp.xml --coverage-text +```yaml +test:app: + script: + - phpunit --configuration phpunit_myapp.xml +``` -#### Custom PHP configuration in Docker builds +Finally, push to GitLab and let the tests begin! -You can customise your PHP environment by putting your .ini file into `/usr/local/etc/php/conf.d/`: +### Test against different PHP versions in Shell builds - before_script: - - cp my_php.ini /usr/local/etc/php/conf.d/test.ini +The [phpenv][] project allows you to easily manage different versions of PHP +each with its own config. This is specially usefull when testing PHP projects +with the Shell executor. -### Test PHP projects using Shell +You will have to install it on your build machine under the `gitlab-runner` +user following [the upstream installation guide][phpenv-installation]. -Shell executor runs your builds in terminal session of your server. Thus in order to test your projects you need to have all dependencies installed as root. +Using phpenv also allows to easily configure the PHP environment with: -1. Install PHP dependencies: +``` +phpenv config-add my_config.ini +``` - sudo apt-get update -qy - sudo apt-get install phpunit php5-mysql -y +### Install custom extensions - This will install the PHP version available for your distribution. +Since this is a pretty bare installation of the PHP environment, you may need +some extensions that are not currently present on the build machine. -2. Now you can run your tests. Usually it will be `phpunit` with arguments: +To install additional extensions simply execute: - test:app: - script: - - phpunit --configuration phpunit_myapp.xml --coverage-text +```bash +pecl install +``` -#### Test against different PHP versions in Shell builds +It's not advised to add this to `.gitlab-ci.yml`. You should execute this +command once, only to setup the build environment. -The [phpenv](https://github.com/phpenv/phpenv) allows you to easily manage different PHP with they own configs. -This is specially usefull when testing PHP project with Shell executor. +## Extend your tests -Login as `gitlab-runner` user and follow [the installation guide](https://github.com/phpenv/phpenv#installation). +### Using atoum -Using phpenv also allows to easily configure PHP environment with: `phpenv config-add my_config.ini`. +Instead of PHPUnit, you can use any other tool to run unit tests. For example +you can use [atoum](https://github.com/atoum/atoum): -#### Install custom extensions +```yaml +before_script: +- wget http://downloads.atoum.org/nightly/mageekguy.atoum.phar -Since we have pretty bare installation of our PHP environment you may need some extensions that are not present on your installation. +test:atoum: + script: + - php mageekguy.atoum.phar +``` -To install additional extensions simply execute.: +### Using Composer - pecl install +The majority of the PHP projects use Composer for managing their PHP packages. +In order to execute Composer before running your tests, simply add the +following in your `.gitlab-ci.yml`: - It's not advised to add this to the `.gitlab-ci.yml`. - You should execute this command once, only to setup the build environment. +```yaml +... -### Extend your tests +# Composer stores all downloaded packages in the vendor/ directory. +# Do not use the following if the vendor/ directory is commited to +# your git repository. +cache: + paths: + - vendor/ -#### Using atoum +before_script: +# Install composer dependencies +- curl -sS https://getcomposer.org/installer | php +- php composer.phar install -Instead of PHPUnit, you can use any other tool to run unit tests. For example [atoum](https://github.com/atoum/atoum): +... +``` - before_script: - - wget http://downloads.atoum.org/nightly/mageekguy.atoum.phar +## Access private packages / dependencies - test:atoum: - script: - - php mageekguy.atoum.phar +If your test suite needs to access a private repository, you need to configure +[the SSH keys](../ssh_keys/README.md) in order to be able to clone it. -#### Using Composer +## Use databases or other services -Majority of the PHP projects use Composer for managing the packages. -It's very simple to execute the Composer before running your tests. -To your `.gitlab-ci.yml` add: +Most of the time you will need a running database in order for your tests to +run. If you are using the Docker executor you can leverage Docker's ability to +connect to other containers. In GitLab Runner lingo, this can be achieved by +defining a `service`. - # The composer stores all downloaded packages in vendor/ - # Remove it if you committed the vendor/ directory - cache: - paths: - - vendor/ +This functionality is covered in [the CI services](../services/README.md) +documentation. - before_script: - # Install composer dependencies - - curl -sS https://getcomposer.org/installer | php - - php composer.phar install +## Testing things locally -### Access private packages / dependencies +With GitLab Runner 1.0 you can also test any changes locally. From your +terminal execute: -You need to configure [the SSH keys](../ssh_keys/README.md) in order to checkout the repositories. +```bash +# Check using docker executor +gitlab-runner exec docker test:app -### Use databases or other services +# Check using shell executor +gitlab-runner exec shell test:app +``` -Please checkout the docs about configuring [the CI services](../services/README.md). +## Example project -### Example project +We have set up an [Example PHP Project](https://gitlab.com/gitlab-examples/php) +for your convenience that runs on [GitLab.com](https://gitlab.com) using our +publicly available [shared runners](../runners/README.md). -You maybe interested in our [Example Project](https://gitlab.com/gitlab-examples/php) that runs on [GitLab.com](https://gitlab.com) using our publically available shared runners. +Want to hack it? Simply fork it, commit and push your changes. Within a few +moments the changes will be picked by a public runner and the build will begin. -Want to hack it? Simply fork it, commit and push changes. Within a few moments the changes will be picked and rebuilt by public runners. +[php-hub]: https://hub.docker.com/_/php/ +[phpenv]: https://github.com/phpenv/phpenv +[phpenv-installation]: https://github.com/phpenv/phpenv#installation -- cgit v1.2.1 From 9b8babb601ac6bdb3b7301b017a8ce20c5dc4814 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 3 Dec 2015 21:18:34 +0200 Subject: Use link instead of connect to be more Docker friendly --- doc/ci/languages/php.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md index c71ae40786e..589253cfc4c 100644 --- a/doc/ci/languages/php.md +++ b/doc/ci/languages/php.md @@ -242,7 +242,7 @@ If your test suite needs to access a private repository, you need to configure Most of the time you will need a running database in order for your tests to run. If you are using the Docker executor you can leverage Docker's ability to -connect to other containers. In GitLab Runner lingo, this can be achieved by +link to other containers. In GitLab Runner lingo, this can be achieved by defining a `service`. This functionality is covered in [the CI services](../services/README.md) -- cgit v1.2.1 From d13d43aca9c486365fbe6eab8d30cb1e005c3f61 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 4 Dec 2015 11:46:08 +0200 Subject: Clean up CI ssh keys docs [ci skip] --- doc/ci/ssh_keys/README.md | 167 ++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 86 deletions(-) diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md index 515194e5f5e..210f9c3e849 100644 --- a/doc/ci/ssh_keys/README.md +++ b/doc/ci/ssh_keys/README.md @@ -1,114 +1,109 @@ # Using SSH keys -GitLab currently doesn't have built-in support for SSH keys in build environment. +GitLab currently doesn't have built-in support for managing SSH keys in a build +environment. The SSH keys can be useful when: -1. You want to checkout internal submodules, -2. You want to download private packages using your package manager (ie. bundler), -3. You want to deploy your app (ex. to Heroku or own server), -4. You want to execute ssh commands from build environment on remote server, -5. You want to rsync files from your build to remote server. -If anyone of the above holds true, then you most likely need SSH key. +1. You want to checkout internal submodules +2. You want to download private packages using your package manager (eg. bundler) +3. You want to deploy your application to eg. Heroku or your own server +4. You want to execute SSH commands from the build server to the remote server +5. You want to rsync files from your build server to the remote server -There are two possibilities to add SSH keys to build environment. +If anything of the above rings a bell, then you most likely need an SSH key. -## Inject keys in your build environment -The most widely supported is to inject SSH key into your build environment by extending your .gitlab-ci.yml. -This is the universal solution which works with any type of executor (docker, shell, etc.). +## Inject keys in your build server -### How it works? -1. We create a new SSH private key with [ssh-keygen](http://linux.die.net/man/1/ssh-keygen). -2. We add the private key as the Secure Variable to project. -3. We run the [ssh-agent](http://linux.die.net/man/1/ssh-agent) during build to load the private key. +The most widely supported method is to inject an SSH key into your build +environment by extending your `.gitlab-ci.yml`. -The example [.gitlab-ci.yml](https://gitlab.com/gitlab-examples/ssh-private-key/blob/master/.gitlab-ci.yml) looks like this. +This is the universal solution which works with any type of executor +(docker, shell, etc.). -### Make it work? -1. First, go to terminal and generate a new SSH key: -```bash -$ ssh-keygen -t rsa -f my_key - -Generating public/private rsa key pair. -Enter passphrase (empty for no passphrase): -Enter same passphrase again: -Your identification has been saved in my_key. -Your public key has been saved in my_key.pub. -The key fingerprint is: -SHA256:tBJEfyJUGTMNmPCiPg4UHywHs67MxlM2iEBAlI/W+TY fingeprint -The key's randomart image is: -+---[RSA 2048]----+ -|=*. .o++*= | -|..= +o..o. | -|.+++o + + . | -|+o*=.. + + | -|o+.=. . S | -|*.o .E . | -|o*o . . | -|.o.. | -| . | -+----[SHA256]-----+ -``` +### How it works + +1. Create a new SSH key pair with [ssh-keygen][] +2. Add the private key as a **Secret Variable** to the project +3. Run the [ssh-agent][] during build to load the private key. + +## SSH keys when using the Docker executor -2. Create a new **Secure Variable** in your project settings on GitLab and name it: `SSH_PRIVATE_KEY`. +You will first need to create an SSH key pair. For more information, follow the +instructions to [generate an SSH key](../ssh/README.md). -3. Copy the content of `my_key` and paste it as a **Value** of **SSH_PRIVATE_KEY**. +Then, create a new **Secret Variable** in your project settings on GitLab +following **Settings > Variables**. As **Key** add the name `SSH_PRIVATE_KEY` +and in the **Value** field paste the content of your _private_ key that you +created earlier. + +Next you need to modify your `.gitlab-ci.yml` with a `before_script` action. +Add it to the top: -4. Next you need to modify your `.gitlab-ci.yml` and at the top of the file add: ``` before_script: -# install ssh-agent (it is required for Docker, change apt-get to yum if you use CentOS-based image) -- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' + # Install ssh-agent if not already installed, it is required by Docker. + # (change apt-get to yum if you use a CentOS-based image) + - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' + + # Run ssh-agent (inside the build environment) + - eval $(ssh-agent -s) + + # Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store + - ssh-add <(echo "$SSH_PRIVATE_KEY") + + # For Docker builds disable host key checking. Be aware that by adding that + # you are suspectible to man-in-the-middle attacks. + # WARNING: Use this only with the Docker executor, if you use it with shell + # you will overwrite your user's SSH config. + - mkdir -p ~/.ssh + - '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config` +``` -# run ssh-agent (in build environment) -- eval $(ssh-agent -s) +As a final step, add the _public_ key from the one you created earlier to the +services that you want to have an access to from within the build environment. +If you are accessing a private GitLab repository you need to add it as a +[deploy key](../ssh/README.md#deploy-keys). -# add ssh key stored in SSH_PRIVATE_KEY variable to the agent store -- ssh-add <(echo "$SSH_PRIVATE_KEY") +That's it! You can now have access to private servers or repositories in your +build environment. -# for Docker builds disable host key checking, by adding that you are suspectible to man-in-the-middle attack -- mkdir -p ~/.ssh -- '[[ -f /.dockerinit ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config` -``` +## SSH keys when using the Shell executor -5. Add the public key from `my_key.pub` to services that you want to have an access from build. +If you are using the Shell executor and not Docker, it is easier to set up an +SSH key. -6. If your builds are run using `shell` executor, you may need to login to server and execute the `ssh ` to store the fingerprint of remote server. +You can generate the SSH key from the machine that GitLab Runner is installed +on, and use that key for all projects that are run on this machine. -## SSH keys when using Shell executor -If use `shell`, not `docker` it can be easier to have the SSH key. +First, you need to login to the server that runs your builds. -We can generate the SSH key for the machine that holds `gitlab-runner` and use that key for all projects that are run on this machine. +Then from the terminal login as the `gitlab-runner` user and generate the SSH +key pair as described in the [SSH keys documentation](../ssh/README.md). -1. First, login to server that runs your builds. +As a final step, add the _public_ key from the one you created earlier to the +services that you want to have an access to from within the build environment. +If you are accessing a private GitLab repository you need to add it as a +[deploy key](../ssh/README.md#deploy-keys). + +Once done, try to login to the remote server in order to accept the fingerprint: -2. From terminal login as `gitlab-runner` user and generate the SSH private key: ```bash -$ ssh-keygen -t rsa -Generating public/private rsa key pair. -Enter passphrase (empty for no passphrase): -Enter same passphrase again: -Your identification has been saved in ~/.ssh/id_rsa. -Your public key has been saved in ~/.ssh/id_rsa.pub. -The key fingerprint is: -SHA256:tBJEfyJUGTMNmPCiPg4UHywHs67MxlM2iEBAlI/W+TY fingeprint -The key's randomart image is: -+---[RSA 2048]----+ -|=*. .o++*= | -|..= +o..o. | -|.+++o + + . | -|+o*=.. + + | -|o+.=. . S | -|*.o .E . | -|o*o . . | -|.o.. | -| . | -+----[SHA256]-----+ +ssh ``` -3. Add the public key from `~/.ssh/id_rsa.pub` to services that you want to have an access from build. +For accessing repositories on GitLab.com, the `` would be +`git@gitlab.com`. -4. Try to login for the first time and accept fingerprint: -```bash -ssh Date: Fri, 4 Dec 2015 11:47:02 +0200 Subject: Move markdown link to the bottom [ci skip] --- doc/ci/languages/php.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md index 589253cfc4c..60079c090d3 100644 --- a/doc/ci/languages/php.md +++ b/doc/ci/languages/php.md @@ -263,13 +263,14 @@ gitlab-runner exec shell test:app ## Example project -We have set up an [Example PHP Project](https://gitlab.com/gitlab-examples/php) -for your convenience that runs on [GitLab.com](https://gitlab.com) using our -publicly available [shared runners](../runners/README.md). +We have set up an [Example PHP Project][php-example-repo] for your convenience +that runs on [GitLab.com](https://gitlab.com) using our publicly available +[shared runners](../runners/README.md). -Want to hack it? Simply fork it, commit and push your changes. Within a few +Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. [php-hub]: https://hub.docker.com/_/php/ [phpenv]: https://github.com/phpenv/phpenv [phpenv-installation]: https://github.com/phpenv/phpenv#installation +[php-example-repo]: https://gitlab.com/gitlab-examples/php -- cgit v1.2.1 From 4fc9e6944e363e3167fede07c0d6898ff6c7e19f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 4 Dec 2015 11:47:50 +0200 Subject: Add an intro to CI services documentation [ci skip] --- doc/ci/services/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md index 0550e9435a3..0856b7679c2 100644 --- a/doc/ci/services/README.md +++ b/doc/ci/services/README.md @@ -1,6 +1,9 @@ ## GitLab CI Services +GitLab CI uses the `service` keyword to define what docker containers should be +linked with your base image. Below is a list of examples you may use. + + [Using MySQL](mysql.md) + [Using PostgreSQL](postgres.md) + [Using Redis](redis.md) -+ [Using Other Services](../docker/using_docker_images.html#how-to-use-other-images-as-services) ++ [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services) -- cgit v1.2.1 From 4236861df03ff86fc1b2bb8a6a0835b0f3a03244 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 7 Dec 2015 09:39:14 +0200 Subject: Clean up Redis CI service example [ci skip] --- doc/ci/services/redis.md | 72 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md index 523634a457e..b0d04f95408 100644 --- a/doc/ci/services/redis.md +++ b/doc/ci/services/redis.md @@ -1,40 +1,68 @@ -## Using Redis +# Using Redis -It's possible to use Redis database test your apps during builds. +As many applications depend on Redis as their key-value store, you will +eventually need it in order for your tests to run. Below you are guided how to +do this with the Docker and Shell executors of GitLab Runner. -### Use Redis with Docker executor +## Use Redis with Docker executor -If you are using our Docker integration you basically have everything already. +If you are using our Docker integration you basically have everything set up +already. -1. Add this to your `.gitlab-ci.yml`: +First, in your `.gitlab-ci.yml` add: - services: - - redis +```yaml +services: + - redis:latest +``` -2. Configure your application to use the database: +Then you need to configure your application to use the Redis database, for +example: - Host: redis +```bash +Host: redis +``` -3. You can also use any other available on [DockerHub](https://hub.docker.com/_/redis/). For example: `redis:2.6`. +And that's it. Redis will now be available to be used within your testing +framework. -Example: https://gitlab.com/gitlab-examples/redis/blob/master/.gitlab-ci.yml +If you want to use any other version of Redis, check the available versions +on [Docker Hub](https://hub.docker.com/_/redis/). -### Use Redis with Shell executor +## Use Redis with Shell executor -It's possible to use Redis on manually configured servers that are using GitLab Runner with Shell executor. +Redis can also be used on manually configured servers that are using GitLab +Runner with the Shell executor. -1. First install the Redis server: +In your build machine install the Redis server: - sudo apt-get install redis-server +```bash +sudo apt-get install redis-server +``` -2. Try to connect to the server: +Verify that you can connect to the server with the `gitlab-runner` user: - # Try connecting the the Redis server - sudo -u gitlab-runner -H redis-cli +```bash +# Try connecting the the Redis server +sudo -u gitlab-runner -H redis-cli - # Quit the session - 127.0.0.1:6379> quit +# Quit the session +127.0.0.1:6379> quit +``` -4. Configure your application to use the database: +Finally, configure your application to use the database, for example: - Host: localhost +```bash +Host: localhost +``` + +## Example project + +We have set up an [Example Redis Project][redis-example-repo] for your convenience +that runs on [GitLab.com](https://gitlab.com) using our publicly available +[shared runners](../runners/README.md). + +Want to hack on it? Simply fork it, commit and push your changes. Within a few +moments the changes will be picked by a public runner and the build will begin. + +[redis-example-repo]: https://gitlab.com/gitlab-examples/redis -- cgit v1.2.1 From 149c934a1d38662358b48cdf844430de7845d1c4 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 15:59:41 +0200 Subject: More cleanup on Redis example --- doc/ci/services/redis.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md index b0d04f95408..e6add57ca76 100644 --- a/doc/ci/services/redis.md +++ b/doc/ci/services/redis.md @@ -4,10 +4,10 @@ As many applications depend on Redis as their key-value store, you will eventually need it in order for your tests to run. Below you are guided how to do this with the Docker and Shell executors of GitLab Runner. -## Use Redis with Docker executor +## Use Redis with the Docker executor -If you are using our Docker integration you basically have everything set up -already. +If you are using GitLab's Runner Docker integration you basically have +everything set up already. First, in your `.gitlab-ci.yml` add: @@ -29,7 +29,7 @@ framework. If you want to use any other version of Redis, check the available versions on [Docker Hub](https://hub.docker.com/_/redis/). -## Use Redis with Shell executor +## Use Redis with the Shell executor Redis can also be used on manually configured servers that are using GitLab Runner with the Shell executor. -- cgit v1.2.1 From 06b86de996232b956129f2bb5dde4a7647f94f69 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 18:22:45 +0200 Subject: Clean up postgres CI example [ci skip] --- doc/ci/services/postgres.md | 116 +++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 45 deletions(-) diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index e57f8c5944a..f82a828756c 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -1,70 +1,96 @@ -## Using PostgreSQL +# Using PostgreSQL -It's possible to use PostgreSQL database test your apps during builds. +As many applications depend on PostgreSQL as their database, you will +eventually need it in order for your tests to run. Below you are guided how to +do this with the Docker and Shell executors of GitLab Runner. -### Use PostgreSQL with Docker executor +## Use PostgreSQL with the Docker executor -If you are using our Docker integration you basically have everything already. +If you are using GitLab's Runner with the Docker executor you basically have +everything set up already. -1. Add this to your `.gitlab-ci.yml`: +First, in your `.gitlab-ci.yml` add: - services: - - postgres +```yaml +services: + - postgres - variables: - # Configure postgres service (https://hub.docker.com/_/postgres/) - POSTGRES_DB: hello_world_test - POSTGRES_USER: postgres - POSTGRES_PASSWORD: "" +variables: + POSTGRES_DB: nice_marmot + POSTGRES_USER: gitlab_runner + POSTGRES_PASSWORD: "" +``` -2. Configure your application to use the database: +And then configure your application to use PostgreSQL, for example: - Host: postgres - User: postgres - Password: postgres - Database: hello_world_test +```yaml +Host: localhost +User: gitlab_runner +Password: +Database: nice_marmot +``` -3. You can also use any other available on [DockerHub](https://hub.docker.com/_/postgres/). For example: `postgres:9.3`. +You can also use any other docker image available on [Docker Hub][hub-pg]. +For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`. -Example: https://gitlab.com/gitlab-examples/postgres/blob/master/.gitlab-ci.yml +The `postgres` image can accept some environment variables. For more details +check the documentation on [Docker Hub][hub-pg]. -### Use PostgreSQL with Shell executor +## Use PostgreSQL with the Shell executor -It's possible to use PostgreSQL on manually configured servers that are using GitLab Runner with Shell executor. +You can also use PostgreSQL on manually configured servers that are using +GitLab Runner with the Shell executor. -1. First install the PostgreSQL server: +First install the PostgreSQL server: - sudo apt-get install -y postgresql postgresql-client libpq-dev +```bash +sudo apt-get install -y postgresql postgresql-client libpq-dev +``` -2. Create an user: +Then create a user: - # Install the database packages - sudo apt-get install -y postgresql postgresql-client libpq-dev +```bash +# Login to PostgreSQL +sudo -u postgres psql -d template1 - # Login to PostgreSQL - sudo -u postgres psql -d template1 +# Create a user for GitLab Runner that can create databases +# Do not type the 'template1=#', this is part of the prompt +template1=# CREATE USER gitlab_runner CREATEDB; - # Create a user for runner - # Do not type the 'template1=#', this is part of the prompt - template1=# CREATE USER runner CREATEDB; +# Create the database & grant all privileges on database +template1=# CREATE DATABASE nice_marmot OWNER gitlab_runner; - # Create the database & grant all privileges on database - template1=# CREATE DATABASE hello_world_test OWNER runner; +# Quit the database session +template1=# \q +``` - # Quit the database session - template1=# \q +Try to connect to database: -3. Try to connect to database: +```bash +# Try connecting to the new database with the new user +sudo -u gitlab_runner -H psql -d nice_marmot - # Try connecting to the new database with the new user - sudo -u gitlab-runner -H psql -d hello_world_test +# Quit the database session +nice_marmot> \q +``` - # Quit the database session - hello_world_test> \q +Finally, configure your application to use the database: -4. Configure your application to use the database: +```bash +Host: localhost +User: gitlab_runner +Password: +Database: nice_marmot +``` - Host: localhost - User: runner - Password: - Database: hello_world_test +## Example project + +We have set up an [Example PostgreSQL Project][postgres-example-repo] for your +convenience that runs on [GitLab.com](https://gitlab.com) using our publicly +available [shared runners](../runners/README.md). + +Want to hack on it? Simply fork it, commit and push your changes. Within a few +moments the changes will be picked by a public runner and the build will begin. + +[hub-pg]: https://hub.docker.com/_/postgres/ +[postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres -- cgit v1.2.1 From 1d410ac96a96462d9f48ec4e43a4e819bbffdeee Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 7 Dec 2015 23:01:53 -0800 Subject: Update project repository size and commit count during import:repos task Closes #3848 --- CHANGELOG | 1 + lib/tasks/gitlab/import.rake | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 52c5941a3ab..7c0c94f4874 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - Bump gollum-lib to 4.1.0 (Stan Hu) - Fix broken group avatar upload under "New group" (Stan Hu) + - Update project repositorize size and commit count during import:repos task (Stan Hu) - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Handle and report SSL errors in Web hook test (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake index c1ee271ae2b..1c04f47f08f 100644 --- a/lib/tasks/gitlab/import.rake +++ b/lib/tasks/gitlab/import.rake @@ -64,6 +64,8 @@ namespace :gitlab do if project.persisted? puts " * Created #{project.name} (#{repo_path})".green + project.update_repository_size + project.update_commit_count else puts " * Failed trying to create #{project.name} (#{repo_path})".red puts " Errors: #{project.errors.messages}".red -- cgit v1.2.1 From bf5683f8892c4aefc4c996812ece6291b701dada Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Tue, 8 Dec 2015 09:47:42 -0600 Subject: Block LDAP user when they are no longer found in the LDAP server --- CHANGELOG | 1 + doc/integration/ldap.md | 8 +++++++- lib/gitlab/ldap/access.rb | 4 +++- spec/lib/gitlab/ldap/access_spec.rb | 5 +++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7c0c94f4874..67f70e676c2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.3.0 (unreleased) - Run custom Git hooks when branch is created or deleted. - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch - Add languages page to graphs + - Block LDAP user when they are no longer found in the LDAP server v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md index 7e2920b8865..845f588f913 100644 --- a/doc/integration/ldap.md +++ b/doc/integration/ldap.md @@ -13,6 +13,12 @@ An LDAP user who is allowed to change their email on the LDAP server can [take o We recommend against using GitLab LDAP integration if your LDAP users are allowed to change their 'mail', 'email' or 'userPrincipalName' attribute on the LDAP server. +If a user is deleted from the LDAP server, they will be blocked in GitLab as well. +Users will be immediately blocked from logging in. However, there is an LDAP check +cache time of one hour. The means users that are already logged in or are using Git +over SSH will still be able to access GitLab for up to one hour. Manually block +the user in the GitLab Admin area to immediately block all access. + ## Configuring GitLab for LDAP integration To enable GitLab LDAP integration you need to add your LDAP server settings in `/etc/gitlab/gitlab.rb` or `/home/git/gitlab/config/gitlab.yml`. @@ -192,4 +198,4 @@ Not supported by GitLab's configuration options. When setting `method: ssl`, the underlying authentication method used by `omniauth-ldap` is `simple_tls`. This method establishes TLS encryption with the LDAP server before any LDAP-protocol data is exchanged but no validation of -the LDAP server's SSL certificate is performed. \ No newline at end of file +the LDAP server's SSL certificate is performed. diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb index 16ff03c38d4..c438a3d167b 100644 --- a/lib/gitlab/ldap/access.rb +++ b/lib/gitlab/ldap/access.rb @@ -37,13 +37,15 @@ module Gitlab # Block user in GitLab if he/she was blocked in AD if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter) - user.block unless user.blocked? + user.block false else user.activate if user.blocked? && !ldap_config.block_auto_created_users true end else + # Block the user if they no longer exist in LDAP/AD + user.block false end rescue diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb index c38f212b405..960547a0ad7 100644 --- a/spec/lib/gitlab/ldap/access_spec.rb +++ b/spec/lib/gitlab/ldap/access_spec.rb @@ -13,6 +13,11 @@ describe Gitlab::LDAP::Access do end it { is_expected.to be_falsey } + + it 'should block user in GitLab' do + access.allowed? + expect(user).to be_blocked + end end context 'when the user is found' do -- cgit v1.2.1 From 47e81da8c2746196ad0506d30720c775a538ebdb Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 21:11:11 +0200 Subject: Clean up mysql CI example --- doc/ci/services/mysql.md | 137 +++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 47 deletions(-) diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index 3155af6b3e1..7f50b4d76c8 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -1,72 +1,115 @@ -## Using MySQL +# Using MySQL -It's possible to use MySQL database test your apps during builds. +As many applications depend on MySQL as their database, you will eventually +need it in order for your tests to run. Below you are guided how to do this +with the Docker and Shell executors of GitLab Runner. -### Use MySQL with Docker executor +## Use MySQL with the Docker executor -If you are using our Docker integration you basically have everything already. +If you are using [GitLab Runner](../runners/README.md) with the Docker executor +you basically have everything set up already. -1. Add this to your `.gitlab-ci.yml`: +First, in your `.gitlab-ci.yml` add: - services: - - mysql +```yaml +services: + - mysql - variables: - # Configure mysql service (https://hub.docker.com/_/mysql/) - MYSQL_DATABASE: hello_world_test - MYSQL_ROOT_PASSWORD: mysql +variables: + # Configure mysql environment variables (https://hub.docker.com/_/mysql/) + MYSQL_DATABASE: el_duderino + MYSQL_ROOT_PASSWORD: mysql_strong_password +``` -2. Configure your application to use the database: +And then configure your application to use the database, for example: - Host: mysql - User: root - Password: mysql - Database: hello_world_test +```yaml +Host: localhost +User: root +Password: mysql_strong_password +Database: el_duderino +``` -3. You can also use any other available on [DockerHub](https://hub.docker.com/_/mysql/). For example: `mysql:5.5`. +You can also use any other docker image available on [Docker Hub][hub-mysql]. +For example, to use MySQL 5.5 the service becomes `mysql:5.5`. -Example: https://gitlab.com/gitlab-examples/mysql/blob/master/.gitlab-ci.yml +The `mysql` image can accept some environment variables. For more details +check the documentation on [Docker Hub][hub-mysql]. -### Use MySQL with Shell executor +## Use MySQL with the Shell executor -It's possible to use MySQL on manually configured servers that are using GitLab Runner with Shell executor. +You can also use MySQL on manually configured servers that are using +GitLab Runner with the Shell executor. -1. First install the MySQL server: - - sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev +First install the MySQL server: - # Pick a MySQL root password (can be anything), type it and press enter - # Retype the MySQL root password and press enter +```bash +sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev +``` -2. Create an user: +Pick a MySQL root password (can be anything), and type it twice when asked. - mysql -u root -p +*Note: As a security measure you can run `mysql_secure_installation` to +remove anonymous users, drop the test database and disable remote logins with +the root user.* - # Create a user which will be used by your apps - # do not type the 'mysql>', this is part of the prompt - # change $password in the command below to a real password you pick - mysql> CREATE USER 'runner'@'localhost' IDENTIFIED BY '$password'; +The next step is to create a user, so login to MySQL as root: - # Ensure you can use the InnoDB engine which is necessary to support long indexes - # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" - mysql> SET storage_engine=INNODB; +```bash +mysql -u root -p +``` - # Create the database - mysql> CREATE DATABASE IF NOT EXISTS `hello_world_test` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; +Then create a user (in our case `runner`) which will be used by your +application. Change `$password` in the command below to a real strong password. - # Grant necessary permissions on the database - mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `hello_world_test`.* TO 'runner'@'localhost'; +*Note: Do not type `mysql>`, this is part of the MySQL prompt.* - # Quit the database session - mysql> \q +```bash +mysql> CREATE USER 'runner'@'localhost' IDENTIFIED BY '$password'; +``` -3. Try to connect to database: +Create the database: - sudo -u gitlab-runner -H mysql -u runner -p -D hello_world_test +```bash +mysql> CREATE DATABASE IF NOT EXISTS `el_duderino` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; +``` -4. Configure your application to use the database: +Grant the necessary permissions on the database: - Host: localhost - User: runner - Password: $password - Database: hello_world_test +```bash +mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES ON `el_duderino`.* TO 'runner'@'localhost'; +``` + +If all went well you can now quit the database session: + +```bash +mysql> \q +``` + +Now, try to connect to the newly created database to check that everything is +in place: + +```bash +mysql -u runner -p -D el_duderino +``` + +As a final step, configure your application to use the database, for example: + +```bash +Host: localhost +User: runner +Password: $password +Database: el_duderino +``` + +## Example project + +We have set up an [Example MySQL Project][mysql-example-repo] for your +convenience that runs on [GitLab.com](https://gitlab.com) using our publicly +available [shared runners](../runners/README.md). + +Want to hack on it? Simply fork it, commit and push your changes. Within a few +moments the changes will be picked by a public runner and the build will begin. + +[hub-mysql]: https://hub.docker.com/_/mysql/ +[mysql-example-repo]: https://gitlab.com/gitlab-examples/mysql -- cgit v1.2.1 From 9dc91f46df377d6220928d6292dac73bc6bae295 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 21:11:27 +0200 Subject: Add link to runners doc [ci skip] --- doc/ci/services/postgres.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index f82a828756c..b8436f4f7ca 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -6,8 +6,8 @@ do this with the Docker and Shell executors of GitLab Runner. ## Use PostgreSQL with the Docker executor -If you are using GitLab's Runner with the Docker executor you basically have -everything set up already. +If you are using [GitLab Runner](../runners/README.md) with the Docker executor +you basically have everything set up already. First, in your `.gitlab-ci.yml` add: @@ -21,7 +21,7 @@ variables: POSTGRES_PASSWORD: "" ``` -And then configure your application to use PostgreSQL, for example: +And then configure your application to use the database, for example: ```yaml Host: localhost -- cgit v1.2.1 From cf44dc510786775a1303384d858eb0a7bf33c34f Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 21:35:08 +0200 Subject: Add note about the various phpenv tools --- doc/ci/languages/php.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/ci/languages/php.md b/doc/ci/languages/php.md index 60079c090d3..dacb67fa3ff 100644 --- a/doc/ci/languages/php.md +++ b/doc/ci/languages/php.md @@ -179,6 +179,14 @@ Using phpenv also allows to easily configure the PHP environment with: phpenv config-add my_config.ini ``` +*__Important note:__ It seems `phpenv/phpenv` + [is abandoned](https://github.com/phpenv/phpenv/issues/57). There is a fork + at [madumlao/phpenv](https://github.com/madumlao/phpenv) that tries to bring + the project back to life. [CHH/phpenv](https://github.com/CHH/phpenv) also + seems like a good alternative. Picking any of the mentioned tools will work + with the basic phpenv commands. Guiding you to choose the right phpenv is out + of the scope of this tutorial.* + ### Install custom extensions Since this is a pretty bare installation of the PHP environment, you may need -- cgit v1.2.1 From 4ea0f06470620064c5d23e28c778f83296aeb551 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 21:35:25 +0200 Subject: Fix wrong naming of services keyword [ci skip] --- doc/ci/services/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md index 0856b7679c2..1ebb0a4a250 100644 --- a/doc/ci/services/README.md +++ b/doc/ci/services/README.md @@ -1,6 +1,6 @@ ## GitLab CI Services -GitLab CI uses the `service` keyword to define what docker containers should be +GitLab CI uses the `services` keyword to define what docker containers should be linked with your base image. Below is a list of examples you may use. + [Using MySQL](mysql.md) -- cgit v1.2.1 From 0c22635aa2036ab3394c7e7bd99fd61ddd99c43c Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 21:41:56 +0200 Subject: More redis CI example clean up --- doc/ci/services/redis.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md index e6add57ca76..b281e8f9f60 100644 --- a/doc/ci/services/redis.md +++ b/doc/ci/services/redis.md @@ -6,8 +6,8 @@ do this with the Docker and Shell executors of GitLab Runner. ## Use Redis with the Docker executor -If you are using GitLab's Runner Docker integration you basically have -everything set up already. +If you are using [GitLab Runner](../runners/README.md) with the Docker executor +you basically have everything set up already. First, in your `.gitlab-ci.yml` add: @@ -19,15 +19,15 @@ services: Then you need to configure your application to use the Redis database, for example: -```bash +```yaml Host: redis ``` And that's it. Redis will now be available to be used within your testing framework. -If you want to use any other version of Redis, check the available versions -on [Docker Hub](https://hub.docker.com/_/redis/). +You can also use any other docker image available on [Docker Hub][hub-redis]. +For example, to use Redis 2.8 the service becomes `redis:2.8`. ## Use Redis with the Shell executor @@ -52,7 +52,7 @@ sudo -u gitlab-runner -H redis-cli Finally, configure your application to use the database, for example: -```bash +```yaml Host: localhost ``` @@ -65,4 +65,5 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available Want to hack on it? Simply fork it, commit and push your changes. Within a few moments the changes will be picked by a public runner and the build will begin. +[hub-redis]: https://hub.docker.com/_/redis/ [redis-example-repo]: https://gitlab.com/gitlab-examples/redis -- cgit v1.2.1 From ebee5077f2437d0828784ff23b19ed470bed534d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Dec 2015 14:47:28 -0500 Subject: Make tab target selectors less naive Prior, any of the specified IDs could have been hijacked by a table of contents header, breaking the tab functionality. For example, a `## Notes` header would get the id `notes` and prevent the Discussion tab from being activated. Closes #3908 --- app/views/projects/merge_requests/_new_submit.html.haml | 4 ++-- app/views/projects/merge_requests/_show.html.haml | 6 +++--- spec/javascripts/fixtures/merge_request_tabs.html.haml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 156922cea41..0bcc826e8d4 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -20,11 +20,11 @@ .mr-compare.merge-request %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.commits-tab - = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do + = link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size %li.diffs-tab.active - = link_to url_for(params), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do + = link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do Changes %span.badge= @diffs.size diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index f5aff0877e7..6a89df38231 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -44,15 +44,15 @@ - if @commits.present? %ul.merge-request-tabs.center-top-menu.no-top.no-bottom %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do + = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do Discussion %span.badge= @merge_request.mr_and_commit_notes.user.count %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#commits', action: 'commits', toggle: 'tab'} do + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#diffs', action: 'diffs', toggle: 'tab'} do + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do Changes %span.badge= @merge_request.diffs.size diff --git a/spec/javascripts/fixtures/merge_request_tabs.html.haml b/spec/javascripts/fixtures/merge_request_tabs.html.haml index 7624a713948..68678c3d7e3 100644 --- a/spec/javascripts/fixtures/merge_request_tabs.html.haml +++ b/spec/javascripts/fixtures/merge_request_tabs.html.haml @@ -1,12 +1,12 @@ %ul.nav.nav-tabs.merge-request-tabs %li.notes-tab - %a{href: '/foo/bar/merge_requests/1', data: {target: '#notes', action: 'notes', toggle: 'tab'}} + %a{href: '/foo/bar/merge_requests/1', data: {target: 'div#notes', action: 'notes', toggle: 'tab'}} Discussion %li.commits-tab - %a{href: '/foo/bar/merge_requests/1/commits', data: {target: '#commits', action: 'commits', toggle: 'tab'}} + %a{href: '/foo/bar/merge_requests/1/commits', data: {target: 'div#commits', action: 'commits', toggle: 'tab'}} Commits %li.diffs-tab - %a{href: '/foo/bar/merge_requests/1/diffs', data: {target: '#diffs', action: 'diffs', toggle: 'tab'}} + %a{href: '/foo/bar/merge_requests/1/diffs', data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'}} Diffs .tab-content -- cgit v1.2.1 From bbe652cdaf11dae7fa917bac1ffa4a02f2099026 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 22:26:41 +0200 Subject: More postgres CI service example cleanup --- doc/ci/services/postgres.md | 55 ++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index b8436f4f7ca..db5be070ee7 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -13,11 +13,11 @@ First, in your `.gitlab-ci.yml` add: ```yaml services: - - postgres + - postgres:latest variables: POSTGRES_DB: nice_marmot - POSTGRES_USER: gitlab_runner + POSTGRES_USER: runner POSTGRES_PASSWORD: "" ``` @@ -25,7 +25,7 @@ And then configure your application to use the database, for example: ```yaml Host: localhost -User: gitlab_runner +User: runner Password: Database: nice_marmot ``` @@ -47,39 +47,54 @@ First install the PostgreSQL server: sudo apt-get install -y postgresql postgresql-client libpq-dev ``` -Then create a user: +The next step is to create a user, so login to PostgreSQL: ```bash -# Login to PostgreSQL sudo -u postgres psql -d template1 +``` -# Create a user for GitLab Runner that can create databases -# Do not type the 'template1=#', this is part of the prompt -template1=# CREATE USER gitlab_runner CREATEDB; +Then create a user (in our case `runner`) which will be used by your +application. Change `$password` in the command below to a real strong password. -# Create the database & grant all privileges on database -template1=# CREATE DATABASE nice_marmot OWNER gitlab_runner; +*__Note:__ Do not type `template1=#`, this is part of the PostgreSQL prompt.* -# Quit the database session -template1=# \q +```bash +template1=# CREATE USER runner WITH PASSWORD '$password' CREATEDB; ``` -Try to connect to database: +*__Note:__ Notice that we created the user with the privilege to be able to +create databases (`CREATEDB`). In the following steps we will create a database +explicitly for that user but having that privilege can be useful if in your +testing framework you have tools that drop and create databases.* + +Create the database and grant all privileges on it for the user `runner`: ```bash -# Try connecting to the new database with the new user -sudo -u gitlab_runner -H psql -d nice_marmot +template1=# CREATE DATABASE nice_marmot OWNER runner; +``` + +If all went well you can now quit the database session: -# Quit the database session -nice_marmot> \q +```bash +template1=# \q ``` -Finally, configure your application to use the database: +Now, try to connect to the newly created database with the user `runner` to +check that everything is in place. ```bash +psql -U runner -h localhost -d nice_marmot -W +``` + +*__Note:__ We are explicitly telling `psql` to connect to localhost in order +to use the md5 authentication. If you omit this step you will be denied access.* + +Finally, configure your application to use the database, for example: + +```yaml Host: localhost -User: gitlab_runner -Password: +User: runner +Password: $password Database: nice_marmot ``` -- cgit v1.2.1 From d55dcd11d59ebfadf49290746c5f30c2e4780c6b Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 22:27:23 +0200 Subject: Use the latest docker image in mysql CI example [ci skip] --- doc/ci/services/mysql.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index 7f50b4d76c8..44bc613e6f7 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -13,7 +13,7 @@ First, in your `.gitlab-ci.yml` add: ```yaml services: - - mysql + - mysql:latest variables: # Configure mysql environment variables (https://hub.docker.com/_/mysql/) -- cgit v1.2.1 From 813c9b2aeb9e5d44c99f02a2373ee0e007d0e87d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Dec 2015 15:29:46 -0500 Subject: Make cross-project reference's clipboard target less naive See !2023 --- app/views/projects/issues/_discussion.html.haml | 2 +- app/views/projects/merge_requests/_discussion.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 21eee70b424..f2011542ca7 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -17,7 +17,7 @@ .input-group.cross-project-reference %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @issue) - = clipboard_button(clipboard_target: '#cross-project-reference') + = clipboard_button(clipboard_target: 'span#cross-project-reference') .row %section.col-md-9 diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 3d7bd78dce3..d64b19ae91a 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -17,7 +17,7 @@ .input-group.cross-project-reference %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} = cross_project_reference(@project, @merge_request) - = clipboard_button(clipboard_target: '#cross-project-reference') + = clipboard_button(clipboard_target: 'span#cross-project-reference') .row %section.col-md-9 -- cgit v1.2.1 From f66454beaa880d283ef527c3a32a3076fedf6551 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 21:43:11 +0100 Subject: Add indication to merge request list item that MR cannot be merged automatically --- app/assets/stylesheets/framework/lists.scss | 18 ++++++++++ app/models/merge_request.rb | 4 +++ app/views/projects/issues/_issue.html.haml | 25 +++++++------ .../merge_requests/_merge_request.html.haml | 41 +++++++++++++--------- 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index a798ae812e3..2404f8898fe 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -124,6 +124,24 @@ ul.content-list { padding: 10px 14px; } } + + ul.controls { + list-style: none; + + li { + float: left; + padding-right: 10px; + + .author_link { + display: inline-block; + + .avatar-inline { + margin-left: 0; + margin-right: 0; + } + } + } + } } } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 080b7f7fb88..1f84dc2a577 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -484,4 +484,8 @@ class MergeRequest < ActiveRecord::Base source_project.ci_commit(last_commit.id) end end + + def broken? + self.commits.blank? || branch_missing? || cannot_be_merged? + end end diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 1eb71990e55..7d9fc8d2c86 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,23 +6,26 @@ .issue-title %span.issue-title-text = link_to_gfm issue.title, issue_path(issue), class: "row_title" - .pull-right.light + %ul.controls.light - if issue.closed? - %span + %li CLOSED + - if issue.assignee - = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") + %li + = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name") + - note_count = issue.notes.user.count - if note_count > 0 -   - = link_to issue_path(issue) + "#notes" do - = icon('comments') - = note_count + %li + = link_to issue_path(issue) + "#notes" do + = icon('comments') + = note_count - else -   - = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do - = icon('comments') - = 0 + %li + = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do + = icon('comments') + = notes_count .issue-info #{issue.to_reference} · diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 1d4c9b66c42..5ee7738e9c4 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -3,31 +3,40 @@ .merge-request-title %span.merge-request-title-text = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" - .pull-right.light - - if ci_commit - = render_ci_status(ci_commit) + %ul.controls.light - if merge_request.merged? - %span + %li = icon('check') MERGED - elsif merge_request.closed? - %span + %li = icon('ban') CLOSED - - note_count = merge_request.mr_and_commit_notes.user.count + + - if ci_commit + %li + = render_ci_status(ci_commit) + + - if merge_request.open? && merge_request.broken? + %li + = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: {container: 'body'} do + = icon('exclamation-triangle') + - if merge_request.assignee -   - = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") + %li + = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name") + + - note_count = merge_request.mr_and_commit_notes.user.count - if note_count > 0 -   - = link_to merge_request_path(merge_request) + "#notes" do - = icon('comments') - = note_count + %li + = link_to merge_request_path(merge_request) + "#notes" do + = icon('comments') + = note_count - else -   - = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do - = icon('comments') - = 0 + %li + = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do + = icon('comments') + = note_count .merge-request-info \##{merge_request.iid} · -- cgit v1.2.1 From 065375ca2d482faac80897e42d82b3afdcb08d99 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 21:43:52 +0100 Subject: Use "Build passed" in tooltip instead of "Build status: passed" --- app/helpers/ci_status_helper.rb | 2 +- features/steps/dashboard/dashboard.rb | 2 +- features/steps/project/merge_requests.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 0ecf77bb45e..599a9adc31a 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -46,7 +46,7 @@ module CiStatusHelper def render_ci_status(ci_commit) link_to ci_status_path(ci_commit), class: "c#{ci_status_color(ci_commit)}", - title: "Build status: #{ci_commit.status}", + title: "Build #{ci_commit.status}", data: { toggle: 'tooltip', placement: 'left' } do ci_status_icon(ci_commit) end diff --git a/features/steps/dashboard/dashboard.rb b/features/steps/dashboard/dashboard.rb index f0fbd8a826a..63f0ec2b6e8 100644 --- a/features/steps/dashboard/dashboard.rb +++ b/features/steps/dashboard/dashboard.rb @@ -12,7 +12,7 @@ class Spinach::Features::Dashboard < Spinach::FeatureSteps end step 'I should see "Shop" project CI status' do - expect(page).to have_link "Build status: skipped" + expect(page).to have_link "Build skipped" end step 'I should see last push widget' do diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 822cf0ffe1c..0107d9d8486 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -367,7 +367,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps step 'I should see merge request "Bug NS-05" with CI status' do page.within ".mr-list" do - expect(page).to have_link "Build status: pending" + expect(page).to have_link "Build pending" end end -- cgit v1.2.1 From 23b6a98de00b966728f6b5ed3747b0d2e078165f Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 22:28:28 +0100 Subject: Move Builds tab to the end --- .../javascripts/merge_request_tabs.js.coffee | 26 +++++++++++----------- .../projects/merge_requests/_new_submit.html.haml | 16 ++++++------- app/views/projects/merge_requests/_show.html.haml | 12 +++++----- config/routes.rb | 2 +- .../markdown/merge_request_reference_filter.rb | 4 ++-- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 69a12fdd045..b0eeb1db536 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -68,10 +68,10 @@ class @MergeRequestTabs if action == 'commits' @loadCommits($target.attr('href')) - else if action == 'builds' - @loadBuilds($target.attr('href')) else if action == 'diffs' @loadDiff($target.attr('href')) + else if action == 'builds' + @loadBuilds($target.attr('href')) @setCurrentAction(action) @@ -110,7 +110,7 @@ class @MergeRequestTabs action = 'notes' if action == 'show' # Remove a trailing '/commits' or '/diffs' - new_state = @_location.pathname.replace(/\/(commits|builds|diffs)(\.html)?\/?$/, '') + new_state = @_location.pathname.replace(/\/(commits|diffs|builds)(\.html)?\/?$/, '') # Append the new action if we're on a tab other than 'notes' unless action == 'notes' @@ -138,6 +138,16 @@ class @MergeRequestTabs @commitsLoaded = true @scrollToElement("#commits") + loadDiff: (source) -> + return if @diffsLoaded + + @_get + url: "#{source}.json" + @_location.search + success: (data) => + document.getElementById('diffs').innerHTML = data.html + @diffsLoaded = true + @scrollToElement("#diffs") + loadBuilds: (source) -> return if @buildsLoaded @@ -149,16 +159,6 @@ class @MergeRequestTabs @buildsLoaded = true @scrollToElement("#builds") - loadDiff: (source) -> - return if @diffsLoaded - - @_get - url: "#{source}.json" + @_location.search - success: (data) => - document.getElementById('diffs').innerHTML = data.html - @diffsLoaded = true - @scrollToElement("#diffs") - # Show or hide the loading spinner # # status - Boolean, true to show, false to hide diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index 3f6e421fcd7..4172d5a4e88 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -23,22 +23,19 @@ = link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size - - if @ci_commit - %li.builds-tab.active - = link_to url_for(params), data: {target: '#builds', action: 'builds', toggle: 'tab'} do - Builds - %span.badge= @statuses.size %li.diffs-tab.active = link_to url_for(params), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do Changes %span.badge= @diffs.size + - if @ci_commit + %li.builds-tab.active + = link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do + Builds + %span.badge= @statuses.size .tab-content #commits.commits.tab-pane = render "projects/merge_requests/show/commits" - - if @ci_commit - #builds.builds.tab-pane - = render "projects/merge_requests/show/builds" #diffs.diffs.tab-pane.active - if @diffs.present? = render "projects/diffs/diffs", diffs: @diffs, project: @project @@ -50,6 +47,9 @@ .alert.alert-danger %h4 This comparison includes a huge diff. %p To preserve performance the line changes are not shown. + - if @ci_commit + #builds.builds.tab-pane + = render "projects/merge_requests/show/builds" :javascript $('.assign-to-me-link').on('click', function(e){ diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 686a9a2c758..960d1561e73 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -50,25 +50,25 @@ = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do Commits %span.badge= @commits.size + %li.diffs-tab + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do + Changes + %span.badge= @merge_request.diffs.size - if @ci_commit %li.builds-tab = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do Builds %span.badge= @statuses.size - %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do - Changes - %span.badge= @merge_request.diffs.size .tab-content #notes.notes.tab-pane.voting_notes = render "projects/merge_requests/discussion" #commits.commits.tab-pane - # This tab is always loaded via AJAX - #builds.builds.tab-pane - - # This tab is always loaded via AJAX #diffs.diffs.tab-pane - # This tab is always loaded via AJAX + #builds.builds.tab-pane + - # This tab is always loaded via AJAX .mr-loading-status = spinner diff --git a/config/routes.rb b/config/routes.rb index 359cd08e134..046e1800235 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -570,9 +570,9 @@ Rails.application.routes.draw do resources :merge_requests, constraints: { id: /\d+/ }, except: [:destroy] do member do + get :commits get :diffs get :builds - get :commits post :merge get :merge_check get :ci_status diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb index 79d67870b14..2eb77c46da7 100644 --- a/lib/gitlab/markdown/merge_request_reference_filter.rb +++ b/lib/gitlab/markdown/merge_request_reference_filter.rb @@ -28,10 +28,10 @@ module Gitlab case path when '/diffs' extras.unshift "diffs" - when '/builds' - extras.unshift "builds" when '/commits' extras.unshift "commits" + when '/builds' + extras.unshift "builds" end extras -- cgit v1.2.1 From b312edaf440b729dad02749259577f6e0589c061 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 23:29:49 +0200 Subject: More clean up to the CI example on how to use docker images [ci skip] --- doc/ci/docker/using_docker_images.md | 100 +++++++++++++---------------------- 1 file changed, 36 insertions(+), 64 deletions(-) diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index 488479418b1..8d4bd44053e 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -1,28 +1,29 @@ # Using Docker Images -GitLab CI can use [Docker Engine](https://www.docker.com/) to build projects. +GitLab CI in conjuction with [GitLab Runner](../runners/README.md) can use +[Docker Engine](https://www.docker.com/) to test and build any application. -Docker is an open-source project that allows to use predefined images to run -applications in independent "containers" that are run within a single Linux +Docker is an open-source project that allows you to use predefined images to +run applications in independent "containers" that are run within a single Linux instance. [Docker Hub][hub] has a rich database of prebuilt images that can be used to test and build your applications. Docker, when used with GitLab CI, runs each build in a separate and isolated -container using the predefined image that is set up in `.gitlab-ci.yml`. The -container is always euphemeral which means you get only one container per build. +container using the predefined image that is set up in +[`.gitlab-ci.yml`](../yaml/README.md). This makes it easier to have a simple and reproducible build environment that can also run on your workstation. The added benefit is that you can test all the commands that we will explore later from your shell, rather than having to -test them on a CI server. +test them on a dedicated CI server. -### Register docker runner +## Register docker runner To use GitLab Runner with docker you need to register a new runner to use the `docker` executor: ```bash -gitlab-ci-multi-runner register \ +gitlab-runner register \ --url "https://gitlab.com/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "docker-ruby-2.1" \ @@ -36,7 +37,7 @@ The registered runner will use the `ruby:2.1` docker image and will run two services, `postgres:latest` and `mysql:latest`, both of which will be accessible during the build process. -### What is image +## What is image The `image` keyword is the name of the docker image that is present in the local Docker Engine (list all images with `docker images`) or any image that @@ -46,9 +47,9 @@ Hub please read the [Docker Fundamentals][] documentation. In short, with `image` we refer to the docker image, which will be used to create a container on which your build will run. -### What is service +## What is service -The `service` keyword defines just another docker image that is run during +The `services` keyword defines just another docker image that is run during your build and is linked to the docker image that the `image` keyword defines. This allows you to access the service image during build time. @@ -57,9 +58,12 @@ run a database container, eg. `mysql`. It's easier and faster to use an existing image and run it as an additional container than install `mysql` every time the project is built. -#### How is service linked to the build +You can see some widely used services examples in the relevant documentation of +[CI services examples](../services/README.md). -To better undestand how the container linking works, read +### How is service linked to the build + +To better understand how the container linking works, read [Linking containers together](https://docs.docker.com/userguide/dockerlinks/). To summarize, if you add `mysql` as service to your application, the image will @@ -69,25 +73,27 @@ The service container for MySQL will be accessible under the hostname `mysql`. So, in order to access your database service you have to connect to the host named `mysql` instead of a socket or `localhost`. -### Overwrite image and services +## Overwrite image and services -See the section below. +See [How to use other images as services](#how-to-use-other-images-as-services). -### How to use other images as services +## How to use other images as services You are not limited to have only database services. You can add as many services you need to `.gitlab-ci.yml` or manually modify `config.toml`. Any image found at [Docker Hub][hub] can be used as a service. -### Define image and services from `.gitlab-ci.yml` +## Define image and services from `.gitlab-ci.yml` You can simply define an image that will be used for all jobs and a list of services that you want to use during build time. ```yaml image: ruby:2.2 + services: - postgres:9.3 + before_script: - bundle install @@ -117,24 +123,20 @@ test:2.2: - bundle exec rake spec ``` -### Define image and services in `config.toml` +## Define image and services in `config.toml` Look for the `[runners.docker]` section: ``` -... - [runners.docker] image = "ruby:2.1" services = ["mysql:latest", "postgres:latest"] - -... ``` The image and services defined this way will be added to all builds run by that runner. -### Accessing the services +## Accessing the services Let's say that you need a Wordpress instance to test some API integration with your application. @@ -143,12 +145,8 @@ You can then use for example the [tutum/wordpress][] image in your `.gitlab-ci.yml`: ```yaml -... - services: - tutum/wordpress:latest - -... ``` When the build is run, `tutum/wordpress` will be started and you will have @@ -160,7 +158,7 @@ rules: 1. Everything after `:` is stripped 2. Backslash (`/`) is replaced with double underscores (`__`) -### Configuring services +## Configuring services Many services accept environment variables which allow you to easily change database names or set account names depending on the environment. @@ -171,46 +169,20 @@ service containers. For all possible configuration variables check the documentation of each image provided in their corresponding Docker hub page. -*Note: All variables will be passed to all service containers. It's not designed - to distinguish which variable should go where.* - -#### PostgreSQL service example - -To configure the database name for [postgres][postgres-hub] service, you need -to set the `POSTGRES_DB` variable: - -```yaml -... - -services: -- postgres - -variables: - POSTGRES_DB: gitlab +*Note: All variables will be passed to all services containers. It's not +designed to distinguish which variable should go where.* -... -``` - -For a real example visit . +### PostgreSQL service example -#### MySQL service example +See the specific documentation for +[using PostgreSQL as a service](../services/postgres.md). -To use the [mysql][mysql-hub] service with an empty password during the time of -build, you need to set the `MYSQL_ALLOW_EMPTY_PASSWORD` variable: +### MySQL service example -```yaml -... - -services: -- mysql - -variables: - MYSQL_ALLOW_EMPTY_PASSWORD: "yes" - -... -``` +See the specific documentation for +[using MySQL as a service](../services/mysql.md). -### How Docker integration works +## How Docker integration works Below is a high level overview of the steps performed by docker during build time. @@ -226,7 +198,7 @@ time. 1. Check exit status of build script. 1. Remove build container and all created service containers. -### How to debug a build locally +## How to debug a build locally *Note: The following commands are run without root privileges. You should be able to run docker with your regular user account.* -- cgit v1.2.1 From 57d71520bdc2ba79ba8182802cd944d4fb42a192 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 22:30:40 +0100 Subject: Make tooltip less confusing --- app/controllers/projects/application_controller.rb | 2 +- app/services/files/base_service.rb | 2 +- app/views/projects/blob/_actions.html.haml | 6 +++--- app/views/projects/tree/_tree_header.html.haml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index d3f926b62bc..eea41dbeeb1 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -21,7 +21,7 @@ class Projects::ApplicationController < ApplicationController unless @repository.branch_names.include?(@ref) redirect_to( namespace_project_tree_path(@project.namespace, @project, @ref), - notice: "This action is not allowed unless you are on top of a branch" + notice: "This action is not allowed unless you are on a branch" ) end end diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 309abcd6d0b..9a67b160940 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -53,7 +53,7 @@ module Files unless project.empty_repo? unless repository.branch_names.include?(@current_branch) - raise_error("You can only create or edit files when you are on top of a branch") + raise_error("You can only create or edit files when you are on a branch") end if @current_branch != @target_branch diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 41d8765623e..b1df8d19938 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -21,6 +21,6 @@ %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete - elsif !on_top_of_branch? .btn-group{ role: "group" } - %button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on top of a branch.", data: {container: 'body'}} Edit - %button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on top of a branch.", data: {container: 'body'}} Replace - %button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on top of a branch.", data: {container: 'body'}} Delete + %button.btn.btn-default.disabled.has_tooltip{title: "You can only edit files when you are on a branch.", data: {container: 'body'}} Edit + %button.btn.btn-default.disabled.has_tooltip{title: "You can only replace files when you are on a branch.", data: {container: 'body'}} Replace + %button.btn.btn-remove.disabled.has_tooltip{title: "You can only delete files when you are on a branch.", data: {container: 'body'}} Delete diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 4767f824ac1..2baa5ecd974 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -32,5 +32,5 @@ New directory - elsif !on_top_of_branch? %li - %span.btn.add-to-tree.disabled.has_tooltip{href: '#', title: "You can only add files when you are on top of a branch.", data: {container: 'body'}} + %span.btn.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}} = icon('plus') -- cgit v1.2.1 From 0f89e6905db6fe821332013e8ddb2707079e0ba4 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 22:31:40 +0100 Subject: Fix spec --- features/project/source/browse_files.feature | 6 ------ features/steps/project/source/browse_files.rb | 4 ---- 2 files changed, 10 deletions(-) diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 37f99b37619..439787f2641 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -110,12 +110,6 @@ Feature: Project Source Browse Files Given I visit a binary file in the repo Then I cannot see the edit button - Scenario: If I don't have edit permission the edit link is disabled - Given public project "Community" - And I visit project "Community" source page - And I click on ".gitignore" file in repo - Then The edit button is disabled - @javascript Scenario: I can edit and commit file 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 2792174cc93..f2b95764267 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -53,10 +53,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).not_to have_link 'edit' end - step 'The edit button is disabled' do - expect(page).to have_css '.disabled', text: 'Edit' - end - step 'I can edit code' do set_new_content expect(evaluate_script('blob.editor.getValue()')).to eq new_gitignore_content -- cgit v1.2.1 From 1464a69c76cf492ad8b9674e24260e917dc7d2ef Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 22:37:07 +0100 Subject: Tweak text of documentation --- .gitignore | 1 - doc/workflow/merge_when_build_succeeds.md | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 881b3fb81ac..f5b6427ca03 100644 --- a/.gitignore +++ b/.gitignore @@ -38,7 +38,6 @@ public/assets/ public/uploads.* public/uploads/ shared/artifacts/ -TODO rails_best_practices_output.html /tags tmp/ diff --git a/doc/workflow/merge_when_build_succeeds.md b/doc/workflow/merge_when_build_succeeds.md index 3c055650c49..75e1fdff2b2 100644 --- a/doc/workflow/merge_when_build_succeeds.md +++ b/doc/workflow/merge_when_build_succeeds.md @@ -1,12 +1,12 @@ # Merge When Build Succeeds -When reviewing a merge request that looks ready to merge but still has one or more CI builds running, you can set it to be merged automatically when the build succeeds. This way, you don't have to wait for the build to finish and remember to merge the merge request then. +When reviewing a merge request that looks ready to merge but still has one or more CI builds running, you can set it to be merged automatically when all builds succeed. This way, you don't have to wait for the builds to finish and remember to merge the request manually. ![Enable](merge_when_build_succeeds/enable.png) -When you hit the "Merge When Build Succeeds" button, the status of the Merge Request will be updated to represent the impending merge. If you cannot wait for the build to succeed and want to build immediately, this option is available in the dropdown menu on the right of the main button. +When you hit the "Merge When Build Succeeds" button, the status of the merge request will be updated to represent the impending merge. If you cannot wait for the build to succeed and want to merge immediately, this option is available in the dropdown menu on the right of the main button. -Both team developers and the author of the merge request have the option to cancel the automatic merge when they find a reason it shouldn't be merged after all. +Both team developers and the author of the merge request have the option to cancel the automatic merge if they find a reason why it shouldn't be merged after all. ![Status](merge_when_build_succeeds/status.png) -- cgit v1.2.1 From 31b4960eca490994bd192f53c4b3f5283fe962d5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 22:39:26 +0100 Subject: Fix specs --- app/views/projects/issues/_issue.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 7d9fc8d2c86..f9cf4910df3 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -25,7 +25,7 @@ %li = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do = icon('comments') - = notes_count + = note_count .issue-info #{issue.to_reference} · -- cgit v1.2.1 From 1e7156ed701945349bef484d35c7f53f2ee4474b Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 8 Dec 2015 23:49:05 +0200 Subject: Use the name of the linked containers instead of localhost https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/1917#note_2833978 --- doc/ci/services/mysql.md | 5 ++++- doc/ci/services/postgres.md | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index 44bc613e6f7..c66d77122b2 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -24,12 +24,15 @@ variables: And then configure your application to use the database, for example: ```yaml -Host: localhost +Host: mysql User: root Password: mysql_strong_password Database: el_duderino ``` +If you are wondering why we used `mysql` for the `Host`, read more at +[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build). + You can also use any other docker image available on [Docker Hub][hub-mysql]. For example, to use MySQL 5.5 the service becomes `mysql:5.5`. diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index db5be070ee7..17d21dbda1c 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -24,12 +24,15 @@ variables: And then configure your application to use the database, for example: ```yaml -Host: localhost +Host: postgres User: runner Password: Database: nice_marmot ``` +If you are wondering why we used `postgres` for the `Host`, read more at +[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build). + You can also use any other docker image available on [Docker Hub][hub-pg]. For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`. -- cgit v1.2.1 From 53fbb0ecda36be783a46413f59551f13090ea9df Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Dec 2015 16:53:48 -0500 Subject: Improve styling for mixed list styles Tasks in an ordered list will now also show their numbers. Closes #2488 Related to #3921 --- app/assets/stylesheets/framework/lists.scss | 10 ++++++++-- app/assets/stylesheets/pages/notes.scss | 10 +++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 927641216e4..f770e535b30 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -88,8 +88,14 @@ ul.bordered-list { } } -li.task-list-item { - list-style-type: none; +ul.task-list { + li.task-list-item { + list-style-type: none; + } + + ul:not(.task-list) { + padding-left: 1.3em; + } } ul.content-list { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 1980fe0d458..4dff87abaa4 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -109,13 +109,9 @@ ul.notes { } } - // Reduce left padding of first task list ul element - ul.task-list:first-child { - padding-left: 10px; - - // sub-tasks should be padded normally - ul { - padding-left: 20px; + ul.task-list { + ul:not(.task-list) { + padding-left: 1.3em; } } -- cgit v1.2.1 From 23522a9b34c30d61e950820a197b6bd353ba1914 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 8 Dec 2015 23:28:28 +0100 Subject: Fix specs --- spec/features/issues_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 0af5e6fc1a6..3d6d87e764a 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -293,10 +293,10 @@ describe 'Issues', feature: true do end def first_issue - page.all('ul.issues-list li').first.text + page.all('ul.issues-list > li').first.text end def last_issue - page.all('ul.issues-list li').last.text + page.all('ul.issues-list > li').last.text end end -- cgit v1.2.1 From 56f25c3e615752817446d5aedb7d66d25d815641 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Mon, 7 Dec 2015 13:37:01 +0100 Subject: Improve text indication visibility on snippets --- CHANGELOG | 1 + app/helpers/visibility_level_helper.rb | 26 ++++--- .../admin/application_settings/_form.html.haml | 4 +- app/views/shared/snippets/_header.html.haml | 2 +- spec/helpers/visibility_level_helper_spec.rb | 85 +++++++++------------- 5 files changed, 53 insertions(+), 65 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 67f70e676c2..c291d4b6dec 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,7 @@ v 8.3.0 (unreleased) - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch - Add languages page to graphs - Block LDAP user when they are no longer found in the LDAP server + - Improve wording on project visibility levels (Zeger-Jan van de Weg) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb index 72c65030f94..2e69ce923a2 100644 --- a/app/helpers/visibility_level_helper.rb +++ b/app/helpers/visibility_level_helper.rb @@ -12,22 +12,22 @@ module VisibilityLevelHelper # Return the description for the +level+ argument. # - # +level+ One of the Gitlab::VisibilityLevel constants - # +form_model+ Either a model object (Project, Snippet, etc.) or the name of - # a Project or Snippet class. + # +level+ One of the Gitlab::VisibilityLevel constants + # +form_model+ Either a model object (Project, Snippet, etc.) or the name of + # a Project or Snippet class. def visibility_level_description(level, form_model) - case form_model.is_a?(String) ? form_model : form_model.class.name - when 'PersonalSnippet', 'ProjectSnippet', 'Snippet' - snippet_visibility_level_description(level) - when 'Project' + case form_model + when Project project_visibility_level_description(level) + when Snippet + snippet_visibility_level_description(level, form_model) end end def project_visibility_level_description(level) case level when Gitlab::VisibilityLevel::PRIVATE - "Project access must be granted explicitly for each user." + "Project access must be granted explicitly to each user." when Gitlab::VisibilityLevel::INTERNAL "The project can be cloned by any logged in user." when Gitlab::VisibilityLevel::PUBLIC @@ -35,12 +35,16 @@ module VisibilityLevelHelper end end - def snippet_visibility_level_description(level) + def snippet_visibility_level_description(level, snippet = nil) case level when Gitlab::VisibilityLevel::PRIVATE - "The snippet is visible only for me." + if snippet.is_a? ProjectSnippet + "The snippet is visible only to project members." + else + "The snippet is visible only to me." + end when Gitlab::VisibilityLevel::INTERNAL - "The snippet is visible for any logged in user." + "The snippet is visible to any logged in user." when Gitlab::VisibilityLevel::PUBLIC "The snippet can be accessed without any authentication." end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index ddaf0e0e8ff..6c355366948 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -14,11 +14,11 @@ .form-group.project-visibility-level-holder = f.label :default_project_visibility, class: 'control-label col-sm-2' .col-sm-10 - = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: 'Project') + = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project) .form-group.project-visibility-level-holder = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 - = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: 'Snippet') + = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: PersonalSnippet) .form-group = f.label :restricted_visibility_levels, class: 'control-label col-sm-2' .col-sm-10 diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index 89c1d7122b0..eb0fd21c2d4 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,6 +1,6 @@ .issuable-details .page-title - .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }} + .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }} = visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_label(@snippet.visibility_level) Snippet ##{@snippet.id} diff --git a/spec/helpers/visibility_level_helper_spec.rb b/spec/helpers/visibility_level_helper_spec.rb index c4f7693329c..aafc24397a9 100644 --- a/spec/helpers/visibility_level_helper_spec.rb +++ b/spec/helpers/visibility_level_helper_spec.rb @@ -7,69 +7,52 @@ describe VisibilityLevelHelper do init_haml_helpers end - let(:project) { create(:project) } + let(:project) { build(:project) } + let(:personal_snippet) { build(:personal_snippet) } + let(:project_snippet) { build(:project_snippet) } describe 'visibility_level_description' do - shared_examples 'a visibility level description' do - let(:desc) do - visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, - form_model) - end - - let(:expected_class) do - class_name = case form_model.class.name - when 'String' - form_model - else - form_model.class.name - end - - class_name.match(/(project|snippet)$/i)[0] - end - - it 'should refer to the correct class' do - expect(desc).to match(/#{expected_class}/i) + context 'used with a Project' do + it 'delegates projects to #project_visibility_level_description' do + expect(visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, project)) + .to match /project/i end end - context 'form_model argument is a String' do - context 'model object is a personal snippet' do - it_behaves_like 'a visibility level description' do - let(:form_model) { 'PersonalSnippet' } - end + context 'called with a Snippet' do + it 'delegates snippets to #snippet_visibility_level_description' do + expect(visibility_level_description(Gitlab::VisibilityLevel::INTERNAL, project_snippet)) + .to match /snippet/i end + end + end - context 'model object is a project snippet' do - it_behaves_like 'a visibility level description' do - let(:form_model) { 'ProjectSnippet' } - end - end + describe "#project_visibility_level_description" do + it "describes private projects" do + expect(project_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE)) + .to eq "Project access must be granted explicitly to each user." + end - context 'model object is a project' do - it_behaves_like 'a visibility level description' do - let(:form_model) { 'Project' } - end - end + it "describes public projects" do + expect(project_visibility_level_description(Gitlab::VisibilityLevel::PUBLIC)) + .to eq "The project can be cloned without any authentication." end + end - context 'form_model argument is a model object' do - context 'model object is a personal snippet' do - it_behaves_like 'a visibility level description' do - let(:form_model) { create(:personal_snippet) } - end - end + describe "#snippet_visibility_level_description" do + it 'describes visibility only for me' do + expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, personal_snippet)) + .to eq "The snippet is visible only to me." + end - context 'model object is a project snippet' do - it_behaves_like 'a visibility level description' do - let(:form_model) { create(:project_snippet, project: project) } - end - end + it 'describes visibility for project members' do + expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE, project_snippet)) + .to eq "The snippet is visible only to project members." + end - context 'model object is a project' do - it_behaves_like 'a visibility level description' do - let(:form_model) { project } - end - end + it 'defaults to personal snippet' do + expect(snippet_visibility_level_description(Gitlab::VisibilityLevel::PRIVATE)) + .to eq "The snippet is visible only to me." end end -- cgit v1.2.1 From 430aa23a5b82b34912dc86c323087fabd2849b7c Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Wed, 9 Dec 2015 00:20:51 +0100 Subject: Add spinach tests for the clone protocol selection. --- features/explore/projects.feature | 21 +++++++++++++++++++-- features/steps/explore/projects.rb | 8 ++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/features/explore/projects.feature b/features/explore/projects.feature index 5d3870827f5..63b0ac65c4b 100644 --- a/features/explore/projects.feature +++ b/features/explore/projects.feature @@ -31,8 +31,17 @@ Feature: Explore Projects Then I should see empty public project details And I should see empty public project details with http clone info - Scenario: I visit an empty public project page as user + Scenario: I visit an empty public project page as user with no ssh-keys Given I sign in as a user + And I have no ssh keys + And public empty project "Empty Public Project" + When I visit empty project page + Then I should see empty public project details + And I should see empty public project details with http clone info + + Scenario: I visit an empty public project page as user with an ssh-key + Given I sign in as a user + And I have ssh key "ssh-rsa Work" And public empty project "Empty Public Project" When I visit empty project page Then I should see empty public project details @@ -57,8 +66,16 @@ Feature: Explore Projects Then I should see project "Community" home page And I should see an http link to the repository - Scenario: I visit public project page as user + Scenario: I visit public project page as user with no ssh-keys + Given I sign in as a user + And I have no ssh keys + When I visit project "Community" page + Then I should see project "Community" home page + And I should see an http link to the repository + + Scenario: I visit public project page as user with an ssh-key Given I sign in as a user + And I have ssh key "ssh-rsa Work" When I visit project "Community" page Then I should see project "Community" home page And I should see an ssh link to the repository diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb index 8b498e7b4a6..82cb9e24062 100644 --- a/features/steps/explore/projects.rb +++ b/features/steps/explore/projects.rb @@ -144,4 +144,12 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps def public_merge_request @public_merge_request ||= MergeRequest.find_by!(title: 'Bug fix for public project') end + + step 'I have ssh key "ssh-rsa Work"' do + create(:key, user: @user, title: "ssh-rsa Work", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") + end + + step 'I have no ssh keys' do + Key.delete_all + end end -- cgit v1.2.1 From 9dd26749634648e2cd27b223eea5d291c0a78f37 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Dec 2015 01:43:57 +0100 Subject: Fix only 20 group members showing on project member page Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/project_members_controller.rb | 2 +- app/views/projects/project_members/_group_members.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 07eb94e4f48..8364fc293b7 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -23,7 +23,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController @group_members = @group_members.where(user_id: users) end - @group_members = @group_members.order('access_level DESC').limit(20) + @group_members = @group_members.order('access_level DESC') end @project_member = @project.project_members.new diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml index d2810f9707a..1c2458fa144 100644 --- a/app/views/projects/project_members/_group_members.html.haml +++ b/app/views/projects/project_members/_group_members.html.haml @@ -10,7 +10,7 @@ = icon('pencil-square-o') Manage group members %ul.content-list - - members.each do |member| + - members.limit(20).each do |member| = render 'groups/group_members/group_member', member: member, show_controls: false - if members.count > 20 %li -- cgit v1.2.1 From 94dc9ef9e1a85b8a4506358479a549dc3a1306b6 Mon Sep 17 00:00:00 2001 From: Eirik Lygre Date: Wed, 9 Dec 2015 02:02:53 +0100 Subject: Move ssh-key steps into SharedUser. Change references in "Explore Projects" feature. Apply ssh-key requirement to "Project Create" feature. --- features/explore/projects.feature | 4 ++-- features/project/create.feature | 2 ++ features/steps/explore/projects.rb | 9 +-------- features/steps/project/create.rb | 1 + features/steps/shared/user.rb | 8 ++++++++ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/features/explore/projects.feature b/features/explore/projects.feature index 63b0ac65c4b..629859e960d 100644 --- a/features/explore/projects.feature +++ b/features/explore/projects.feature @@ -41,7 +41,7 @@ Feature: Explore Projects Scenario: I visit an empty public project page as user with an ssh-key Given I sign in as a user - And I have ssh key "ssh-rsa Work" + And I have an ssh key And public empty project "Empty Public Project" When I visit empty project page Then I should see empty public project details @@ -75,7 +75,7 @@ Feature: Explore Projects Scenario: I visit public project page as user with an ssh-key Given I sign in as a user - And I have ssh key "ssh-rsa Work" + And I have an ssh key When I visit project "Community" page Then I should see project "Community" home page And I should see an ssh link to the repository diff --git a/features/project/create.feature b/features/project/create.feature index e9dc4fe6b3c..a86079143e5 100644 --- a/features/project/create.feature +++ b/features/project/create.feature @@ -7,6 +7,7 @@ Feature: Project Create Scenario: User create a project Given I sign in as a user When I visit new project page + And I have an ssh key And fill project form with valid data Then I should see project page And I should see empty project instuctions @@ -14,6 +15,7 @@ Feature: Project Create @javascript Scenario: Empty project instructions Given I sign in as a user + And I have an ssh key When I visit new project page And fill project form with valid data Then I see empty project instuctions diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb index 82cb9e24062..f819dec2192 100644 --- a/features/steps/explore/projects.rb +++ b/features/steps/explore/projects.rb @@ -2,6 +2,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps include SharedAuthentication include SharedPaths include SharedProject + include SharedUser step 'I should see project "Empty Public Project"' do expect(page).to have_content "Empty Public Project" @@ -144,12 +145,4 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps def public_merge_request @public_merge_request ||= MergeRequest.find_by!(title: 'Bug fix for public project') end - - step 'I have ssh key "ssh-rsa Work"' do - create(:key, user: @user, title: "ssh-rsa Work", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") - end - - step 'I have no ssh keys' do - Key.delete_all - end end diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb index 0d39e1997b5..f90218f3791 100644 --- a/features/steps/project/create.rb +++ b/features/steps/project/create.rb @@ -1,6 +1,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps include SharedAuthentication include SharedPaths + include SharedUser step 'fill project form with valid data' do fill_in 'project_path', with: 'Empty' diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb index 250cc5b94f3..33c146f6dbc 100644 --- a/features/steps/shared/user.rb +++ b/features/steps/shared/user.rb @@ -18,4 +18,12 @@ module SharedUser def user_exists(name, options = {}) User.find_by(name: name) || create(:user, { name: name, admin: false }.merge(options)) end + + step 'I have an ssh key' do + create(:key, user: @user, title: "An ssh-key", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") + end + + step 'I have no ssh keys' do + Key.delete_all + end end -- cgit v1.2.1 From 1d250b48922ef88b14ad8ce2af157e2f2d256773 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Dec 2015 02:50:46 +0100 Subject: Move Network page from separate tab to sub tab of Commits Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + app/views/layouts/nav/_project.html.haml | 16 ++++++++-------- app/views/projects/commits/_head.html.haml | 6 ++++++ app/views/projects/network/_head.html.haml | 2 +- app/views/projects/network/show.html.haml | 3 ++- features/project/active_tab.feature | 11 ++++++----- features/steps/project/active_tab.rb | 4 ++++ features/steps/shared/project_tab.rb | 4 ---- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c291d4b6dec..747afc82aa0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,6 +25,7 @@ v 8.3.0 (unreleased) - Add languages page to graphs - Block LDAP user when they are no longer found in the LDAP server - Improve wording on project visibility levels (Zeger-Jan van de Weg) + - Make Network page as sub tab of Commits v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 87a7707b095..2fcba7bd672 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 releases)) do + = nav_link(controller: %w(commit commits compare repositories tags branches releases network)) do = link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do = icon('history fw') %span @@ -46,13 +46,6 @@ Builds %span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all) - - if project_nav_tab? :network - = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do - = icon('code-fork fw') - %span - Network - - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do = link_to namespace_project_graph_path(@project.namespace, @project, current_ref), title: 'Graphs', class: 'shortcuts-graphs' do @@ -118,3 +111,10 @@ = icon('cogs fw') %span Settings + + -# Global shortcut to network page for compatibility + - if project_nav_tab? :network + %li.hidden + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do + Network + diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index f11a41cfd7b..2f604c82f11 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -3,6 +3,12 @@ = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do Commits %span.badge= number_with_delimiter(@repository.commit_count) + + - if project_nav_tab? :network + = nav_link(controller: %w(network)) do + = link_to namespace_project_network_path(@project.namespace, @project, current_ref), class: 'shortcuts-network' do + Network + = nav_link(controller: :compare) do = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do Compare diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml index 9e0e0dc6bb0..28a617538b5 100644 --- a/app/views/projects/network/_head.html.haml +++ b/app/views/projects/network/_head.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.top-block.append-bottom-default +.gray-content-block.append-bottom-default .tree-ref-holder = render partial: 'shared/ref_switcher', locals: {destination: 'graph'} diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 16005161df6..8065663ca2a 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,5 +1,6 @@ - page_title "Network", @ref -= header_title project_title(@project, "Network", namespace_project_network_path(@project.namespace, @project, current_ref)) += render "projects/commits/header_title" += render "projects/commits/head" = render "head" .project-network .controls diff --git a/features/project/active_tab.feature b/features/project/active_tab.feature index 8661ea98c20..2fd097d100b 100644 --- a/features/project/active_tab.feature +++ b/features/project/active_tab.feature @@ -20,11 +20,6 @@ Feature: Project Active Tab Then the active main tab should be Commits And no other main tabs should be active - Scenario: On Project Network - Given I visit my project's network page - Then the active main tab should be Network - And no other main tabs should be active - Scenario: On Project Issues Given I visit my project's issues page Then the active main tab should be Issues @@ -83,6 +78,12 @@ Feature: Project Active Tab And no other sub tabs should be active And the active main tab should be Commits + Scenario: On Project Commits/Network + Given I visit my project's network page + Then the active sub tab should be Network + And no other sub tabs should be active + And the active main tab should be Commits + Scenario: On Project Commits/Compare Given I visit my project's commits page And I click the "Compare" tab diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb index 9e96fa5ba49..94a5dd744d0 100644 --- a/features/steps/project/active_tab.rb +++ b/features/steps/project/active_tab.rb @@ -67,6 +67,10 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ensure_active_sub_tab('Commits') end + step 'the active sub tab should be Network' do + ensure_active_sub_tab('Network') + end + step 'the active sub tab should be Compare' do ensure_active_sub_tab('Compare') end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb index 33ff7084e30..3bad28dc283 100644 --- a/features/steps/shared/project_tab.rb +++ b/features/steps/shared/project_tab.rb @@ -16,10 +16,6 @@ module SharedProjectTab ensure_active_main_tab('Commits') end - step 'the active main tab should be Network' do - ensure_active_main_tab('Network') - end - step 'the active main tab should be Graphs' do ensure_active_main_tab('Graphs') end -- cgit v1.2.1 From 9dbc768db8304004c08957710d423c0b2b54510a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 21:00:01 -0800 Subject: Update annotations --- app/models/lfs_object.rb | 12 ++++++++++++ app/models/lfs_objects_project.rb | 11 +++++++++++ app/models/note.rb | 1 + app/models/project.rb | 1 + app/models/user.rb | 1 + spec/factories/lfs_objects.rb | 12 ++++++++++++ spec/factories/lfs_objects_projects.rb | 11 +++++++++++ spec/factories/notes.rb | 1 + spec/factories/projects.rb | 1 + spec/models/note_spec.rb | 1 + spec/models/project_spec.rb | 1 + spec/models/user_spec.rb | 1 + 12 files changed, 54 insertions(+) diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 18657c3e1c8..86b1b7e2f99 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: lfs_objects +# +# id :integer not null, primary key +# oid :string(255) not null +# size :integer not null +# created_at :datetime +# updated_at :datetime +# file :string(255) +# + class LfsObject < ActiveRecord::Base has_many :lfs_objects_projects, dependent: :destroy has_many :projects, through: :lfs_objects_projects diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb index 0fd5f089db9..890736bfc80 100644 --- a/app/models/lfs_objects_project.rb +++ b/app/models/lfs_objects_project.rb @@ -1,3 +1,14 @@ +# == Schema Information +# +# Table name: lfs_objects_projects +# +# id :integer not null, primary key +# lfs_object_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + class LfsObjectsProject < ActiveRecord::Base belongs_to :project belongs_to :lfs_object diff --git a/app/models/note.rb b/app/models/note.rb index 8d433c57ceb..98c29ddc4cd 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -16,6 +16,7 @@ # system :boolean default(FALSE), not null # st_diff :text # updated_by_id :integer +# is_award :boolean default(FALSE), not null # require 'carrierwave/orm/activerecord' diff --git a/app/models/project.rb b/app/models/project.rb index af034a6692b..cb965ce1b9e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -28,6 +28,7 @@ # import_type :string(255) # import_source :string(255) # commit_count :integer default(0) +# import_error :text # require 'carrierwave/orm/activerecord' diff --git a/app/models/user.rb b/app/models/user.rb index cfed797e725..7155dd2bea7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -56,6 +56,7 @@ # project_view :integer default(0) # consumed_timestep :integer # layout :integer default(0) +# hide_project_limit :boolean default(FALSE) # require 'carrierwave/orm/activerecord' diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index 7fb2d77ca32..2da107ba24b 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -1,3 +1,15 @@ +# == Schema Information +# +# Table name: lfs_objects +# +# id :integer not null, primary key +# oid :string(255) not null +# size :integer not null +# created_at :datetime +# updated_at :datetime +# file :string(255) +# + # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb index 93de6607df8..3772236a77a 100644 --- a/spec/factories/lfs_objects_projects.rb +++ b/spec/factories/lfs_objects_projects.rb @@ -1,3 +1,14 @@ +# == Schema Information +# +# Table name: lfs_objects_projects +# +# id :integer not null, primary key +# lfs_object_id :integer not null +# project_id :integer not null +# created_at :datetime +# updated_at :datetime +# + # Read about factories at https://github.com/thoughtbot/factory_girl FactoryGirl.define do diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 9d777ddfccd..35a20adeef3 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -16,6 +16,7 @@ # system :boolean default(FALSE), not null # st_diff :text # updated_by_id :integer +# is_award :boolean default(FALSE), not null # require_relative '../support/repo_helpers' diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 1d500a11ad7..112213377ff 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -28,6 +28,7 @@ # import_type :string(255) # import_source :string(255) # commit_count :integer default(0) +# import_error :text # FactoryGirl.define do diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index f347f537550..e7e8887baf2 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -16,6 +16,7 @@ # system :boolean default(FALSE), not null # st_diff :text # updated_by_id :integer +# is_award :boolean default(FALSE), not null # require 'spec_helper' diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 06a02c13bf1..dc703d8095c 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -28,6 +28,7 @@ # import_type :string(255) # import_source :string(255) # commit_count :integer default(0) +# import_error :text # require 'spec_helper' diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a0f78d3b336..1aad37fa02e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -56,6 +56,7 @@ # project_view :integer default(0) # consumed_timestep :integer # layout :integer default(0) +# hide_project_limit :boolean default(FALSE) # require 'spec_helper' -- cgit v1.2.1 From 7b0ac5b6b49675790491dcf8764ebfb17b93012c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 21:31:22 -0800 Subject: Remove default_branch from project API creation since an empty repository has no branches to start. Closes #3937 --- doc/api/projects.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 42919a312ae..43a50a9a810 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -398,7 +398,6 @@ Parameters: - `user_id` (required) - user_id of owner - `name` (required) - new project name - `description` (optional) - short project description -- `default_branch` (optional) - 'master' by default - `issues_enabled` (optional) - `merge_requests_enabled` (optional) - `builds_enabled` (optional) -- cgit v1.2.1 From dd980b46f1833ba72207846c66995e517b63b50d Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 09:08:53 +0100 Subject: Use up and down --- db/migrate/20140122112253_create_merge_request_diffs.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/db/migrate/20140122112253_create_merge_request_diffs.rb b/db/migrate/20140122112253_create_merge_request_diffs.rb index 47bee269f9c..f34e30925df 100644 --- a/db/migrate/20140122112253_create_merge_request_diffs.rb +++ b/db/migrate/20140122112253_create_merge_request_diffs.rb @@ -1,5 +1,5 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration - def change + def up create_table :merge_request_diffs do |t| t.string :state, null: false, default: 'collected' t.text :st_commits, null: true @@ -14,4 +14,8 @@ class CreateMergeRequestDiffs < ActiveRecord::Migration change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647 end end + + def down + drop_table :merge_request_diffs + end end -- cgit v1.2.1 From 28351806aa1f89f584d6905365fd34f5f0e8bbc7 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 09:59:19 +0100 Subject: Give merge request widget the vars it desires --- app/controllers/projects/merge_requests_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 3240c77e994..530f3d3dcb8 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -7,6 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :closes_issues, only: [:edit, :update, :show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] before_action :define_show_vars, only: [:show, :diffs, :commits, :builds] + before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds] before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds] # Allow read any merge_request @@ -301,6 +302,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end + def define_widget_vars + @ci_commit = @merge_request.ci_commit + end + def invalid_mr # Render special view for MR with removed source or target branch render 'invalid' -- cgit v1.2.1 From 903151141da36ed9b8c2333a9b66f5c611e4efb6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 09:59:39 +0100 Subject: Tweak specs --- spec/features/merge_requests/merge_when_build_succeeds_spec.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index ee2fb25e9e5..a674124aab7 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -43,20 +43,14 @@ feature 'Merge When Build Succeeds', feature: true, js: true do context 'When it is enabled' do let(:merge_request) do - create(:merge_request_with_diffs, source_project: project, author: user, - merge_user: user, title: "MepMep", merge_when_build_succeeds: true) + create(:merge_request_with_diffs, :simple, source_project: project, author: user, + merge_user: user, title: "MepMep", merge_when_build_succeeds: true) end let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } let!(:ci_build) { create(:ci_build, commit: ci_commit) } before do - merge_request.source_project.team << [user, :master] - merge_request.source_branch = "feature" - merge_request.target_branch = "master" - merge_request.save! - - login_as user visit_merge_request(merge_request) end -- cgit v1.2.1 From 20b55981bc09c82dcbc523123cbd81a5bf2ae593 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 10:50:28 +0100 Subject: Tag feature specs --- features/project/commits/branches.feature | 1 + features/project/commits/comments.feature | 1 + features/project/commits/commits.feature | 1 + features/project/commits/diff_comments.feature | 1 + features/project/commits/tags.feature | 1 + features/project/commits/user_lookup.feature | 1 + features/project/issues/award_emoji.feature | 1 + features/project/issues/filter_labels.feature | 1 + features/project/issues/issues.feature | 2 +- features/project/issues/labels.feature | 1 + features/project/issues/milestones.feature | 1 + features/project/merge_requests.feature | 1 + features/project/merge_requests/accept.feature | 1 + 13 files changed, 13 insertions(+), 1 deletion(-) diff --git a/features/project/commits/branches.feature b/features/project/commits/branches.feature index 65d8e48b9b3..5103ca12947 100644 --- a/features/project/commits/branches.feature +++ b/features/project/commits/branches.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits Branches Background: Given I sign in as a user diff --git a/features/project/commits/comments.feature b/features/project/commits/comments.feature index 320f008abb6..fafb54b183a 100644 --- a/features/project/commits/comments.feature +++ b/features/project/commits/comments.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits Comments Background: Given I sign in as a user diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature index e4beeb59adc..21367fd76a0 100644 --- a/features/project/commits/commits.feature +++ b/features/project/commits/commits.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits Background: Given I sign in as a user diff --git a/features/project/commits/diff_comments.feature b/features/project/commits/diff_comments.feature index d6e0c84537e..2bde4c8a99b 100644 --- a/features/project/commits/diff_comments.feature +++ b/features/project/commits/diff_comments.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits Diff Comments Background: Given I sign in as a user diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature index 56ee091acc0..a4be39b2d40 100644 --- a/features/project/commits/tags.feature +++ b/features/project/commits/tags.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits Tags Background: Given I sign in as a user diff --git a/features/project/commits/user_lookup.feature b/features/project/commits/user_lookup.feature index db51d4a6cfa..c18f4e070f3 100644 --- a/features/project/commits/user_lookup.feature +++ b/features/project/commits/user_lookup.feature @@ -1,3 +1,4 @@ +@project_commits Feature: Project Commits User Lookup Background: Given I sign in as a user diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature index 2609f129d07..0ce99e855c6 100644 --- a/features/project/issues/award_emoji.feature +++ b/features/project/issues/award_emoji.feature @@ -1,3 +1,4 @@ +@project_issues Feature: Award Emoji Background: Given I sign in as a user diff --git a/features/project/issues/filter_labels.feature b/features/project/issues/filter_labels.feature index e316f519861..e07f8053fb7 100644 --- a/features/project/issues/filter_labels.feature +++ b/features/project/issues/filter_labels.feature @@ -1,3 +1,4 @@ +@project_issues Feature: Project Issues Filter Labels Background: Given I sign in as a user diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index 28cc43ef710..f08b30e0b88 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -1,3 +1,4 @@ +@project_issues Feature: Project Issues Background: Given I sign in as a user @@ -196,4 +197,3 @@ Feature: Project Issues And I should not see labels field And I submit new issue "500 error on profile" Then I should see issue "500 error on profile" - diff --git a/features/project/issues/labels.feature b/features/project/issues/labels.feature index 039a7d83cb1..45de57f18e3 100644 --- a/features/project/issues/labels.feature +++ b/features/project/issues/labels.feature @@ -1,3 +1,4 @@ +@project_issues Feature: Project Issues Labels Background: Given I sign in as a user diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature index c1a20e9b488..1af05b3c326 100644 --- a/features/project/issues/milestones.feature +++ b/features/project/issues/milestones.feature @@ -1,3 +1,4 @@ +@project_issues Feature: Project Issues Milestones Background: Given I sign in as a user diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 6cd081c868e..35ace948888 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -1,3 +1,4 @@ +@project_merge_requests Feature: Project Merge Requests Background: Given I sign in as a user diff --git a/features/project/merge_requests/accept.feature b/features/project/merge_requests/accept.feature index 9bc2b7c8eca..d5e4f2b0bd8 100644 --- a/features/project/merge_requests/accept.feature +++ b/features/project/merge_requests/accept.feature @@ -1,3 +1,4 @@ +@project_merge_requests Feature: Project Merge Requests Acceptance Background: Given There is an open Merge Request -- cgit v1.2.1 From 9ebdee09673d79b57c07c7332ee87791229b6f72 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 10:50:38 +0100 Subject: Split up feature specs more --- .gitlab-ci.yml | 11 +++++++++-- lib/tasks/spinach.rake | 26 +++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8290fb36b2..527ccb08ab7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,9 +39,16 @@ spec:other: - ruby - mysql -spinach:project: +spinach:project:half: script: - - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:half + tags: + - ruby + - mysql + +spinach:project:rest: + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spinach:project:rest tags: - ruby - mysql diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake index d5a96fd38f4..3acfc6e2075 100644 --- a/lib/tasks/spinach.rake +++ b/lib/tasks/spinach.rake @@ -1,11 +1,31 @@ Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach') namespace :spinach do + namespace :project do + desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features" + task :half do + cmds = [ + %W(rake gitlab:setup), + %W(spinach --tags @project_commits,@project_issues,@project_merge_requests), + ] + run_commands(cmds) + end + + desc "GitLab | Spinach | Run remaining project spinach features" + task :rest do + cmds = [ + %W(rake gitlab:setup), + %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests), + ] + run_commands(cmds) + end + end + desc "GitLab | Spinach | Run project spinach features" task :project do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits), + %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets), ] run_commands(cmds) end @@ -14,7 +34,7 @@ namespace :spinach do task :other do cmds = [ %W(rake gitlab:setup), - %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits), + %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets), ] run_commands(cmds) end @@ -33,4 +53,4 @@ def run_commands(cmds) cmds.each do |cmd| system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!") end -end \ No newline at end of file +end -- cgit v1.2.1 From 1e8d703a854b372b12a2ad41b5e54d2abc028f74 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 10:50:51 +0100 Subject: Tag model specs --- spec/models/broadcast_message_spec.rb | 2 +- spec/models/build_spec.rb | 2 +- spec/models/ci/commit_spec.rb | 2 +- spec/models/ci/project_services/hip_chat_message_spec.rb | 2 +- spec/models/ci/project_services/hip_chat_service_spec.rb | 2 +- spec/models/ci/project_services/mail_service_spec.rb | 2 +- spec/models/ci/project_services/slack_message_spec.rb | 2 +- spec/models/ci/project_services/slack_service_spec.rb | 2 +- spec/models/ci/project_spec.rb | 2 +- spec/models/ci/runner_project_spec.rb | 2 +- spec/models/ci/runner_spec.rb | 2 +- spec/models/ci/service_spec.rb | 2 +- spec/models/ci/trigger_spec.rb | 2 +- spec/models/ci/variable_spec.rb | 2 +- spec/models/ci/web_hook_spec.rb | 2 +- spec/models/commit_range_spec.rb | 2 +- spec/models/commit_spec.rb | 2 +- spec/models/commit_status_spec.rb | 2 +- spec/models/concerns/case_sensitivity_spec.rb | 2 +- spec/models/deploy_key_spec.rb | 2 +- spec/models/deploy_keys_project_spec.rb | 2 +- spec/models/event_spec.rb | 2 +- spec/models/external_issue_spec.rb | 2 +- spec/models/external_wiki_service_spec.rb | 2 +- spec/models/generic_commit_status_spec.rb | 2 +- spec/models/global_milestone_spec.rb | 2 +- spec/models/group_spec.rb | 2 +- spec/models/hooks/project_hook_spec.rb | 2 +- spec/models/hooks/service_hook_spec.rb | 2 +- spec/models/hooks/system_hook_spec.rb | 2 +- spec/models/hooks/web_hook_spec.rb | 2 +- spec/models/issue_spec.rb | 2 +- spec/models/key_spec.rb | 2 +- spec/models/label_link_spec.rb | 2 +- spec/models/label_spec.rb | 2 +- spec/models/member_spec.rb | 2 +- spec/models/members/group_member_spec.rb | 2 +- spec/models/members/project_member_spec.rb | 2 +- spec/models/merge_request_spec.rb | 2 +- spec/models/milestone_spec.rb | 2 +- spec/models/namespace_spec.rb | 2 +- spec/models/note_spec.rb | 2 +- spec/models/project_security_spec.rb | 2 +- spec/models/project_services/buildkite_service_spec.rb | 2 +- spec/models/project_services/drone_ci_service_spec.rb | 2 +- spec/models/project_services/flowdock_service_spec.rb | 2 +- spec/models/project_services/gemnasium_service_spec.rb | 2 +- spec/models/project_services/gitlab_ci_service_spec.rb | 2 +- spec/models/project_services/gitlab_issue_tracker_service_spec.rb | 2 +- spec/models/project_services/hipchat_service_spec.rb | 2 +- spec/models/project_services/irker_service_spec.rb | 2 +- spec/models/project_services/jira_service_spec.rb | 2 +- spec/models/project_services/pushover_service_spec.rb | 2 +- spec/models/project_services/slack_service/issue_message_spec.rb | 2 +- spec/models/project_services/slack_service/merge_message_spec.rb | 2 +- spec/models/project_services/slack_service/note_message_spec.rb | 2 +- spec/models/project_services/slack_service/push_message_spec.rb | 2 +- spec/models/project_services/slack_service_spec.rb | 2 +- spec/models/project_snippet_spec.rb | 2 +- spec/models/project_spec.rb | 2 +- spec/models/project_team_spec.rb | 2 +- spec/models/project_wiki_spec.rb | 2 +- spec/models/protected_branch_spec.rb | 2 +- spec/models/repository_spec.rb | 2 +- spec/models/service_spec.rb | 2 +- spec/models/snippet_spec.rb | 2 +- spec/models/user_spec.rb | 2 +- spec/models/wiki_page_spec.rb | 2 +- 68 files changed, 68 insertions(+), 68 deletions(-) diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 2b325f44f64..e4cac105110 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -15,7 +15,7 @@ require 'spec_helper' -describe BroadcastMessage do +describe BroadcastMessage, models: true do subject { create(:broadcast_message) } it { is_expected.to be_valid } diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 839b4c6b16e..70c831b7cbe 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -25,7 +25,7 @@ require 'spec_helper' -describe Ci::Build do +describe Ci::Build, models: true do let(:project) { FactoryGirl.create :ci_project } let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index a13f6458cac..89813cdf7fc 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -18,7 +18,7 @@ require 'spec_helper' -describe Ci::Commit do +describe Ci::Commit, models: true do let(:project) { FactoryGirl.create :ci_project } let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb index e23d6ae2c28..7d54b6cf84c 100644 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ b/spec/models/ci/project_services/hip_chat_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::HipChatMessage do +describe Ci::HipChatMessage, models: true do subject { Ci::HipChatMessage.new(build) } let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb index d9ccc855edf..714f1e17e0b 100644 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ b/spec/models/ci/project_services/hip_chat_service_spec.rb @@ -15,7 +15,7 @@ require 'spec_helper' -describe Ci::HipChatService do +describe Ci::HipChatService, models: true do describe "Validations" do diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb index c03be3ef75f..638d9a4a626 100644 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ b/spec/models/ci/project_services/mail_service_spec.rb @@ -14,7 +14,7 @@ require 'spec_helper' -describe Ci::MailService do +describe Ci::MailService, models: true do describe "Associations" do it { is_expected.to belong_to :project } end diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb index 8adda6c86cc..226032b4cda 100644 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ b/spec/models/ci/project_services/slack_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::SlackMessage do +describe Ci::SlackMessage, models: true do subject { Ci::SlackMessage.new(commit) } let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb index 1ac7dfe568d..e7d7d5d6f4c 100644 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ b/spec/models/ci/project_services/slack_service_spec.rb @@ -14,7 +14,7 @@ require 'spec_helper' -describe Ci::SlackService do +describe Ci::SlackService, models: true do describe "Associations" do it { is_expected.to belong_to :project } end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index ac7e38bbcb0..346471aa9b5 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -27,7 +27,7 @@ require 'spec_helper' -describe Ci::Project do +describe Ci::Project, models: true do let(:project) { FactoryGirl.create :ci_project } let(:gl_project) { project.gl_project } subject { project } diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb index 37682c6ea0c..da8491357a5 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -11,6 +11,6 @@ require 'spec_helper' -describe Ci::RunnerProject do +describe Ci::RunnerProject, models: true do pending "add some examples to (or delete) #{__FILE__}" end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 9a1233b9095..6ebb5e86863 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -19,7 +19,7 @@ require 'spec_helper' -describe Ci::Runner do +describe Ci::Runner, models: true do describe '#display_name' do it 'should return the description if it has a value' do runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448') diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb index 36cda988eb4..34e3af7f810 100644 --- a/spec/models/ci/service_spec.rb +++ b/spec/models/ci/service_spec.rb @@ -14,7 +14,7 @@ require 'spec_helper' -describe Ci::Service do +describe Ci::Service, models: true do describe "Associations" do it { is_expected.to belong_to :project } diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index b8aa3c1e777..61eb3c08296 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -12,7 +12,7 @@ require 'spec_helper' -describe Ci::Trigger do +describe Ci::Trigger, models: true do let(:project) { FactoryGirl.create :ci_project } describe 'before_validation' do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index a515f5881ff..31b56953a13 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -13,7 +13,7 @@ require 'spec_helper' -describe Ci::Variable do +describe Ci::Variable, models: true do subject { Ci::Variable.new } let(:secret_value) { 'secret' } diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb index 2865482a212..1a4edec9d4f 100644 --- a/spec/models/ci/web_hook_spec.rb +++ b/spec/models/ci/web_hook_spec.rb @@ -11,7 +11,7 @@ require 'spec_helper' -describe Ci::WebHook do +describe Ci::WebHook, models: true do describe "Associations" do it { is_expected.to belong_to :project } end diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb index 3c1009a2eb0..9307d97e214 100644 --- a/spec/models/commit_range_spec.rb +++ b/spec/models/commit_range_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CommitRange do +describe CommitRange, models: true do describe 'modules' do subject { described_class } diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 38a3dc1f4a6..ecf37b40c58 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Commit do +describe Commit, models: true do let(:project) { create(:project) } let(:commit) { project.commit } diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index dca0715eed8..5e311ead28b 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -33,7 +33,7 @@ require 'spec_helper' -describe CommitStatus do +describe CommitStatus, models: true do let(:commit) { FactoryGirl.create :ci_commit } let(:commit_status) { FactoryGirl.create :commit_status, commit: commit } diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb index f7ed30f8198..25b3f4e50da 100644 --- a/spec/models/concerns/case_sensitivity_spec.rb +++ b/spec/models/concerns/case_sensitivity_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CaseSensitivity do +describe CaseSensitivity, models: true do describe '.iwhere' do let(:connection) { ActiveRecord::Base.connection } let(:model) { Class.new { include CaseSensitivity } } diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 95729932459..64ba778afea 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -15,7 +15,7 @@ require 'spec_helper' -describe DeployKey do +describe DeployKey, models: true do let(:project) { create(:project) } let(:deploy_key) { create(:deploy_key, projects: [project]) } diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index 0eb22599d18..8aedbfb8636 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -11,7 +11,7 @@ require 'spec_helper' -describe DeployKeysProject do +describe DeployKeysProject, models: true do describe "Associations" do it { is_expected.to belong_to(:deploy_key) } it { is_expected.to belong_to(:project) } diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index ae53f7a536b..071582b0282 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -16,7 +16,7 @@ require 'spec_helper' -describe Event do +describe Event, models: true do describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:target) } diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index 7744610db78..6ec6b9037a4 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ExternalIssue do +describe ExternalIssue, models: true do let(:project) { double('project', to_reference: 'namespace1/project1') } let(:issue) { described_class.new('EXT-1234', project) } diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/external_wiki_service_spec.rb index 4bd5b0be61c..b198aa77526 100644 --- a/spec/models/external_wiki_service_spec.rb +++ b/spec/models/external_wiki_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe ExternalWikiService do +describe ExternalWikiService, models: true do include ExternalWikiHelper describe "Associations" do it { should belong_to :project } diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index c86314c454c..d61c1c96bde 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -33,7 +33,7 @@ require 'spec_helper' -describe GenericCommitStatus do +describe GenericCommitStatus, models: true do let(:commit) { FactoryGirl.create :ci_commit } let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit } diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb index 6eeff30b20e..ba03e6aabd0 100644 --- a/spec/models/global_milestone_spec.rb +++ b/spec/models/global_milestone_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GlobalMilestone do +describe GlobalMilestone, models: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:group) { create(:group) } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 6f166b5ab75..646f767e6fe 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -16,7 +16,7 @@ require 'spec_helper' -describe Group do +describe Group, models: true do let!(:group) { create(:group) } describe 'associations' do diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb index a2dc66fce3e..645ee0b929a 100644 --- a/spec/models/hooks/project_hook_spec.rb +++ b/spec/models/hooks/project_hook_spec.rb @@ -18,7 +18,7 @@ require 'spec_helper' -describe ProjectHook do +describe ProjectHook, models: true do describe '.push_hooks' do it 'should return hooks for push events only' do hook = create(:project_hook, push_events: true) diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb index 16641c12124..1455661485b 100644 --- a/spec/models/hooks/service_hook_spec.rb +++ b/spec/models/hooks/service_hook_spec.rb @@ -18,7 +18,7 @@ require "spec_helper" -describe ServiceHook do +describe ServiceHook, models: true do describe "Associations" do it { is_expected.to belong_to :service } end diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb index 02d2cc2c77a..138b87a9a06 100644 --- a/spec/models/hooks/system_hook_spec.rb +++ b/spec/models/hooks/system_hook_spec.rb @@ -18,7 +18,7 @@ require "spec_helper" -describe SystemHook do +describe SystemHook, models: true do describe "execute" do before(:each) do @system_hook = create(:system_hook) diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 35042788c65..2d90b0793cc 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -18,7 +18,7 @@ require 'spec_helper' -describe ProjectHook do +describe ProjectHook, models: true do describe "Associations" do it { is_expected.to belong_to :project } end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index c9aa1b063c6..52271c7c8c6 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe Issue do +describe Issue, models: true do describe "Associations" do it { is_expected.to belong_to(:milestone) } end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index 2f819f60cbb..d7fe01976d8 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -15,7 +15,7 @@ require 'spec_helper' -describe Key do +describe Key, models: true do describe "Associations" do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index 8c240826582..dc7510b1de3 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -12,7 +12,7 @@ require 'spec_helper' -describe LabelLink do +describe LabelLink, models: true do let(:label) { create(:label_link) } it { expect(label).to be_valid } diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 511ee8cbd96..696fbf7e0aa 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -13,7 +13,7 @@ require 'spec_helper' -describe Label do +describe Label, models: true do let(:label) { create(:label) } describe 'associations' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 57f840c1e91..2aedca20df2 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -19,7 +19,7 @@ require 'spec_helper' -describe Member do +describe Member, models: true do describe "Associations" do it { is_expected.to belong_to(:user) } end diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index 652026729bb..5424c9b9cba 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -19,7 +19,7 @@ require 'spec_helper' -describe GroupMember do +describe GroupMember, models: true do context 'notification' do describe "#after_create" do it "should send email to user" do diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index ee912bf12a2..9f26d9eb5ce 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -19,7 +19,7 @@ require 'spec_helper' -describe ProjectMember do +describe ProjectMember, models: true do describe :import_team do before do @abilities = Six.new diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 567c911425c..6a456ca5ea0 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -25,7 +25,7 @@ require 'spec_helper' -describe MergeRequest do +describe MergeRequest, models: true do subject { create(:merge_request) } describe 'associations' do diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 77c58627322..30a71987d86 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -15,7 +15,7 @@ require 'spec_helper' -describe Milestone do +describe Milestone, models: true do describe "Associations" do it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:issues) } diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index a98b9cb7321..4fa2d2bc4d2 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -16,7 +16,7 @@ require 'spec_helper' -describe Namespace do +describe Namespace, models: true do let!(:namespace) { create(:namespace) } it { is_expected.to have_many :projects } diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index e7e8887baf2..cd3c868ecc5 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -21,7 +21,7 @@ require 'spec_helper' -describe Note do +describe Note, models: true do describe 'associations' do it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:noteable) } diff --git a/spec/models/project_security_spec.rb b/spec/models/project_security_spec.rb index f600a240c46..3643ad1b052 100644 --- a/spec/models/project_security_spec.rb +++ b/spec/models/project_security_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Project do +describe Project, models: true do describe :authorization do before do @p1 = create(:project) diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 230807ea672..88cd624877a 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe BuildkiteService do +describe BuildkiteService, models: true do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index e9967f5fe0b..a2cf68a9e38 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe DroneCiService do +describe DroneCiService, models: true do describe 'associations' do it { is_expected.to belong_to(:project) } it { is_expected.to have_one(:service_hook) } diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index 16296607a94..ff7fbcaa004 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe FlowdockService do +describe FlowdockService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index 9e156472316..ecb3ccb1673 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe GemnasiumService do +describe GemnasiumService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index b9006b693b2..835bf364050 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe GitlabCiService do +describe GitlabCiService, models: true do describe 'associations' do it { is_expected.to belong_to(:project) } it { is_expected.to have_one(:service_hook) } diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index e34ca09bffc..3518dbd1728 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe GitlabIssueTrackerService do +describe GitlabIssueTrackerService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index f67d7b30980..c96ab548149 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe HipchatService do +describe HipchatService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index 7d483a44c53..b783b1a576e 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -22,7 +22,7 @@ require 'spec_helper' require 'socket' require 'json' -describe IrkerService do +describe IrkerService, models: true do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 576f5fc79eb..7d91ebe9ce6 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe JiraService do +describe JiraService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index ac10ffbd39b..96039f9491b 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe PushoverService do +describe PushoverService, models: true do describe 'Associations' do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb index b78d92f23a1..97e6f03e308 100644 --- a/spec/models/project_services/slack_service/issue_message_spec.rb +++ b/spec/models/project_services/slack_service/issue_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::IssueMessage do +describe SlackService::IssueMessage, models: true do subject { SlackService::IssueMessage.new(args) } let(:args) do diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb index 581c50d6c88..dae8bd90922 100644 --- a/spec/models/project_services/slack_service/merge_message_spec.rb +++ b/spec/models/project_services/slack_service/merge_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::MergeMessage do +describe SlackService::MergeMessage, models: true do subject { SlackService::MergeMessage.new(args) } let(:args) do diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb index 21fb575480b..ebf8837570e 100644 --- a/spec/models/project_services/slack_service/note_message_spec.rb +++ b/spec/models/project_services/slack_service/note_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::NoteMessage do +describe SlackService::NoteMessage, models: true do let(:color) { '#345' } before do diff --git a/spec/models/project_services/slack_service/push_message_spec.rb b/spec/models/project_services/slack_service/push_message_spec.rb index ddc290820d1..cda9ee670b0 100644 --- a/spec/models/project_services/slack_service/push_message_spec.rb +++ b/spec/models/project_services/slack_service/push_message_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SlackService::PushMessage do +describe SlackService::PushMessage, models: true do subject { SlackService::PushMessage.new(args) } let(:args) do diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index 97b60e19e40..a9e0afad90f 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe SlackService do +describe SlackService, models: true do describe "Associations" do it { is_expected.to belong_to :project } it { is_expected.to have_one :service_hook } diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index 3e8f106d27f..cc92eb0bd9f 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -17,7 +17,7 @@ require 'spec_helper' -describe ProjectSnippet do +describe ProjectSnippet, models: true do describe "Associations" do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index dc703d8095c..6ddb0e2b8f7 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -33,7 +33,7 @@ require 'spec_helper' -describe Project do +describe Project, models: true do describe 'associations' do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:namespace) } diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb index 26e8fdae472..5cd5ae327bf 100644 --- a/spec/models/project_team_spec.rb +++ b/spec/models/project_team_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe ProjectTeam do +describe ProjectTeam, models: true do let(:master) { create(:user) } let(:reporter) { create(:user) } let(:guest) { create(:user) } diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb index 3b889144447..876b927eaea 100644 --- a/spec/models/project_wiki_spec.rb +++ b/spec/models/project_wiki_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe ProjectWiki do +describe ProjectWiki, models: true do let(:project) { create(:empty_project) } let(:repository) { project.repository } let(:user) { project.owner } diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 1e6937b536c..7e956cf6779 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -12,7 +12,7 @@ require 'spec_helper' -describe ProtectedBranch do +describe ProtectedBranch, models: true do describe 'Associations' do it { is_expected.to belong_to(:project) } end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index fa261e64c35..e6c415da267 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Repository do +describe Repository, models: true do include RepoHelpers let(:repository) { create(:project).repository } diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 692e5fda3ba..0ca82365b98 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -20,7 +20,7 @@ require 'spec_helper' -describe Service do +describe Service, models: true do describe "Associations" do it { is_expected.to belong_to :project } diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 81581838675..eb2dbbdc5a4 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -17,7 +17,7 @@ require 'spec_helper' -describe Snippet do +describe Snippet, models: true do describe 'modules' do subject { described_class } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 1aad37fa02e..daa9d1087bf 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -61,7 +61,7 @@ require 'spec_helper' -describe User do +describe User, models: true do include Gitlab::CurrentSettings describe 'modules' do diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index d7802d1734f..c1b03838aa9 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe WikiPage do +describe WikiPage, models: true do let(:project) { create(:empty_project) } let(:user) { project.owner } let(:wiki) { ProjectWiki.new(project, user) } -- cgit v1.2.1 From ae0b9017315b38dd42b4a1c9b6fb1daa78fee28a Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 10:51:01 +0100 Subject: Split up specs more --- .gitlab-ci.yml | 9 ++++++++- lib/tasks/spec.rake | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 527ccb08ab7..943e40d5303 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,6 +24,13 @@ spec:api: - ruby - mysql +spec:models: + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:models + tags: + - ruby + - mysql + spec:benchmark: script: - RAILS_ENV=test bundle exec rake spec:benchmark @@ -96,7 +103,7 @@ flay: - mysql bundler:audit: - script: + script: - "bundle exec bundle-audit update" - "bundle exec bundle-audit check" tags: diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 365ff2defd4..049e17e6c11 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -19,6 +19,15 @@ namespace :spec do run_commands(cmds) end + desc 'GitLab | Rspec | Run model specs' + task :models do + cmds = [ + %W(rake gitlab:setup), + %W(rspec spec --tag @models) + ] + run_commands(cmds) + end + desc 'GitLab | Rspec | Run benchmark specs' task :benchmark do cmds = [ @@ -32,7 +41,7 @@ namespace :spec do task :other do cmds = [ %W(rake gitlab:setup), - %W(rspec spec --tag ~@api --tag ~@feature --tag ~@benchmark) + %W(rspec spec --tag ~@api,~@feature,~@models,~@benchmark) ] run_commands(cmds) end -- cgit v1.2.1 From 7a5e77c0a0591ff5d121f93deced27b798ddbee3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 11:09:25 +0100 Subject: Fix rspec tag syntax --- lib/tasks/spec.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 049e17e6c11..343e4b63524 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -41,7 +41,7 @@ namespace :spec do task :other do cmds = [ %W(rake gitlab:setup), - %W(rspec spec --tag ~@api,~@feature,~@models,~@benchmark) + %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@benchmark) ] run_commands(cmds) end -- cgit v1.2.1 From 905090dbe9d140ef950995c63204a864a161dbdb Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 11:18:21 +0100 Subject: Remove RedCloth and no longer allow bundle-audit to fail --- .gitlab-ci.yml | 3 +-- Gemfile | 1 - Gemfile.lock | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e8290fb36b2..c6d1e622bd0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -89,10 +89,9 @@ flay: - mysql bundler:audit: - script: + script: - "bundle exec bundle-audit update" - "bundle exec bundle-audit check" tags: - ruby - mysql - allow_failure: true diff --git a/Gemfile b/Gemfile index 91ad4b6fb4d..473770f53e5 100644 --- a/Gemfile +++ b/Gemfile @@ -93,7 +93,6 @@ gem 'html-pipeline', '~> 1.11.0' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'github-markup', '~> 1.3.1' gem 'redcarpet', '~> 3.3.3' -gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3979418ed45..37ba22b7ceb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,6 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.2) - RedCloth (4.2.9) ace-rails-ap (2.0.1) actionmailer (4.2.4) actionpack (= 4.2.4) @@ -826,7 +825,6 @@ PLATFORMS ruby DEPENDENCIES - RedCloth (~> 4.2.9) ace-rails-ap (~> 2.0.1) activerecord-deprecated_finders (~> 1.0.3) activerecord-session_store (~> 0.1.0) -- cgit v1.2.1 From 13d6bab177d2a703d19b2fc5f28271ed083273c3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 11:55:36 +0100 Subject: Tag lib specs --- spec/lib/ci/ansi2html_spec.rb | 2 +- spec/lib/ci/charts_spec.rb | 2 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 2 +- spec/lib/disable_email_interceptor_spec.rb | 2 +- spec/lib/extracts_path_spec.rb | 2 +- spec/lib/file_size_validator_spec.rb | 2 +- spec/lib/git_ref_validator_spec.rb | 2 +- spec/lib/gitlab/asciidoc_spec.rb | 2 +- spec/lib/gitlab/auth_spec.rb | 2 +- spec/lib/gitlab/backend/grack_auth_spec.rb | 2 +- spec/lib/gitlab/backend/shell_spec.rb | 4 ++-- spec/lib/gitlab/bitbucket_import/client_spec.rb | 2 +- spec/lib/gitlab/bitbucket_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/closing_issue_extractor_spec.rb | 2 +- spec/lib/gitlab/color_schemes_spec.rb | 2 +- spec/lib/gitlab/database_spec.rb | 2 +- spec/lib/gitlab/diff/file_spec.rb | 2 +- spec/lib/gitlab/diff/parser_spec.rb | 2 +- spec/lib/gitlab/email/attachment_uploader_spec.rb | 2 +- spec/lib/gitlab/email/receiver_spec.rb | 2 +- spec/lib/gitlab/email/reply_parser_spec.rb | 2 +- spec/lib/gitlab/git_access_spec.rb | 2 +- spec/lib/gitlab/git_access_wiki_spec.rb | 2 +- spec/lib/gitlab/github_import/client_spec.rb | 2 +- spec/lib/gitlab/github_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/gitlab_import/client_spec.rb | 2 +- spec/lib/gitlab/gitlab_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/gitorious_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/google_code_import/client_spec.rb | 2 +- spec/lib/gitlab/google_code_import/importer_spec.rb | 2 +- spec/lib/gitlab/google_code_import/project_creator_spec.rb | 2 +- spec/lib/gitlab/incoming_email_spec.rb | 2 +- spec/lib/gitlab/inline_diff_spec.rb | 2 +- spec/lib/gitlab/key_fingerprint_spec.rb | 2 +- spec/lib/gitlab/ldap/access_spec.rb | 2 +- spec/lib/gitlab/ldap/adapter_spec.rb | 2 +- spec/lib/gitlab/ldap/auth_hash_spec.rb | 2 +- spec/lib/gitlab/ldap/authentication_spec.rb | 2 +- spec/lib/gitlab/ldap/config_spec.rb | 2 +- spec/lib/gitlab/ldap/user_spec.rb | 2 +- spec/lib/gitlab/lfs/lfs_router_spec.rb | 2 +- spec/lib/gitlab/markdown/autolink_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/commit_range_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/commit_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/cross_project_reference_spec.rb | 2 +- spec/lib/gitlab/markdown/emoji_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/external_issue_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/external_link_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/issue_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/label_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/merge_request_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/redactor_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/reference_gatherer_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/relative_link_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/sanitization_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/snippet_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/syntax_highlight_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/table_of_contents_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/task_list_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/upload_link_filter_spec.rb | 2 +- spec/lib/gitlab/markdown/user_reference_filter_spec.rb | 2 +- spec/lib/gitlab/markup_helper_spec.rb | 2 +- spec/lib/gitlab/note_data_builder_spec.rb | 2 +- spec/lib/gitlab/o_auth/auth_hash_spec.rb | 2 +- spec/lib/gitlab/o_auth/user_spec.rb | 2 +- spec/lib/gitlab/popen_spec.rb | 2 +- spec/lib/gitlab/project_search_results_spec.rb | 2 +- spec/lib/gitlab/push_data_builder_spec.rb | 2 +- spec/lib/gitlab/reference_extractor_spec.rb | 2 +- spec/lib/gitlab/regex_spec.rb | 2 +- spec/lib/gitlab/sherlock/collection_spec.rb | 2 +- spec/lib/gitlab/sherlock/file_sample_spec.rb | 2 +- spec/lib/gitlab/sherlock/line_profiler_spec.rb | 2 +- spec/lib/gitlab/sherlock/line_sample_spec.rb | 2 +- spec/lib/gitlab/sherlock/location_spec.rb | 2 +- spec/lib/gitlab/sherlock/middleware_spec.rb | 2 +- spec/lib/gitlab/sherlock/query_spec.rb | 2 +- spec/lib/gitlab/sherlock/transaction_spec.rb | 2 +- spec/lib/gitlab/sql/union_spec.rb | 2 +- spec/lib/gitlab/themes_spec.rb | 2 +- spec/lib/gitlab/upgrader_spec.rb | 2 +- spec/lib/gitlab/uploads_transfer_spec.rb | 2 +- spec/lib/gitlab/url_builder_spec.rb | 2 +- spec/lib/gitlab/version_info_spec.rb | 2 +- spec/lib/repository_cache_spec.rb | 2 +- 85 files changed, 86 insertions(+), 86 deletions(-) diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb index 75c023bbc43..3a2b568f4c7 100644 --- a/spec/lib/ci/ansi2html_spec.rb +++ b/spec/lib/ci/ansi2html_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Ansi2html do +describe Ci::Ansi2html, lib: true do subject { Ci::Ansi2html } it "prints non-ansi as-is" do diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb index 83e2ad220b8..50a77308cde 100644 --- a/spec/lib/ci/charts_spec.rb +++ b/spec/lib/ci/charts_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe "Charts" do +describe Ci::Charts, lib: true do context "build_times" do before do diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index 6f287719ba6..ce4a5244bd0 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Ci - describe GitlabCiYamlProcessor do + describe GitlabCiYamlProcessor, lib: true do let(:path) { 'path' } describe "#builds_for_ref" do diff --git a/spec/lib/disable_email_interceptor_spec.rb b/spec/lib/disable_email_interceptor_spec.rb index a9624e9a2b7..c2a7b20b84d 100644 --- a/spec/lib/disable_email_interceptor_spec.rb +++ b/spec/lib/disable_email_interceptor_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DisableEmailInterceptor do +describe DisableEmailInterceptor, lib: true do before do ActionMailer::Base.register_interceptor(DisableEmailInterceptor) end diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb index 48bc60eed16..f38fadda9ba 100644 --- a/spec/lib/extracts_path_spec.rb +++ b/spec/lib/extracts_path_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ExtractsPath do +describe ExtractsPath, lib: true do include ExtractsPath include RepoHelpers include Gitlab::Application.routes.url_helpers diff --git a/spec/lib/file_size_validator_spec.rb b/spec/lib/file_size_validator_spec.rb index 12ccc051c74..fda6f9a6c88 100644 --- a/spec/lib/file_size_validator_spec.rb +++ b/spec/lib/file_size_validator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Gitlab::FileSizeValidatorSpec' do +describe FileSizeValidator, lib: true do let(:validator) { FileSizeValidator.new(options) } let(:attachment) { AttachmentUploader.new } let(:note) { create(:note) } diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/git_ref_validator_spec.rb index 4633b6f3934..dc57e94f193 100644 --- a/spec/lib/git_ref_validator_spec.rb +++ b/spec/lib/git_ref_validator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitRefValidator do +describe Gitlab::GitRefValidator, lib: true do it { expect(Gitlab::GitRefValidator.validate('feature/new')).to be_truthy } it { expect(Gitlab::GitRefValidator.validate('implement_@all')).to be_truthy } it { expect(Gitlab::GitRefValidator.validate('my_new_feature')).to be_truthy } diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index 03e36fd3552..7d985fb6f1e 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'nokogiri' module Gitlab - describe Asciidoc do + describe Asciidoc, lib: true do let(:input) { 'ascii' } let(:context) { {} } diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb index 72806bebe1f..aad291c03cd 100644 --- a/spec/lib/gitlab/auth_spec.rb +++ b/spec/lib/gitlab/auth_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Auth do +describe Gitlab::Auth, lib: true do let(:gl_auth) { Gitlab::Auth.new } describe :find do diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index dfa0e10318a..73458735ad9 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Grack::Auth do +describe Grack::Auth, lib: true do let(:user) { create(:user) } let(:project) { create(:project) } diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index b60e23454d6..fd869f48b5c 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Shell do +describe Gitlab::Shell, lib: true do let(:project) { double('Project', id: 7, path: 'diaspora') } let(:gitlab_shell) { Gitlab::Shell.new } @@ -16,7 +16,7 @@ describe Gitlab::Shell do 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 Gitlab::Shell::KeyAdder, lib: true do describe '#add_key' do it 'normalizes space characters in the key' do io = spy diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index dfe58637eee..aa0699f2ebf 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BitbucketImport::Client do +describe Gitlab::BitbucketImport::Client, lib: true do let(:token) { '123456' } let(:secret) { 'secret' } let(:client) { Gitlab::BitbucketImport::Client.new(token, secret) } diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb index 0e826a319e0..e1c60e07b4d 100644 --- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::BitbucketImport::ProjectCreator do +describe Gitlab::BitbucketImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) do { diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb index fe1b94a484e..99288da1e43 100644 --- a/spec/lib/gitlab/closing_issue_extractor_spec.rb +++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ClosingIssueExtractor do +describe Gitlab::ClosingIssueExtractor, lib: true do let(:project) { create(:project) } let(:project2) { create(:project) } let(:issue) { create(:issue, project: project) } diff --git a/spec/lib/gitlab/color_schemes_spec.rb b/spec/lib/gitlab/color_schemes_spec.rb index c7be45dbcd3..0a1ec66f199 100644 --- a/spec/lib/gitlab/color_schemes_spec.rb +++ b/spec/lib/gitlab/color_schemes_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ColorSchemes do +describe Gitlab::ColorSchemes, lib: true do describe '.body_classes' do it 'returns a space-separated list of class names' do css = described_class.body_classes diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index 7cdebdf209a..8461e8ce50d 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Database do +describe Gitlab::Database, lib: true do # These are just simple smoke tests to check if the methods work (regardless # of what they may return). describe '.mysql?' do diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 8b7946f3117..c7cdf8691d6 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::File do +describe Gitlab::Diff::File, lib: true do include RepoHelpers let(:project) { create(:project) } diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb index 4d5d1431683..ba577bd28e5 100644 --- a/spec/lib/gitlab/diff/parser_spec.rb +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Diff::Parser do +describe Gitlab::Diff::Parser, lib: true do include RepoHelpers let(:project) { create(:project) } diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb index 8fb432367b6..476a21bf996 100644 --- a/spec/lib/gitlab/email/attachment_uploader_spec.rb +++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Email::AttachmentUploader do +describe Gitlab::Email::AttachmentUploader, lib: true do describe "#execute" do let(:project) { build(:project) } let(:message_raw) { fixture_file("emails/attachment.eml") } diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index e470b7cd5f5..b535413bbd4 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::Email::Receiver do +describe Gitlab::Email::Receiver, lib: true do before do stub_incoming_email_setting(enabled: true, address: "reply+%{key}@appmail.adventuretime.ooo") end diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb index 7cae1da8050..6f8e9a4be64 100644 --- a/spec/lib/gitlab/email/reply_parser_spec.rb +++ b/spec/lib/gitlab/email/reply_parser_spec.rb @@ -1,7 +1,7 @@ require "spec_helper" # Inspired in great part by Discourse's Email::Receiver -describe Gitlab::Email::ReplyParser do +describe Gitlab::Email::ReplyParser, lib: true do describe '#execute' do def test_parse_body(mail_string) described_class.new(Mail::Message.new(mail_string)).execute diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index c7291689e32..9b3a0e3a75f 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitAccess do +describe Gitlab::GitAccess, lib: true do let(:access) { Gitlab::GitAccess.new(actor, project) } let(:project) { create(:project) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb index 4cb91094cb3..77ecfce6f17 100644 --- a/spec/lib/gitlab/git_access_wiki_spec.rb +++ b/spec/lib/gitlab/git_access_wiki_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitAccessWiki do +describe Gitlab::GitAccessWiki, lib: true do let(:access) { Gitlab::GitAccessWiki.new(user, project) } let(:project) { create(:project) } let(:user) { create(:user) } diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb index 26618120316..49d8cdf4314 100644 --- a/spec/lib/gitlab/github_import/client_spec.rb +++ b/spec/lib/gitlab/github_import/client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::Client do +describe Gitlab::GithubImport::Client, lib: true do let(:token) { '123456' } let(:client) { Gitlab::GithubImport::Client.new(token) } diff --git a/spec/lib/gitlab/github_import/project_creator_spec.rb b/spec/lib/gitlab/github_import/project_creator_spec.rb index ca61d3c5234..c93a3ebdaec 100644 --- a/spec/lib/gitlab/github_import/project_creator_spec.rb +++ b/spec/lib/gitlab/github_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GithubImport::ProjectCreator do +describe Gitlab::GithubImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) do OpenStruct.new( diff --git a/spec/lib/gitlab/gitlab_import/client_spec.rb b/spec/lib/gitlab/gitlab_import/client_spec.rb index c511c515474..e6831e7c383 100644 --- a/spec/lib/gitlab/gitlab_import/client_spec.rb +++ b/spec/lib/gitlab/gitlab_import/client_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitlabImport::Client do +describe Gitlab::GitlabImport::Client, lib: true do let(:token) { '123456' } let(:client) { Gitlab::GitlabImport::Client.new(token) } diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb index 2d8923d14bb..483f65cd053 100644 --- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb +++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitlabImport::ProjectCreator do +describe Gitlab::GitlabImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) do { diff --git a/spec/lib/gitlab/gitorious_import/project_creator_spec.rb b/spec/lib/gitlab/gitorious_import/project_creator_spec.rb index c1125ca6357..946712ca38e 100644 --- a/spec/lib/gitlab/gitorious_import/project_creator_spec.rb +++ b/spec/lib/gitlab/gitorious_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GitoriousImport::ProjectCreator do +describe Gitlab::GitoriousImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) { Gitlab::GitoriousImport::Repository.new('foo/bar-baz-qux') } let(:namespace){ create(:group, owner: user) } diff --git a/spec/lib/gitlab/google_code_import/client_spec.rb b/spec/lib/gitlab/google_code_import/client_spec.rb index 37985c062b4..85949ae8dc4 100644 --- a/spec/lib/gitlab/google_code_import/client_spec.rb +++ b/spec/lib/gitlab/google_code_import/client_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::GoogleCodeImport::Client do +describe Gitlab::GoogleCodeImport::Client, lib: true do let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) } subject { described_class.new(raw_data) } diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index 65ad7524cc2..647631271e0 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::GoogleCodeImport::Importer do +describe Gitlab::GoogleCodeImport::Importer, lib: true do let(:mapped_user) { create(:user, username: "thilo123") } let(:raw_data) { JSON.parse(fixture_file("GoogleCodeProjectHosting.json")) } let(:client) { Gitlab::GoogleCodeImport::Client.new(raw_data) } diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb index 35549b48687..499a896ee76 100644 --- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb +++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::GoogleCodeImport::ProjectCreator do +describe Gitlab::GoogleCodeImport::ProjectCreator, lib: true do let(:user) { create(:user) } let(:repo) do Gitlab::GoogleCodeImport::Repository.new( diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb index 5fdb9c723b1..bcdba8d4c12 100644 --- a/spec/lib/gitlab/incoming_email_spec.rb +++ b/spec/lib/gitlab/incoming_email_spec.rb @@ -1,6 +1,6 @@ require "spec_helper" -describe Gitlab::IncomingEmail do +describe Gitlab::IncomingEmail, lib: true do describe "self.enabled?" do context "when reply by email is enabled" do before do diff --git a/spec/lib/gitlab/inline_diff_spec.rb b/spec/lib/gitlab/inline_diff_spec.rb index 2e0a05088cc..c690c195112 100644 --- a/spec/lib/gitlab/inline_diff_spec.rb +++ b/spec/lib/gitlab/inline_diff_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::InlineDiff do +describe Gitlab::InlineDiff, lib: true do describe '#processing' do let(:diff) do < Date: Wed, 9 Dec 2015 11:55:49 +0100 Subject: Tag service specs --- spec/services/archive_repository_service_spec.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 2 +- spec/services/ci/create_trigger_request_service_spec.rb | 2 +- spec/services/ci/event_service_spec.rb | 2 +- spec/services/ci/image_for_build_service_spec.rb | 2 +- spec/services/ci/register_build_service_spec.rb | 2 +- spec/services/ci/web_hook_service_spec.rb | 2 +- spec/services/create_release_service_spec.rb | 2 +- spec/services/create_snippet_service_spec.rb | 2 +- spec/services/destroy_group_service_spec.rb | 2 +- spec/services/event_create_service_spec.rb | 2 +- spec/services/git_hooks_service_spec.rb | 2 +- spec/services/git_push_service_spec.rb | 2 +- spec/services/git_tag_push_service_spec.rb | 2 +- spec/services/issues/bulk_update_service_spec.rb | 2 +- spec/services/issues/close_service_spec.rb | 2 +- spec/services/issues/create_service_spec.rb | 2 +- spec/services/issues/update_service_spec.rb | 2 +- spec/services/merge_requests/close_service_spec.rb | 2 +- spec/services/merge_requests/create_service_spec.rb | 2 +- spec/services/merge_requests/merge_service_spec.rb | 2 +- spec/services/merge_requests/refresh_service_spec.rb | 2 +- spec/services/merge_requests/reopen_service_spec.rb | 2 +- spec/services/merge_requests/update_service_spec.rb | 2 +- spec/services/milestones/close_service_spec.rb | 2 +- spec/services/milestones/create_service_spec.rb | 2 +- spec/services/notes/create_service_spec.rb | 2 +- spec/services/notification_service_spec.rb | 2 +- spec/services/projects/create_service_spec.rb | 2 +- spec/services/projects/destroy_service_spec.rb | 2 +- spec/services/projects/download_service_spec.rb | 2 +- spec/services/projects/fork_service_spec.rb | 2 +- spec/services/projects/transfer_service_spec.rb | 2 +- spec/services/projects/update_service_spec.rb | 2 +- spec/services/projects/upload_service_spec.rb | 2 +- spec/services/search_service_spec.rb | 2 +- spec/services/system_hooks_service_spec.rb | 2 +- spec/services/system_note_service_spec.rb | 2 +- spec/services/test_hook_service_spec.rb | 2 +- spec/services/update_release_service_spec.rb | 2 +- spec/services/update_snippet_service_spec.rb | 2 +- 41 files changed, 41 insertions(+), 41 deletions(-) diff --git a/spec/services/archive_repository_service_spec.rb b/spec/services/archive_repository_service_spec.rb index f7a36cd9670..bd871605c66 100644 --- a/spec/services/archive_repository_service_spec.rb +++ b/spec/services/archive_repository_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ArchiveRepositoryService do +describe ArchiveRepositoryService, services: true do let(:project) { create(:project) } subject { ArchiveRepositoryService.new(project, "master", "zip") } diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index e0ede1d58b7..c2fafca2ad2 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Ci - describe CreateCommitService do + describe CreateCommitService, services: true do let(:service) { CreateCommitService.new } let(:project) { FactoryGirl.create(:ci_project) } let(:user) { nil } diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index 2ef4bb50a57..c80cb58163a 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::CreateTriggerRequestService do +describe Ci::CreateTriggerRequestService, services: true do let(:service) { Ci::CreateTriggerRequestService.new } let(:gl_project) { create(:project) } let(:project) { gl_project.ensure_gitlab_ci_project } diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb index 1264e17ff5e..32516c75cf3 100644 --- a/spec/services/ci/event_service_spec.rb +++ b/spec/services/ci/event_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::EventService do +describe Ci::EventService, services: true do let(:project) { FactoryGirl.create :ci_project } let(:user) { double(username: "root", id: 1) } diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index cda7d0c4a51..b43cabb4ee4 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Ci - describe ImageForBuildService do + describe ImageForBuildService, services: true do let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:ci_project) } let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) } diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index b370dfbe113..379e07982fb 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' module Ci - describe RegisterBuildService do + describe RegisterBuildService, services: true do let!(:service) { RegisterBuildService.new } let!(:gl_project) { FactoryGirl.create :empty_project } let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb index aa48fcbcbfd..e7d8ab30652 100644 --- a/spec/services/ci/web_hook_service_spec.rb +++ b/spec/services/ci/web_hook_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::WebHookService do +describe Ci::WebHookService, services: true do let(:project) { FactoryGirl.create :ci_project } let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb index 26d7f365bbb..61e5ae72f51 100644 --- a/spec/services/create_release_service_spec.rb +++ b/spec/services/create_release_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateReleaseService do +describe CreateReleaseService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } diff --git a/spec/services/create_snippet_service_spec.rb b/spec/services/create_snippet_service_spec.rb index 8edabe9450b..c800dea04fa 100644 --- a/spec/services/create_snippet_service_spec.rb +++ b/spec/services/create_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe CreateSnippetService do +describe CreateSnippetService, services: true do before do @user = create :user @admin = create :user, admin: true diff --git a/spec/services/destroy_group_service_spec.rb b/spec/services/destroy_group_service_spec.rb index e28564b3866..afa89b84175 100644 --- a/spec/services/destroy_group_service_spec.rb +++ b/spec/services/destroy_group_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DestroyGroupService do +describe DestroyGroupService, services: true do let!(:user) { create(:user) } let!(:group) { create(:group) } let!(:project) { create(:project, namespace: group) } diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index 7756b973ecd..f6dc9d4008f 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe EventCreateService do +describe EventCreateService, services: true do let(:service) { EventCreateService.new } describe 'Issues' do diff --git a/spec/services/git_hooks_service_spec.rb b/spec/services/git_hooks_service_spec.rb index 7e018d3c9fe..2bb9c3b3db3 100644 --- a/spec/services/git_hooks_service_spec.rb +++ b/spec/services/git_hooks_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GitHooksService do +describe GitHooksService, services: true do include RepoHelpers let(:user) { create :user } diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 17015d29e51..a04c242cf0e 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GitPushService do +describe GitPushService, services: true do include RepoHelpers let(:user) { create :user } diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index eed50c7ebac..e2d15f1a83d 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe GitTagPushService do +describe GitTagPushService, services: true do include RepoHelpers let(:user) { create :user } diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb index 4c62fbafd73..6a7ea4b2f44 100644 --- a/spec/services/issues/bulk_update_service_spec.rb +++ b/spec/services/issues/bulk_update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::BulkUpdateService do +describe Issues::BulkUpdateService, services: true do let(:issue) { create(:issue, project: @project) } before do diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb index d711e3da104..3a8daf28f5e 100644 --- a/spec/services/issues/close_service_spec.rb +++ b/spec/services/issues/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::CloseService do +describe Issues::CloseService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:issue) { create(:issue, assignee: user2) } diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 7f1ebcb3198..2148d091a57 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::CreateService do +describe Issues::CreateService, services: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 73d0b7f7abe..87da0e9618b 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Issues::UpdateService do +describe Issues::UpdateService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb index 272f3897938..50d0c288790 100644 --- a/spec/services/merge_requests/close_service_spec.rb +++ b/spec/services/merge_requests/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CloseService do +describe MergeRequests::CloseService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:merge_request) { create(:merge_request, assignee: user2) } diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb index cc64d69361e..be8f1676eeb 100644 --- a/spec/services/merge_requests/create_service_spec.rb +++ b/spec/services/merge_requests/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::CreateService do +describe MergeRequests::CreateService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index c0961ceb11e..f2160567f0a 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::MergeService do +describe MergeRequests::MergeService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:merge_request) { create(:merge_request, assignee: user2) } diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb index 7ee4488521d..b892f44af03 100644 --- a/spec/services/merge_requests/refresh_service_spec.rb +++ b/spec/services/merge_requests/refresh_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::RefreshService do +describe MergeRequests::RefreshService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } let(:service) { MergeRequests::RefreshService } diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb index 05146bf43f4..ac0221998f5 100644 --- a/spec/services/merge_requests/reopen_service_spec.rb +++ b/spec/services/merge_requests/reopen_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::ReopenService do +describe MergeRequests::ReopenService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:merge_request) { create(:merge_request, assignee: user2) } diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index d899b1f01d1..2e9e6e0870d 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe MergeRequests::UpdateService do +describe MergeRequests::UpdateService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb index 034c0f22e12..1cd6eb2ab38 100644 --- a/spec/services/milestones/close_service_spec.rb +++ b/spec/services/milestones/close_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestones::CloseService do +describe Milestones::CloseService, services: true do let(:user) { create(:user) } let(:project) { create(:project) } let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) } diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb index 757c9a226d8..c793026e300 100644 --- a/spec/services/milestones/create_service_spec.rb +++ b/spec/services/milestones/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Milestones::CreateService do +describe Milestones::CreateService, services: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index cc38d257792..a797a2fe4aa 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Notes::CreateService do +describe Notes::CreateService, services: true do let(:project) { create(:empty_project) } let(:issue) { create(:issue, project: project) } let(:user) { create(:user) } diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 35fa412ed80..d7a898e85ff 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe NotificationService do +describe NotificationService, services: true do let(:notification) { NotificationService.new } around(:each) do |example| diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index e81c4edb7d8..2d8c316e38d 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::CreateService do +describe Projects::CreateService, services: true do describe :create_by_user do before do @user = create :user diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index e83eef0b1a2..1ec27077717 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::DestroyService do +describe Projects::DestroyService, services: true do let!(:user) { create(:user) } let!(:project) { create(:project, namespace: user.namespace) } let!(:path) { project.repository.path_to_repo } diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index ddee2e62dfc..5ceed5af9a5 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::DownloadService do +describe Projects::DownloadService, services: true do describe 'File service' do before do @user = create :user diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb index 1feba6ce048..d1ee60a0aea 100644 --- a/spec/services/projects/fork_service_spec.rb +++ b/spec/services/projects/fork_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::ForkService do +describe Projects::ForkService, services: true do describe :fork_by_user do before do @from_namespace = create(:namespace) diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 47755bfc990..c46259431aa 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::TransferService do +describe Projects::TransferService, services: true do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, namespace: user.namespace) } diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index b347fa15f87..c36d4581989 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::UpdateService do +describe Projects::UpdateService, services: true do describe :update_by_user do before do @user = create :user diff --git a/spec/services/projects/upload_service_spec.rb b/spec/services/projects/upload_service_spec.rb index 1b1a80d1fe7..9268a9fb1a2 100644 --- a/spec/services/projects/upload_service_spec.rb +++ b/spec/services/projects/upload_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Projects::UploadService do +describe Projects::UploadService, services: true do describe 'File service' do before do @user = create :user diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index f57bfaea879..7b3a9a75d7c 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Search::GlobalService' do +describe 'Search::GlobalService', services: true do let(:user) { create(:user) } let(:public_user) { create(:user) } let(:internal_user) { create(:user) } diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index a31fc1e4b07..febc78d2784 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SystemHooksService do +describe SystemHooksService, services: true do let(:user) { create :user } let(:project) { create :project } let(:project_member) { create :project_member } diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index a45130bd473..19f63323187 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe SystemNoteService do +describe SystemNoteService, services: true do let(:project) { create(:project) } let(:author) { create(:user) } let(:noteable) { create(:issue, project: project) } diff --git a/spec/services/test_hook_service_spec.rb b/spec/services/test_hook_service_spec.rb index 226196eedae..f034f251ba4 100644 --- a/spec/services/test_hook_service_spec.rb +++ b/spec/services/test_hook_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe TestHookService do +describe TestHookService, services: true do let(:user) { create :user } let(:project) { create :project } let(:hook) { create :project_hook, project: project } diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb index 93368c45b88..bba211089a8 100644 --- a/spec/services/update_release_service_spec.rb +++ b/spec/services/update_release_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UpdateReleaseService do +describe UpdateReleaseService, services: true do let(:project) { create(:project) } let(:user) { create(:user) } let(:tag_name) { project.repository.tag_names.first } diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index d7c516e3934..124bb76e678 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UpdateSnippetService do +describe UpdateSnippetService, services: true do before do @user = create :user @admin = create :user, admin: true -- cgit v1.2.1 From 6c6fb1d8bb2c6aa44553ebf35496d64ff6b202d2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 11:56:23 +0100 Subject: Split up spec:other even more --- .gitlab-ci.yml | 14 ++++++++++++++ lib/tasks/spec.rake | 20 +++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 943e40d5303..aec9936da28 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,6 +31,20 @@ spec:models: - ruby - mysql +spec:lib: + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:lib + tags: + - ruby + - mysql + +spec:services: + script: + - RAILS_ENV=test SIMPLECOV=true bundle exec rake spec:services + tags: + - ruby + - mysql + spec:benchmark: script: - RAILS_ENV=test bundle exec rake spec:benchmark diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 343e4b63524..0985ef3a669 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -28,6 +28,24 @@ namespace :spec do run_commands(cmds) end + desc 'GitLab | Rspec | Run service specs' + task :services do + cmds = [ + %W(rake gitlab:setup), + %W(rspec spec --tag @services) + ] + run_commands(cmds) + end + + desc 'GitLab | Rspec | Run lib specs' + task :lib do + cmds = [ + %W(rake gitlab:setup), + %W(rspec spec --tag @lib) + ] + run_commands(cmds) + end + desc 'GitLab | Rspec | Run benchmark specs' task :benchmark do cmds = [ @@ -41,7 +59,7 @@ namespace :spec do task :other do cmds = [ %W(rake gitlab:setup), - %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@benchmark) + %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services --tag ~@benchmark) ] run_commands(cmds) end -- cgit v1.2.1 From 3fd1a36d74c6af0ff040580f014505568d0b3aab Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 12:00:26 +0100 Subject: Fix spec --- spec/features/atom/users_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index dc41be8246f..52134556339 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -79,6 +79,6 @@ describe "User Feed", feature: true do end def safe_name - html_escape(user.name) + CGI.escapeHTML(user.name) end end -- cgit v1.2.1 From 64eaccf10a7ac70c3dc2f9565a638e9f45a8359c Mon Sep 17 00:00:00 2001 From: Felix Eckhofer Date: Wed, 9 Dec 2015 12:26:18 +0100 Subject: Update init script only once The init script is already being updated in section 6. --- doc/update/8.1-to-8.2.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 7b228d6a22f..b08a79ca0aa 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -85,11 +85,10 @@ sudo -u git -H git checkout 0.4.2 sudo -u git -H make ``` -Update the GitLab init script and 'default' file. +Update the GitLab 'default' file. ``` cd /home/git/gitlab -sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab test -e /etc/default/gitlab && \ sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab ``` -- cgit v1.2.1 From f7ff146aaac158ce2099583af5d5620204ec9b93 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 13:52:13 +0100 Subject: Fix size of disabled 'New file' button --- app/views/projects/tree/_tree_header.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 0e1f7076608..cefe33e581f 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -32,5 +32,5 @@ New directory - elsif !on_top_of_branch? %li - %span.btn.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}} + %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch.", data: {container: 'body'}} = icon('plus') -- cgit v1.2.1 From 9e5ac7285cc6b9b641d863457086b2e5292699e1 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Dec 2015 11:30:21 -0200 Subject: Ensure notes are replaced on a merge request diff when they're updated --- app/assets/javascripts/notes.js.coffee | 2 +- features/project/merge_requests.feature | 10 ++++++++++ features/steps/project/merge_requests.rb | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 533d00bfb0c..5fe0318de6f 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -286,7 +286,7 @@ class @Notes $html.find('.js-task-list-container').taskList('enable') # Find the note's `li` element by ID and replace it with the updated HTML - $note_li = $("#note_#{note.id}") + $note_li = $('.note-row-' + note.id) $note_li.replaceWith($html) ### diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 6cd081c868e..5a7f7f7c642 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -83,6 +83,16 @@ Feature: Project Merge Requests And I switch to the merge request's comments tab Then I should see a discussion has started on diff + @javascript + Scenario: I edit a comment on a merge request diff + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I click on the Changes tab + And I leave a comment like "Line is wrong" on diff + And I change the comment "Line is wrong" to "Typo, please fix" on diff + Then I should not see a diff comment saying "Line is wrong" + And I should see a diff comment saying "Typo, please fix" + @javascript Scenario: I comment on a line of a commit in merge request Given project "Shop" have "Bug NS-05" open merge request with diffs inside diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 822cf0ffe1c..74539d0b019 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -186,6 +186,31 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps leave_comment "Line is wrong" end + step 'I change the comment "Line is wrong" to "Typo, please fix" on diff' do + page.within('.diff-file:nth-of-type(5) .note') do + find('.js-note-edit').click + + page.within('.current-note-edit-form', visible: true) do + fill_in 'note_note', with: 'Typo, please fix' + click_button 'Save Comment' + end + + expect(page).not_to have_button 'Save Comment', disabled: true, visible: true + end + end + + step 'I should not see a diff comment saying "Line is wrong"' do + page.within('.diff-file:nth-of-type(5) .note') do + expect(page).not_to have_visible_content 'Line is wrong' + end + end + + step 'I should see a diff comment saying "Typo, please fix"' do + page.within('.diff-file:nth-of-type(5) .note') do + expect(page).to have_visible_content 'Typo, please fix' + end + end + step 'I should see a discussion has started on diff' do page.within(".notes .discussion") do page.should have_content "#{current_user.name} started a discussion" -- cgit v1.2.1 From 05f06955bad652fa68901d620c32b30969237c69 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Dec 2015 11:59:32 -0200 Subject: Ensure existing notes are highlighted properly on a merge request diff --- app/assets/javascripts/merge_request_tabs.js.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index b0eeb1db536..1f0b6d9ced0 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -144,7 +144,9 @@ class @MergeRequestTabs @_get url: "#{source}.json" + @_location.search success: (data) => - document.getElementById('diffs').innerHTML = data.html + html = $(data.html) + html.syntaxHighlight() + $('#diffs').html(html) @diffsLoaded = true @scrollToElement("#diffs") -- cgit v1.2.1 From 429932a8b40540faa4d5cbe39119bfeccddd7bab Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Dec 2015 12:16:24 -0200 Subject: Ensure new notes are highlighted properly on a merge request diff --- app/assets/javascripts/notes.js.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 5fe0318de6f..b1df56b24fe 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -148,6 +148,8 @@ class @Notes @note_ids.push(note.id) form = $("form[rel='" + note.discussion_id + "']") row = form.closest("tr") + note_html = $(note.html) + note_html.syntaxHighlight() # is this the first note of discussion? if row.is(".js-temp-notes-holder") @@ -158,14 +160,16 @@ class @Notes row.next().find(".note").remove() # Add note to 'Changes' page discussions - $(".notes[rel='" + note.discussion_id + "']").append note.html + $(".notes[rel='" + note.discussion_id + "']").append note_html # Init discussion on 'Discussion' page if it is merge request page if $('body').attr('data-page').indexOf('projects:merge_request') == 0 - $('ul.main-notes-list').append(note.discussion_with_diff_html) + discussion_html = $(note.discussion_with_diff_html) + discussion_html.syntaxHighlight() + $('ul.main-notes-list').append(discussion_html) else # append new note to all matching discussions - $(".notes[rel='" + note.discussion_id + "']").append note.html + $(".notes[rel='" + note.discussion_id + "']").append note_html # cleanup after successfully creating a diff/discussion note @removeDiscussionNoteForm(form) -- cgit v1.2.1 From 37199719c7da301fae368348df52f90f2af95863 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 9 Dec 2015 16:19:59 +0100 Subject: Make sure everyone has shared/lfs-objects --- shared/lfs-objects/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 shared/lfs-objects/.gitkeep diff --git a/shared/lfs-objects/.gitkeep b/shared/lfs-objects/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 7e8fc4822758057ed8a2239659cdd8f49099613d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Fri, 4 Dec 2015 16:54:25 -0500 Subject: Normalize email when looking for GitLab users from commit info. #3854 --- app/models/commit.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/commit.rb b/app/models/commit.rb index 8ae5325d16a..fa88a408fa3 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -175,11 +175,11 @@ class Commit end def author - @author ||= User.find_by_any_email(author_email) + @author ||= User.find_by_any_email(author_email.downcase) end def committer - @committer ||= User.find_by_any_email(committer_email) + @committer ||= User.find_by_any_email(committer_email.downcase) end def parents -- cgit v1.2.1 From 2cb542b9d365558923ed125f76c5005188dbee77 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Dec 2015 17:39:13 +0100 Subject: remove unnecessary code Signed-off-by: Dmitriy Zaporozhets --- app/views/projects/commits/_head.html.haml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index 2f604c82f11..fcccb002d7e 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -4,10 +4,9 @@ Commits %span.badge= number_with_delimiter(@repository.commit_count) - - if project_nav_tab? :network - = nav_link(controller: %w(network)) do - = link_to namespace_project_network_path(@project.namespace, @project, current_ref), class: 'shortcuts-network' do - Network + = nav_link(controller: %w(network)) do + = link_to namespace_project_network_path(@project.namespace, @project, current_ref) do + Network = nav_link(controller: :compare) do = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: current_ref) do -- cgit v1.2.1 From 2c61e3c057af02068bfb57f1c3f921d9f0c87b1d Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 9 Dec 2015 17:47:45 +0100 Subject: Fix test for network shortcut Signed-off-by: Dmitriy Zaporozhets --- features/project/shortcuts.feature | 3 ++- features/steps/project/active_tab.rb | 4 ---- features/steps/shared/project_tab.rb | 4 ++++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature index 0f71c32380b..10e7c234610 100644 --- a/features/project/shortcuts.feature +++ b/features/project/shortcuts.feature @@ -19,7 +19,8 @@ Feature: Project Shortcuts @javascript Scenario: Navigate to network tab Given I press "g" and "n" - Then the active main tab should be Network + Then the active sub tab should be Network + And the active main tab should be Commits @javascript Scenario: Navigate to graphs tab diff --git a/features/steps/project/active_tab.rb b/features/steps/project/active_tab.rb index 94a5dd744d0..9e96fa5ba49 100644 --- a/features/steps/project/active_tab.rb +++ b/features/steps/project/active_tab.rb @@ -67,10 +67,6 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ensure_active_sub_tab('Commits') end - step 'the active sub tab should be Network' do - ensure_active_sub_tab('Network') - end - step 'the active sub tab should be Compare' do ensure_active_sub_tab('Compare') end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb index 3bad28dc283..4fc2ece79ff 100644 --- a/features/steps/shared/project_tab.rb +++ b/features/steps/shared/project_tab.rb @@ -49,4 +49,8 @@ module SharedProjectTab step 'the active main tab should be Activity' do ensure_active_main_tab('Activity') end + + step 'the active sub tab should be Network' do + ensure_active_sub_tab('Network') + end end -- cgit v1.2.1 From 364041e7ddf56a16a90f35ec85e35c05e5758621 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 9 Dec 2015 14:48:00 -0200 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 59fe30746c6..08a40d8d4d0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.3.0 (unreleased) - Add languages page to graphs - Block LDAP user when they are no longer found in the LDAP server - Improve wording on project visibility levels (Zeger-Jan van de Weg) + - Fix editing notes on a merge request diff v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 3d4b4683ebca56973a0b5f56a558d9cac3c67ed9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 9 Dec 2015 12:08:44 -0500 Subject: Add CHANGELOG entry for !1998 [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3017c528380..b9a746c780c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.3.0 (unreleased) - Add languages page to graphs - Block LDAP user when they are no longer found in the LDAP server - Improve wording on project visibility levels (Zeger-Jan van de Weg) + - Automatically select default clone protocol based on user preferences (Eirik Lygre) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 7a2a860adcda179578098afb9b84894c1670f2c7 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 9 Dec 2015 12:09:00 -0500 Subject: Simplify shared User SSH key steps --- features/steps/shared/user.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb index 33c146f6dbc..f0721094ee3 100644 --- a/features/steps/shared/user.rb +++ b/features/steps/shared/user.rb @@ -20,10 +20,10 @@ module SharedUser end step 'I have an ssh key' do - create(:key, user: @user, title: "An ssh-key", key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work") + create(:personal_key, user: @user) end step 'I have no ssh keys' do - Key.delete_all + @user.keys.delete_all end end -- cgit v1.2.1 From 8d5527007a3cca93dabe7324f8fffbabdc7664d0 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 20:52:13 -0800 Subject: Provide better diagnostic message upon project creation errors Prevents an Error 500 when project fails to create due to invalid parameters. See #3937 --- CHANGELOG | 1 + app/services/projects/create_service.rb | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3017c528380..dd1cdc7549b 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.3.0 (unreleased) - Merge when build succeeds (Zeger-Jan van de Weg) + - Provide better diagnostic message upon project creation errors (Stan Hu) - Bump gollum-lib to 4.1.0 (Stan Hu) - Fix broken group avatar upload under "New group" (Stan Hu) - Update project repositorize size and commit count during import:repos task (Stan Hu) diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 700a1db04d8..a6820183bee 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -64,8 +64,10 @@ module Projects after_create_actions if @project.persisted? @project - rescue - @project.errors.add(:base, "Can't save project. Please try again later") + rescue => e + message = "Unable to save project: #{e.message}" + Rails.logger.error(message) + @project.errors.add(:base, message) if @project @project end -- cgit v1.2.1 From 99fd78d6db9ca444853fc9903bdd203f6113b78b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 8 Dec 2015 21:49:24 -0800 Subject: Add spec for invalid options in project creation --- spec/services/projects/create_service_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index 2d8c316e38d..5d0b18558b1 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -49,6 +49,13 @@ describe Projects::CreateService, services: true do it { expect(@project.namespace).to eq(@group) } end + context 'error handling' do + it 'handles invalid options' do + @opts.merge!({ default_branch: 'master' } ) + expect(create_project(@user, @opts)).to eq(nil) + end + end + context 'wiki_enabled creates repository directory' do context 'wiki_enabled true creates wiki repository directory' do before do -- cgit v1.2.1 From 86aa535562ea627ccea93bcdf7e63468a4a148bf Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 9 Dec 2015 13:35:20 -0500 Subject: Run db:reset before db:create on CI --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 39d8f59cf07..c614e14e243 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ before_script: - touch log/application.log - touch log/test.log - bundle install --without postgres production --jobs $(nproc) "${FLAGS[@]}" - - bundle exec rake db:create RAILS_ENV=test + - bundle exec rake db:reset db:create RAILS_ENV=test spec:feature: script: -- cgit v1.2.1 From 98958decce9e3a5764de609f0c730c7c5fde65d6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 9 Dec 2015 13:55:31 -0500 Subject: Add number_with_delimiter to build counts --- app/views/projects/builds/index.html.haml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 742676305a9..fbf2c293db8 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -12,17 +12,20 @@ %li{class: ('active' if @scope.nil?)} = link_to project_builds_path(@project) do Running - %span.badge.js-running-count= @all_builds.running_or_pending.count(:id) + %span.badge.js-running-count + = number_with_delimiter(@all_builds.running_or_pending.count(:id)) %li{class: ('active' if @scope == 'finished')} = link_to project_builds_path(@project, scope: :finished) do Finished - %span.badge.js-running-count= @all_builds.finished.count(:id) + %span.badge.js-running-count + = number_with_delimiter(@all_builds.finished.count(:id)) %li{class: ('active' if @scope == 'all')} = link_to project_builds_path(@project, scope: :all) do All - %span.badge.js-totalbuilds-count= @all_builds.count(:id) + %span.badge.js-totalbuilds-count + = number_with_delimiter(@all_builds.count(:id)) .gray-content-block #{(@scope || 'running').capitalize} builds from this project -- cgit v1.2.1 From 7b50965e9990bcb88f56b771d47514cbeb5316e5 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 9 Dec 2015 14:21:08 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b9a746c780c..01f4c386e1a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,7 +20,6 @@ v 8.3.0 (unreleased) - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info - Fix: Ensure "Remove Source Branch" button is not shown when branch is being deleted. #3583 - - Fix 500 error when creating a merge request that removes a submodule - Run custom Git hooks when branch is created or deleted. - Fix bug when simultaneously accepting multiple MRs results in MRs that are of "merged" status, but not merged to the target branch - Add languages page to graphs @@ -31,9 +30,10 @@ v 8.3.0 (unreleased) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) - -v 8.2.3 + - Update documentation for "Guest" permissions + - Properly convert Emoji-only comments into Award Emojis - Webhook payload has an added, modified and removed properties for each commit + - Fix 500 error when creating a merge request that removes a submodule v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) @@ -94,7 +94,6 @@ v 8.2.0 - Add email notification to former assignee upon unassignment (Adam Lieskovský) - New design for project graphs page - Remove deprecated dumped yaml file generated from previous job definitions - - Fix incoming email config defaults - Show specific runners from projects where user is master or owner - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page @@ -252,7 +251,6 @@ v 8.0.2 - Allow AWS S3 Server-Side Encryption with Amazon S3-Managed Keys for backups (Paul Beattie) v 8.0.1 - - Remove git refs used internally by GitLab from network graph (Stan Hu) - Improve CI migration procedure and documentation v 8.0.0 -- cgit v1.2.1 From c5dacce4d7e47a0504975fbb3bfaf478b95f1065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 9 Dec 2015 19:50:00 +0000 Subject: Use YAML.safe_load --- lib/ci/gitlab_ci_yaml_processor.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 3beafcad117..7f54f5f0722 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -10,7 +10,7 @@ module Ci attr_reader :before_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) - @config = YAML.load(config) + @config = YAML.safe_load(config) @path = path unless @config.is_a? Hash @@ -250,4 +250,4 @@ module Ci end end end -end +end \ No newline at end of file -- cgit v1.2.1 From cbac31d923c39d71b162250f893d716073cc08e0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 8 Dec 2015 17:43:03 -0500 Subject: Add copy-to-clipboard button to Snippets#show Closes #3701 --- CHANGELOG | 1 + app/assets/stylesheets/pages/snippets.scss | 6 ++++++ app/views/projects/snippets/show.html.haml | 4 ++-- app/views/shared/_file_highlight.html.haml | 5 +++-- app/views/snippets/show.html.haml | 4 ++-- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 01f4c386e1a..a9ee1435e48 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -26,6 +26,7 @@ v 8.3.0 (unreleased) - Block LDAP user when they are no longer found in the LDAP server - Improve wording on project visibility levels (Zeger-Jan van de Weg) - Automatically select default clone protocol based on user preferences (Eirik Lygre) + - Add copy-to-clipboard button for Snippets v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss index bb74e50151d..1430d01859d 100644 --- a/app/assets/stylesheets/pages/snippets.scss +++ b/app/assets/stylesheets/pages/snippets.scss @@ -32,6 +32,12 @@ .file-holder { border-top: 0; } + + .file-actions { + .btn-clipboard { + @extend .btn; + } + } } .snippet-box { diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 5d706942f2d..7c599563ce4 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -10,8 +10,8 @@ %strong = @snippet.file_name .file-actions.hidden-xs - .btn-group.tree-btn-group - = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" + = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']") + = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank" = render 'shared/snippets/blob' diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index 57c3aff3e18..2bc98983d67 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -8,5 +8,6 @@ %a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} %i.fa.fa-link = i - :preserve - #{highlight(blob.name, blob.data)} + .blob-content{data: {blob_id: blob.id}} + :preserve + #{highlight(blob.name, blob.data)} diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 69d8899d4c1..a2b36568770 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -9,6 +9,6 @@ %strong = @snippet.file_name .file-actions.hidden-xs - .btn-group.tree-btn-group - = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" + = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']") + = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank" = render 'shared/snippets/blob' -- cgit v1.2.1 From b4b9df277bb1490ba04a976fb2a59f2a0f603173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Trzci=C5=84ski?= Date: Wed, 9 Dec 2015 20:58:53 +0000 Subject: Allow [Symbol] when loading YAML --- lib/ci/gitlab_ci_yaml_processor.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 7f54f5f0722..e7fb1b79b7c 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -10,7 +10,7 @@ module Ci attr_reader :before_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) - @config = YAML.safe_load(config) + @config = YAML.safe_load(config, [Symbol]) @path = path unless @config.is_a? Hash @@ -250,4 +250,4 @@ module Ci end end end -end \ No newline at end of file +end -- cgit v1.2.1 From 48bf0a34e2771096b175555b47133b2573faa4d8 Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 9 Dec 2015 16:11:21 -0500 Subject: "No ssh" message should be same on project page --- app/views/projects/empty.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 950ab33825e..503d156661e 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,4 +1,4 @@ -.alert_holder += content_for :flash_message do - if current_user && can?(current_user, :download_code, @project) = render 'shared/no_ssh' = render 'shared/no_password' -- cgit v1.2.1 From ab8ab9b10c6bda35d3a82e0cf52f45c1cd81eb09 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 01:24:10 +0100 Subject: Fix list with controls display Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/lists.scss | 44 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 481e3f33a20..cc48f8c8166 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -7,7 +7,7 @@ padding: 0; list-style: none; - li { + > li { padding: 10px 15px; min-height: 20px; border-bottom: 1px solid #eee; @@ -123,24 +123,6 @@ ul.content-list { padding: 10px 14px; } } - - ul.controls { - list-style: none; - - li { - float: left; - padding-right: 10px; - - .author_link { - display: inline-block; - - .avatar-inline { - margin-left: 0; - margin-right: 0; - } - } - } - } } } @@ -149,3 +131,27 @@ ul.content-list { margin: 0; } } + +ul.controls { + padding-top: 1px; + float: right; + list-style: none; + + .btn { + padding: 10px 14px; + } + + > li { + float: left; + padding-right: 10px; + + .author_link { + display: inline-block; + + .avatar-inline { + margin-left: 0; + margin-right: 0; + } + } + } +} -- cgit v1.2.1 From f4ec906e90b2f8dbf18b359b773e3b31f5da89ff Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 9 Dec 2015 11:45:26 -0600 Subject: Use devise paranoid mode and ensure the same message is returned every time Skipped CI because it has already passed. Had to rebase due to CHANGELOG. --- CHANGELOG | 3 +++ app/controllers/passwords_controller.rb | 6 ++++-- config/initializers/devise.rb | 2 +- config/locales/devise.en.yml | 1 - spec/features/password_reset_spec.rb | 26 +++++++++++++------------- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 01f4c386e1a..03574b1e771 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,6 +32,9 @@ v 8.2.3 - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) - Update documentation for "Guest" permissions - Properly convert Emoji-only comments into Award Emojis + - Enable devise paranoid mode to prevent user enumeration attack + +v 8.2.3 - Webhook payload has an added, modified and removed properties for each commit - Fix 500 error when creating a merge request that removes a submodule diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 2025158d065..f74daff3bd0 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -40,7 +40,9 @@ class PasswordsController < Devise::PasswordsController def throttle_reset return unless resource && resource.recently_sent_password_reset? - redirect_to new_password_path(resource_name), - alert: I18n.t('devise.passwords.recently_reset') + # Throttle reset attempts, but return a normal message to + # avoid user enumeration attack. + redirect_to new_user_session_path, + notice: I18n.t('devise.passwords.send_paranoid_instructions') end end diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 29506970af2..5fb43a86e13 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -60,7 +60,7 @@ Devise.setup do |config| # It will change confirmation, password recovery and other workflows # to behave the same regardless if the e-mail provided was right or wrong. # Does not affect registerable. - # config.paranoid = true + config.paranoid = true # ==> Configuration for :database_authenticatable # For bcrypt, this is the cost for hashing the password and defaults to 10. If diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 22070e37f07..bd4c3ebc69e 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -30,7 +30,6 @@ en: success: "Successfully authenticated from %{kind} account." passwords: no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." - recently_reset: "Instructions about how to reset your password have already been sent recently. Please wait a few minutes to try again." send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." updated: "Your password has been changed successfully. You are now signed in." diff --git a/spec/features/password_reset_spec.rb b/spec/features/password_reset_spec.rb index 85e70b4d47f..257d363438c 100644 --- a/spec/features/password_reset_spec.rb +++ b/spec/features/password_reset_spec.rb @@ -3,11 +3,12 @@ require 'spec_helper' feature 'Password reset', feature: true do describe 'throttling' do it 'sends reset instructions when not previously sent' do - visit root_path - forgot_password(create(:user)) + user = create(:user) + forgot_password(user) - expect(page).to have_content(I18n.t('devise.passwords.send_instructions')) + expect(page).to have_content(I18n.t('devise.passwords.send_paranoid_instructions')) expect(current_path).to eq new_user_session_path + expect(user.recently_sent_password_reset?).to be_truthy end it 'sends reset instructions when previously sent more than a minute ago' do @@ -15,26 +16,25 @@ feature 'Password reset', feature: true do user.send_reset_password_instructions user.update_attribute(:reset_password_sent_at, 5.minutes.ago) - visit root_path - forgot_password(user) - - expect(page).to have_content(I18n.t('devise.passwords.send_instructions')) + expect{ forgot_password(user) }.to change{ user.reset_password_sent_at } + expect(page).to have_content(I18n.t('devise.passwords.send_paranoid_instructions')) expect(current_path).to eq new_user_session_path end - it "throttles multiple resets in a short timespan" do + it 'throttles multiple resets in a short timespan' do user = create(:user) user.send_reset_password_instructions + # Reload because PG handles datetime less precisely than Ruby/Rails + user.reload - visit root_path - forgot_password(user) - - expect(page).to have_content(I18n.t('devise.passwords.recently_reset')) - expect(current_path).to eq new_user_password_path + expect{ forgot_password(user) }.not_to change{ user.reset_password_sent_at } + expect(page).to have_content(I18n.t('devise.passwords.send_paranoid_instructions')) + expect(current_path).to eq new_user_session_path end end def forgot_password(user) + visit root_path click_on 'Forgot your password?' fill_in 'Email', with: user.email click_button 'Reset password' -- cgit v1.2.1 From e3ee46a13b91a6cefb0efb1841fb24afed37b674 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 10 Dec 2015 14:36:31 +0200 Subject: Don't allow to edit award emoji comments --- CHANGELOG | 1 + app/models/note.rb | 2 +- spec/models/note_spec.rb | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 144b3487714..776f86c0e07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.3.0 (unreleased) - Improve wording on project visibility levels (Zeger-Jan van de Weg) - Automatically select default clone protocol based on user preferences (Eirik Lygre) - Make Network page as sub tab of Commits + - Prevent possible XSS attack with award-emoji v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/models/note.rb b/app/models/note.rb index 98c29ddc4cd..0f7efc2f2ab 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -350,7 +350,7 @@ class Note < ActiveRecord::Base end def editable? - !system? + !system? && !is_award end # Checks if note is an award added as a comment diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index cd3c868ecc5..5b6f177ebb2 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -142,4 +142,21 @@ describe Note, models: true do expect(Note.grouped_awards.first.last).to match_array(Note.all) end end + + describe "editable?" do + it "returns true" do + note = build(:note) + expect(note.editable?).to be_truthy + end + + it "returns false" do + note = build(:note, system: true) + expect(note.editable?).to be_falsy + end + + it "returns false" do + note = build(:note, is_award: true, note: "smiley") + expect(note.editable?).to be_falsy + end + end end -- cgit v1.2.1 From 57af1231f2e99ab525d8c1fda2e0888e41a873a5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 10 Dec 2015 15:46:29 +0100 Subject: Add missing changelog items --- CHANGELOG | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fc1cb1a996f..18a9d317cc4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.3.0 (unreleased) - - Merge when build succeeds (Zeger-Jan van de Weg) + - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Bump gollum-lib to 4.1.0 (Stan Hu) - Fix broken group avatar upload under "New group" (Stan Hu) - Update project repositorize size and commit count during import:repos task (Stan Hu) @@ -28,6 +28,13 @@ v 8.3.0 (unreleased) - Automatically select default clone protocol based on user preferences (Eirik Lygre) - Make Network page as sub tab of Commits - Add copy-to-clipboard button for Snippets + - Add indication to merge request list item that MR cannot be merged automatically + - Default target branch to patch-n when editing file in protected branch + - Add Builds tab to merge request detail page + - Allow milestones, issues and MRs to be created from dashboard and group indexes + - Use new style for wiki + - Use new style for milestone detail page + - Fix sidebar tooltips when collapsed v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 2988e1fbf50b3c9e803a9358933e3e969e64dcc3 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 7 Dec 2015 13:23:23 +0100 Subject: Migrate CI::Services and CI::WebHooks to Services and WebHooks --- CHANGELOG | 1 + app/controllers/projects/ci_services_controller.rb | 49 ------ .../projects/ci_web_hooks_controller.rb | 45 ------ app/controllers/projects/hooks_controller.rb | 3 +- app/controllers/projects/services_controller.rb | 4 +- app/mailers/ci/emails/builds.rb | 17 -- app/mailers/ci/notify.rb | 46 ------ app/mailers/emails/builds.rb | 15 ++ app/mailers/notify.rb | 1 + app/models/ci/build.rb | 20 ++- app/models/ci/commit.rb | 4 + app/models/ci/project.rb | 41 ----- app/models/ci/service.rb | 105 ------------ app/models/ci/web_hook.rb | 43 ----- app/models/hooks/project_hook.rb | 1 + app/models/hooks/web_hook.rb | 1 + app/models/project.rb | 1 + .../project_services/builds_email_service.rb | 81 ++++++++++ app/models/project_services/ci/hip_chat_message.rb | 73 --------- app/models/project_services/ci/hip_chat_service.rb | 93 ----------- app/models/project_services/ci/mail_service.rb | 84 ---------- app/models/project_services/ci/slack_message.rb | 92 ----------- app/models/project_services/ci/slack_service.rb | 81 ---------- app/models/project_services/hipchat_service.rb | 42 ++++- app/models/project_services/slack_service.rb | 22 ++- .../project_services/slack_service/base_message.rb | 3 + .../slack_service/build_message.rb | 82 ++++++++++ app/models/service.rb | 18 +++ app/services/ci/create_builds_service.rb | 3 +- app/views/ci/notify/build_fail_email.html.haml | 23 --- app/views/ci/notify/build_fail_email.text.erb | 11 -- app/views/ci/notify/build_success_email.html.haml | 24 --- app/views/ci/notify/build_success_email.text.erb | 11 -- app/views/layouts/nav/_project_settings.html.haml | 10 -- app/views/notify/build_fail_email.html.haml | 23 +++ app/views/notify/build_fail_email.text.erb | 11 ++ app/views/notify/build_success_email.html.haml | 24 +++ app/views/notify/build_success_email.text.erb | 11 ++ app/views/projects/ci_services/_form.html.haml | 54 ------- app/views/projects/ci_services/edit.html.haml | 2 - app/views/projects/ci_services/index.html.haml | 23 --- app/views/projects/ci_web_hooks/index.html.haml | 94 ----------- app/views/projects/hooks/index.html.haml | 9 +- app/views/shared/_service_settings.html.haml | 9 ++ app/workers/build_email_worker.rb | 19 +++ app/workers/ci/hip_chat_notifier_worker.rb | 19 --- app/workers/ci/slack_notifier_worker.rb | 10 -- app/workers/ci/web_hook_worker.rb | 9 -- config/routes.rb | 11 -- .../20151203162134_add_build_events_to_services.rb | 6 + db/schema.rb | 4 +- features/project/service.feature | 8 +- features/steps/admin/settings.rb | 2 + features/steps/project/services.rb | 8 +- lib/api/entities.rb | 5 +- lib/api/project_hooks.rb | 2 + lib/ci/api/projects.rb | 38 ----- lib/gitlab/build_data_builder.rb | 64 ++++++++ spec/factories/ci/web_hook.rb | 6 - spec/features/ci_web_hooks_spec.rb | 27 ---- spec/lib/gitlab/build_data_builder_spec.rb | 20 +++ spec/mailers/ci/notify_spec.rb | 35 ---- spec/mailers/notify_spec.rb | 29 ++++ .../ci/project_services/hip_chat_message_spec.rb | 39 ----- .../ci/project_services/hip_chat_service_spec.rb | 73 --------- .../ci/project_services/mail_service_spec.rb | 177 --------------------- .../ci/project_services/slack_message_spec.rb | 43 ----- .../ci/project_services/slack_service_spec.rb | 57 ------- spec/models/ci/project_spec.rb | 2 - spec/models/ci/service_spec.rb | 48 ------ spec/models/ci/web_hook_spec.rb | 63 -------- .../project_services/hipchat_service_spec.rb | 49 ++++++ .../slack_service/build_message_spec.rb | 46 ++++++ spec/requests/api/project_hooks_spec.rb | 12 +- spec/requests/ci/api/projects_spec.rb | 73 --------- spec/services/ci/web_hook_service_spec.rb | 37 ----- spec/workers/build_email_worker_spec.rb | 35 ++++ 77 files changed, 669 insertions(+), 1817 deletions(-) delete mode 100644 app/controllers/projects/ci_services_controller.rb delete mode 100644 app/controllers/projects/ci_web_hooks_controller.rb delete mode 100644 app/mailers/ci/emails/builds.rb delete mode 100644 app/mailers/ci/notify.rb create mode 100644 app/mailers/emails/builds.rb delete mode 100644 app/models/ci/service.rb delete mode 100644 app/models/ci/web_hook.rb create mode 100644 app/models/project_services/builds_email_service.rb delete mode 100644 app/models/project_services/ci/hip_chat_message.rb delete mode 100644 app/models/project_services/ci/hip_chat_service.rb delete mode 100644 app/models/project_services/ci/mail_service.rb delete mode 100644 app/models/project_services/ci/slack_message.rb delete mode 100644 app/models/project_services/ci/slack_service.rb create mode 100644 app/models/project_services/slack_service/build_message.rb delete mode 100644 app/views/ci/notify/build_fail_email.html.haml delete mode 100644 app/views/ci/notify/build_fail_email.text.erb delete mode 100644 app/views/ci/notify/build_success_email.html.haml delete mode 100644 app/views/ci/notify/build_success_email.text.erb create mode 100644 app/views/notify/build_fail_email.html.haml create mode 100644 app/views/notify/build_fail_email.text.erb create mode 100644 app/views/notify/build_success_email.html.haml create mode 100644 app/views/notify/build_success_email.text.erb delete mode 100644 app/views/projects/ci_services/_form.html.haml delete mode 100644 app/views/projects/ci_services/edit.html.haml delete mode 100644 app/views/projects/ci_services/index.html.haml delete mode 100644 app/views/projects/ci_web_hooks/index.html.haml create mode 100644 app/workers/build_email_worker.rb delete mode 100644 app/workers/ci/hip_chat_notifier_worker.rb delete mode 100644 app/workers/ci/slack_notifier_worker.rb delete mode 100644 app/workers/ci/web_hook_worker.rb create mode 100644 db/migrate/20151203162134_add_build_events_to_services.rb create mode 100644 lib/gitlab/build_data_builder.rb delete mode 100644 spec/factories/ci/web_hook.rb delete mode 100644 spec/features/ci_web_hooks_spec.rb create mode 100644 spec/lib/gitlab/build_data_builder_spec.rb delete mode 100644 spec/mailers/ci/notify_spec.rb delete mode 100644 spec/models/ci/project_services/hip_chat_message_spec.rb delete mode 100644 spec/models/ci/project_services/hip_chat_service_spec.rb delete mode 100644 spec/models/ci/project_services/mail_service_spec.rb delete mode 100644 spec/models/ci/project_services/slack_message_spec.rb delete mode 100644 spec/models/ci/project_services/slack_service_spec.rb delete mode 100644 spec/models/ci/service_spec.rb delete mode 100644 spec/models/ci/web_hook_spec.rb create mode 100644 spec/models/project_services/slack_service/build_message_spec.rb delete mode 100644 spec/services/ci/web_hook_service_spec.rb create mode 100644 spec/workers/build_email_worker_spec.rb diff --git a/CHANGELOG b/CHANGELOG index fc1cb1a996f..2f94eb6c66c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.3.0 (unreleased) - Fire update hook from GitLab - Style warning about mentioning many people in a comment - Fix: sort milestones by due date once again (Greg Smethells) + - Migrate all CI::Services and CI::WebHooks to Services and WebHooks - Don't show project fork event as "imported" - Add API endpoint to fetch merge request commits list - Expose events API with comment information and author info diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb deleted file mode 100644 index 550a019e8e2..00000000000 --- a/app/controllers/projects/ci_services_controller.rb +++ /dev/null @@ -1,49 +0,0 @@ -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.namespace, @project, service.to_param) - else - render 'edit' - end - end - - def test - last_build = @project.ci_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_back_or_default(options: 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/controllers/projects/ci_web_hooks_controller.rb b/app/controllers/projects/ci_web_hooks_controller.rb deleted file mode 100644 index a2d470d4a69..00000000000 --- a/app/controllers/projects/ci_web_hooks_controller.rb +++ /dev/null @@ -1,45 +0,0 @@ -class Projects::CiWebHooksController < Projects::ApplicationController - before_action :ci_project - before_action :authorize_admin_project! - - layout "project_settings" - - def index - @web_hooks = @ci_project.web_hooks - @web_hook = Ci::WebHook.new - end - - def create - @web_hook = @ci_project.web_hooks.new(web_hook_params) - @web_hook.save - - if @web_hook.valid? - redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project) - else - @web_hooks = @ci_project.web_hooks.select(&:persisted?) - render :index - end - end - - def test - Ci::TestHookService.new.execute(hook, current_user) - - redirect_back_or_default(default: { action: 'index' }) - end - - def destroy - hook.destroy - - redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project) - end - - private - - def hook - @web_hook ||= @ci_project.web_hooks.find(params[:id]) - end - - def web_hook_params - params.require(:web_hook).permit(:url) - end -end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 6a62880cb71..5fd4f855dec 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -53,6 +53,7 @@ class Projects::HooksController < Projects::ApplicationController def hook_params params.require(:hook).permit(:url, :push_events, :issues_events, - :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification) + :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification) end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 42dbb497e01..6e7590260ff 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -6,7 +6,9 @@ class Projects::ServicesController < Projects::ApplicationController :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, :colorize_messages, :channels, :push_events, :issues_events, :merge_requests_events, :tag_push_events, - :note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url, + :note_events, :build_events, + :notify_only_broken_builds, :add_pusher, + :send_from_committer_email, :disable_diffs, :external_wiki_url, :notify, :color, :server_host, :server_port, :default_irc_uri, :enable_ssl_verification] diff --git a/app/mailers/ci/emails/builds.rb b/app/mailers/ci/emails/builds.rb deleted file mode 100644 index 6fb4fba85e5..00000000000 --- a/app/mailers/ci/emails/builds.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Ci - module Emails - module Builds - def build_fail_email(build_id, to) - @build = Ci::Build.find(build_id) - @project = @build.project - mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha)) - end - - def build_success_email(build_id, to) - @build = Ci::Build.find(build_id) - @project = @build.project - mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha)) - end - end - end -end diff --git a/app/mailers/ci/notify.rb b/app/mailers/ci/notify.rb deleted file mode 100644 index 404842cf213..00000000000 --- a/app/mailers/ci/notify.rb +++ /dev/null @@ -1,46 +0,0 @@ -module Ci - class Notify < ActionMailer::Base - include Ci::Emails::Builds - - add_template_helper Ci::GitlabHelper - - default_url_options[:host] = Gitlab.config.gitlab.host - default_url_options[:protocol] = Gitlab.config.gitlab.protocol - default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port? - default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root - - default from: Gitlab.config.gitlab.email_from - - # Just send email with 3 seconds delay - def self.delay - delay_for(2.seconds) - end - - private - - # Formats arguments into a String suitable for use as an email subject - # - # extra - Extra Strings to be inserted into the subject - # - # Examples - # - # >> subject('Lorem ipsum') - # => "GitLab-CI | Lorem ipsum" - # - # # Automatically inserts Project name when @project is set - # >> @project = Project.last - # => # - # >> subject('Lorem ipsum') - # => "GitLab-CI | Ruby on Rails | Lorem ipsum " - # - # # Accepts multiple arguments - # >> subject('Lorem ipsum', 'Dolor sit amet') - # => "GitLab-CI | Lorem ipsum | Dolor sit amet" - def subject(*extra) - subject = "GitLab-CI" - subject << (@project ? " | #{@project.name}" : "") - subject << " | " + extra.join(' | ') if extra.present? - subject - end - end -end diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb new file mode 100644 index 00000000000..d58609a2de5 --- /dev/null +++ b/app/mailers/emails/builds.rb @@ -0,0 +1,15 @@ +module Emails + module Builds + def build_fail_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha)) + end + + def build_success_email(build_id, to) + @build = Ci::Build.find(build_id) + @project = @build.project + mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha)) + end + end +end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 50a409c3754..874769e239a 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -7,6 +7,7 @@ class Notify < BaseMailer include Emails::Projects include Emails::Profile include Emails::Groups + include Emails::Builds add_template_helper MergeRequestsHelper add_template_helper EmailsHelper diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 52ce1b920fa..564041e3214 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -96,21 +96,21 @@ module Ci end state_machine :status, initial: :pending do + after_transition pending: :running do |build, transition| + build.execute_hooks + end + after_transition any => [:success, :failed, :canceled] do |build, transition| return unless build.gl_project project = build.project - if project.web_hooks? - Ci::WebHookService.new.build_end(build) - end - - build.commit.create_next_builds(build) - project.execute_services(build) - if project.coverage_enabled? build.update_coverage end + + build.commit.create_next_builds(build) + build.execute_hooks end end @@ -275,6 +275,12 @@ module Ci end end + def execute_hooks + build_data = Gitlab::BuildDataBuilder.build(self) + gl_project.execute_hooks(build_data.dup, :build_hooks) + gl_project.execute_services(build_data.dup, :build_hooks) + end + private def yaml_variables diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 75465685e98..e63f7790946 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -178,6 +178,10 @@ module Ci duration_array.reduce(:+).to_i end + def started_at + @started_at ||= statuses.order('started_at ASC').first.try(:started_at) + end + def finished_at @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at) end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb index 669ee1cc0d2..79ff7e1dcd4 100644 --- a/app/models/ci/project.rb +++ b/app/models/ci/project.rb @@ -35,17 +35,10 @@ module Ci has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' - has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook' has_many :events, dependent: :destroy, class_name: 'Ci::Event' has_many :variables, dependent: :destroy, class_name: 'Ci::Variable' has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - # Project services - has_many :services, dependent: :destroy, class_name: 'Ci::Service' - has_one :hip_chat_service, dependent: :destroy, class_name: 'Ci::HipChatService' - has_one :slack_service, dependent: :destroy, class_name: 'Ci::SlackService' - has_one :mail_service, dependent: :destroy, class_name: 'Ci::MailService' - accepts_nested_attributes_for :variables, allow_destroy: true delegate :name_with_namespace, :path_with_namespace, :web_url, :http_url_to_repo, :ssh_url_to_repo, to: :gl_project @@ -122,14 +115,6 @@ module Ci email_add_pusher || email_recipients.present? end - def web_hooks? - web_hooks.any? - end - - def services? - services.any? - end - def timeout_in_minutes timeout / 60 end @@ -151,32 +136,6 @@ module Ci end end - def available_services_names - %w(slack mail hip_chat) - end - - def build_missing_services - available_services_names.each do |service_name| - service = services.find { |service| service.to_param == service_name } - - # If service is available but missing in db - # we should create an instance. Ex `create_gitlab_ci_service` - self.send :"create_#{service_name}_service" if service.nil? - end - end - - def execute_services(data) - services.each do |service| - - # Call service hook only if it is active - begin - service.execute(data) if service.active && service.can_execute?(data) - rescue => e - logger.error(e) - end - end - end - def setup_finished? commits.any? end diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb deleted file mode 100644 index 8063c51e82b..00000000000 --- a/app/models/ci/service.rb +++ /dev/null @@ -1,105 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -# To add new service you should build a class inherited from Service -# and implement a set of methods -module Ci - class Service < ActiveRecord::Base - extend Ci::Model - - serialize :properties, JSON - - default_value_for :active, false - - after_initialize :initialize_properties - - belongs_to :project, class_name: 'Ci::Project' - - validates :project_id, presence: true - - def activated? - active - end - - def category - :common - end - - def initialize_properties - self.properties = {} if properties.nil? - end - - def title - # implement inside child - end - - def description - # implement inside child - end - - def help - # implement inside child - end - - def to_param - # implement inside child - end - - def fields - # implement inside child - [] - end - - def can_test? - project.builds.any? - end - - def can_execute?(build) - true - end - - def execute(build) - # implement inside child - end - - # Provide convenient accessor methods - # for each serialized property. - def self.prop_accessor(*args) - args.each do |arg| - class_eval %{ - def #{arg} - (properties || {})['#{arg}'] - end - - def #{arg}=(value) - self.properties ||= {} - self.properties['#{arg}'] = value - end - } - end - end - - def self.boolean_accessor(*args) - self.prop_accessor(*args) - - args.each do |arg| - class_eval %{ - def #{arg}? - ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) - end - } - end - end - end -end diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb deleted file mode 100644 index 0dc15eb6683..00000000000 --- a/app/models/ci/web_hook.rb +++ /dev/null @@ -1,43 +0,0 @@ -# == Schema Information -# -# Table name: ci_web_hooks -# -# id :integer not null, primary key -# url :string(255) not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -module Ci - class WebHook < ActiveRecord::Base - extend Ci::Model - - include HTTParty - - belongs_to :project, class_name: 'Ci::Project' - - # HTTParty timeout - default_timeout 10 - - validates :url, presence: true, url: true - - def execute(data) - parsed_url = URI.parse(url) - if parsed_url.userinfo.blank? - Ci::WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false) - else - post_url = url.gsub("#{parsed_url.userinfo}@", "") - auth = { - username: URI.decode(parsed_url.user), - password: URI.decode(parsed_url.password), - } - Ci::WebHook.post(post_url, - body: data.to_json, - headers: { "Content-Type" => "application/json" }, - verify: false, - basic_auth: auth) - end - end - end -end diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb index 337b3097126..22638057773 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -25,4 +25,5 @@ class ProjectHook < WebHook scope :issue_hooks, -> { where(issues_events: true) } scope :note_hooks, -> { where(note_events: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true) } + scope :build_hooks, -> { where(build_events: true) } end diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 715ec5908b7..40eb0e20b4b 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -26,6 +26,7 @@ class WebHook < ActiveRecord::Base default_value_for :note_events, false default_value_for :merge_requests_events, false default_value_for :tag_push_events, false + default_value_for :build_events, false default_value_for :enable_ssl_verification, true # HTTParty timeout diff --git a/app/models/project.rb b/app/models/project.rb index e78868af1cc..60ca2cad6ac 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,6 +81,7 @@ class Project < ActiveRecord::Base has_one :campfire_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy + has_one :builds_email_service, dependent: :destroy has_one :irker_service, dependent: :destroy has_one :pivotaltracker_service, dependent: :destroy has_one :hipchat_service, dependent: :destroy diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb new file mode 100644 index 00000000000..9c9b5a4d08c --- /dev/null +++ b/app/models/project_services/builds_email_service.rb @@ -0,0 +1,81 @@ +# == 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 +# + +class BuildsEmailService < Service + prop_accessor :recipients + boolean_accessor :add_pusher + boolean_accessor :notify_only_broken_builds + validates :recipients, presence: true, if: :activated? + + def title + 'Builds emails' + end + + def description + 'Email the builds status to a list of recipients.' + end + + def to_param + 'builds_email' + end + + def supported_events + %w(build) + end + + def execute(push_data) + return unless supported_events.include?(push_data[:object_kind]) + + if should_build_be_notified?(push_data) + BuildEmailWorker.perform_async( + push_data[:build_id], + all_recipients(push_data), + push_data, + ) + end + end + + def fields + [ + { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' }, + { type: 'checkbox', name: 'add_pusher', label: 'Add pusher to recipients list' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + ] + end + + def should_build_be_notified?(data) + case data[:build_status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end + + def all_recipients(data) + if add_pusher? && data[:user][:email] + recipients + " #{data[:user][:email]}" + else + recipients + end + end +end diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb deleted file mode 100644 index d89466b689f..00000000000 --- a/app/models/project_services/ci/hip_chat_message.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Ci - class HipChatMessage - include Gitlab::Application.routes.url_helpers - - attr_reader :build - - def initialize(build) - @build = build - end - - def to_s - lines = Array.new - lines.push("#{project.name} - ") - lines.push("Commit ##{commit.id}
    ") - lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}
    ") - lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).") - lines.join('') - end - - def status_color(build_or_commit=nil) - build_or_commit ||= commit_status - case build_or_commit - when :success - 'green' - when :failed, :canceled - 'red' - else # :pending, :running or unknown - 'yellow' - end - end - - def notify? - [:failed, :canceled].include?(commit_status) - end - - - private - - def commit - build.commit - end - - def project - commit.project - end - - def build_status - build.status.to_sym - end - - def commit_status - commit.status.to_sym - end - - def humanized_status(build_or_commit=nil) - build_or_commit ||= commit_status - case build_or_commit - when :pending - "Pending" - when :running - "Running" - when :failed - "Failed" - when :success - "Successful" - when :canceled - "Canceled" - else - "Unknown" - end - end - end -end diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb deleted file mode 100644 index 0df03890efb..00000000000 --- a/app/models/project_services/ci/hip_chat_service.rb +++ /dev/null @@ -1,93 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class HipChatService < Ci::Service - prop_accessor :hipchat_token, :hipchat_room, :hipchat_server - boolean_accessor :notify_only_broken_builds - validates :hipchat_token, presence: true, if: :activated? - validates :hipchat_room, presence: true, if: :activated? - default_value_for :notify_only_broken_builds, true - - def title - "HipChat" - end - - def description - "Private group chat, video chat, instant messaging for teams" - end - - def help - end - - def to_param - 'hip_chat' - end - - def fields - [ - { type: 'text', name: 'hipchat_token', label: 'Token', placeholder: '' }, - { type: 'text', name: 'hipchat_room', label: 'Room', placeholder: '' }, - { type: 'text', name: 'hipchat_server', label: 'Server', placeholder: 'https://hipchat.example.com', help: 'Leave blank for default' }, - { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - commit = build.commit - return unless commit - return unless commit.latest_builds.include? build - - case commit.status.to_sym - when :failed - true - when :success - true unless notify_only_broken_builds? - else - false - end - end - - def execute(build) - msg = Ci::HipChatMessage.new(build) - opts = default_options.merge( - token: hipchat_token, - room: hipchat_room, - server: server_url, - color: msg.status_color, - notify: msg.notify? - ) - Ci::HipChatNotifierWorker.perform_async(msg.to_s, opts) - end - - private - - def default_options - { - service_name: 'GitLab CI', - message_format: 'html' - } - end - - def server_url - if hipchat_server.blank? - 'https://api.hipchat.com' - else - hipchat_server - end - end - end -end diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb deleted file mode 100644 index bb961d06972..00000000000 --- a/app/models/project_services/ci/mail_service.rb +++ /dev/null @@ -1,84 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class MailService < Ci::Service - delegate :email_recipients, :email_recipients=, - :email_add_pusher, :email_add_pusher=, - :email_only_broken_builds, :email_only_broken_builds=, to: :project, prefix: false - - before_save :update_project - - default_value_for :active, true - - def title - 'Mail' - end - - def description - 'Email notification' - end - - def to_param - 'mail' - end - - def fields - [ - { type: 'text', name: 'email_recipients', label: 'Recipients', help: 'Whitespace-separated list of recipient addresses' }, - { type: 'checkbox', name: 'email_add_pusher', label: 'Add pusher to recipients list' }, - { type: 'checkbox', name: 'email_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - # it doesn't make sense to send emails for retried builds - commit = build.commit - return unless commit - return unless commit.latest_builds.include?(build) - - case build.status.to_sym - when :failed - true - when :success - true unless email_only_broken_builds - else - false - end - end - - def execute(build) - build.project_recipients.each do |recipient| - case build.status.to_sym - when :success - mailer.build_success_email(build.id, recipient).deliver_later - when :failed - mailer.build_fail_email(build.id, recipient).deliver_later - end - end - end - - private - - def update_project - project.save! - end - - def mailer - Ci::Notify - end - end -end diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb deleted file mode 100644 index 1a6ff8e34c9..00000000000 --- a/app/models/project_services/ci/slack_message.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'slack-notifier' - -module Ci - class SlackMessage - include Gitlab::Application.routes.url_helpers - - def initialize(commit) - @commit = commit - end - - def pretext - '' - end - - def color - attachment_color - end - - def fallback - format(attachment_message) - end - - def attachments - fields = [] - - commit.latest_builds.each do |build| - next if build.allow_failure? - next unless build.failed? - fields << { - title: build.name, - value: "Build <#{namespace_project_build_url(build.gl_project.namespace, build.gl_project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)." - } - end - - [{ - text: attachment_message, - color: attachment_color, - fields: fields - }] - end - - private - - attr_reader :commit - - def attachment_message - out = "<#{ci_project_url(project)}|#{project_name}>: " - out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> " - out << "(<#{commit_sha_link}|#{commit.short_sha}>) " - out << "of <#{commit_ref_link}|#{commit.ref}> " - out << "by #{commit.git_author_name} " if commit.git_author_name - out << "#{commit_status} in " - out << "#{commit.duration} second(s)" - end - - def format(string) - Slack::Notifier::LinkFormatter.format(string) - end - - def project - commit.project - end - - def project_name - project.name - end - - def commit_sha_link - "#{project.gitlab_url}/commit/#{commit.sha}" - end - - def commit_ref_link - "#{project.gitlab_url}/commits/#{commit.ref}" - end - - def attachment_color - if commit.success? - 'good' - else - 'danger' - end - end - - def commit_status - if commit.success? - 'succeeded' - else - 'failed' - end - end - end -end diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb deleted file mode 100644 index 7064bfe78db..00000000000 --- a/app/models/project_services/ci/slack_service.rb +++ /dev/null @@ -1,81 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -module Ci - class SlackService < Ci::Service - prop_accessor :webhook - boolean_accessor :notify_only_broken_builds - validates :webhook, presence: true, if: :activated? - - default_value_for :notify_only_broken_builds, true - - def title - 'Slack' - end - - def description - 'A team communication tool for the 21st century' - end - - def to_param - 'slack' - end - - def help - 'Visit https://www.slack.com/services/new/incoming-webhook. Then copy link and save project!' unless webhook.present? - end - - def fields - [ - { type: 'text', name: 'webhook', label: 'Webhook URL', placeholder: '' }, - { type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' } - ] - end - - def can_execute?(build) - return if build.allow_failure? - - commit = build.commit - return unless commit - return unless commit.latest_builds.include?(build) - - case commit.status.to_sym - when :failed - true - when :success - true unless notify_only_broken_builds? - else - false - end - end - - def execute(build) - message = Ci::SlackMessage.new(build.commit) - options = default_options.merge( - color: message.color, - fallback: message.fallback, - attachments: message.attachments - ) - Ci::SlackNotifierWorker.perform_async(webhook, message.pretext, options) - end - - private - - def default_options - { - username: 'GitLab CI' - } - end - end -end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index af2840a57f0..6f96907ec18 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -22,6 +22,7 @@ class HipchatService < Service MAX_COMMITS = 3 prop_accessor :token, :room, :server, :notify, :color, :api_version + boolean_accessor :notify_only_broken_builds validates :token, presence: true, if: :activated? def title @@ -45,12 +46,13 @@ class HipchatService < Service { type: 'text', name: 'api_version', placeholder: 'Leave blank for default (v2)' }, { type: 'text', name: 'server', - placeholder: 'Leave blank for default. https://hipchat.example.com' } + placeholder: 'Leave blank for default. https://hipchat.example.com' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, ] end def supported_events - %w(push issue merge_request note tag_push) + %w(push issue merge_request note tag_push build) end def execute(data) @@ -94,6 +96,8 @@ class HipchatService < Service create_merge_request_message(data) unless is_update?(data) when "note" create_note_message(data) + when "build" + create_build_message(data) if should_build_be_notified?(data) end end @@ -235,6 +239,20 @@ class HipchatService < Service message end + def create_build_message(data) + ref_type = data[:tag] ? 'tag' : 'branch' + ref = data[:ref] + sha = data[:sha] + user_name = data[:commit][:author_name] + status = data[:commit][:status] + duration = data[:commit][:duration] + + branch_link = "#{ref}" + commit_link = "#{Commit.truncate_sha(sha)}" + + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)" + end + def project_name project.name_with_namespace.gsub(/\s/, '') end @@ -250,4 +268,24 @@ class HipchatService < Service def is_update?(data) data[:object_attributes][:action] == 'update' end + + def humanized_status(status) + case status + when 'success' + 'passed' + else + status + end + end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 7cd5e892507..35819491575 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -20,6 +20,7 @@ class SlackService < Service prop_accessor :webhook, :username, :channel + boolean_accessor :notify_only_broken_builds validates :webhook, presence: true, if: :activated? def title @@ -45,12 +46,13 @@ class SlackService < Service { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, { type: 'text', name: 'username', placeholder: 'username' }, - { type: 'text', name: 'channel', placeholder: '#channel' } + { type: 'text', name: 'channel', placeholder: '#channel' }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, ] end def supported_events - %w(push issue merge_request note tag_push) + %w(push issue merge_request note tag_push build) end def execute(data) @@ -78,6 +80,8 @@ class SlackService < Service MergeMessage.new(data) unless is_update?(data) when "note" NoteMessage.new(data) + when "build" + BuildMessage.new(data) if should_build_be_notified?(data) end opt = {} @@ -86,7 +90,7 @@ class SlackService < Service if message notifier = Slack::Notifier.new(webhook, opt) - notifier.ping(message.pretext, attachments: message.attachments) + notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) end end @@ -103,9 +107,21 @@ class SlackService < Service def is_update?(data) data[:object_attributes][:action] == 'update' end + + def should_build_be_notified?(data) + case data[:commit][:status] + when 'success' + !notify_only_broken_builds? + when 'failed' + true + else + false + end + end end require "slack_service/issue_message" require "slack_service/push_message" require "slack_service/merge_message" require "slack_service/note_message" +require "slack_service/build_message" diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/slack_service/base_message.rb index aa00d6061a1..f1182824687 100644 --- a/app/models/project_services/slack_service/base_message.rb +++ b/app/models/project_services/slack_service/base_message.rb @@ -10,6 +10,9 @@ class SlackService format(message) end + def fallback + end + def attachments raise NotImplementedError end diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/slack_service/build_message.rb new file mode 100644 index 00000000000..c124cad4afd --- /dev/null +++ b/app/models/project_services/slack_service/build_message.rb @@ -0,0 +1,82 @@ +class SlackService + class BuildMessage < BaseMessage + attr_reader :sha + attr_reader :ref_type + attr_reader :ref + attr_reader :status + attr_reader :project_name + attr_reader :project_url + attr_reader :user_name + attr_reader :duration + + def initialize(params, commit = true) + @sha = params[:sha] + @ref_type = params[:tag] ? 'tag' : 'branch' + @ref = params[:ref] + @project_name = params[:project_name] + @project_url = params[:project_url] + @status = params[:commit][:status] + @user_name = params[:commit][:author_name] + @duration = params[:commit][:duration] + end + + def pretext + '' + end + + def fallback + format(message) + end + + def attachments + [{ text: format(message), color: attachment_color }] + end + + private + + def message + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} second(s)" + end + + def format(string) + Slack::Notifier::LinkFormatter.format(string) + end + + def humanized_status + case status + when 'success' + 'passed' + else + status + end + end + + def attachment_color + if status == 'success' + 'good' + else + 'danger' + end + end + + def branch_url + "#{project_url}/commits/#{ref}" + end + + def branch_link + "[#{ref}](#{branch_url})" + end + + def project_link + "[#{project_name}](#{project_url})" + end + + def commit_url + "#{project_url}/commit/#{sha}/builds" + end + + def commit_link + "[#{Commit.truncate_sha(sha)}](#{commit_url})" + end + end +end diff --git a/app/models/service.rb b/app/models/service.rb index d610abd1683..195c4690e8f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -30,6 +30,7 @@ class Service < ActiveRecord::Base default_value_for :merge_requests_events, true default_value_for :tag_push_events, true default_value_for :note_events, true + default_value_for :build_events, true after_initialize :initialize_properties @@ -47,6 +48,7 @@ class Service < ActiveRecord::Base scope :issue_hooks, -> { where(issues_events: true, active: true) } scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) } scope :note_hooks, -> { where(note_events: true, active: true) } + scope :build_hooks, -> { where(build_events: true, active: true) } def activated? active @@ -133,6 +135,21 @@ class Service < ActiveRecord::Base end end + # Provide convenient boolean accessor methods + # for each serialized property. + # Also keep track of updated properties in a similar way as ActiveModel::Dirty + def self.boolean_accessor(*args) + self.prop_accessor(*args) + + args.each do |arg| + class_eval %{ + def #{arg}? + ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) + 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, @@ -163,6 +180,7 @@ class Service < ActiveRecord::Base assembla bamboo buildkite + builds_email campfire custom_issue_tracker drone_ci diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb index 912eb6258a4..847db2d48a7 100644 --- a/app/services/ci/create_builds_service.rb +++ b/app/services/ci/create_builds_service.rb @@ -31,7 +31,8 @@ module Ci trigger_request: trigger_request, user: user) - commit.builds.create!(build_attrs) + build = commit.builds.create!(build_attrs) + build.execute_hooks end end end diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml deleted file mode 100644 index de6291aa914..00000000000 --- a/app/views/ci/notify/build_fail_email.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- content_for :header do - %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab CI (build failed) -%h3 - Project: - = link_to ci_project_url(@project) do - = @project.name - -%p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} -%p - Author: #{@build.commit.git_author_name} -%p - Branch: #{@build.ref} -%p - Stage: #{@build.stage} -%p - Job: #{@build.name} -%p - Message: #{@build.commit.git_commit_message} - -%p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb deleted file mode 100644 index 17a3b9b1d33..00000000000 --- a/app/views/ci/notify/build_fail_email.text.erb +++ /dev/null @@ -1,11 +0,0 @@ -Build failed for <%= @project.name %> - -Status: <%= @build.status %> -Commit: <%= @build.commit.short_sha %> -Author: <%= @build.commit.git_author_name %> -Branch: <%= @build.ref %> -Stage: <%= @build.stage %> -Job: <%= @build.name %> -Message: <%= @build.commit.git_commit_message %> - -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml deleted file mode 100644 index 6ef1fd67d89..00000000000 --- a/app/views/ci/notify/build_success_email.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -- content_for :header do - %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} - GitLab CI (build successful) - -%h3 - Project: - = link_to ci_project_url(@project) do - = @project.name - -%p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} -%p - Author: #{@build.commit.git_author_name} -%p - Branch: #{@build.ref} -%p - Stage: #{@build.stage} -%p - Job: #{@build.name} -%p - Message: #{@build.commit.git_commit_message} - -%p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb deleted file mode 100644 index bc8b978c3d7..00000000000 --- a/app/views/ci/notify/build_success_email.text.erb +++ /dev/null @@ -1,11 +0,0 @@ -Build successful for <%= @project.name %> - -Status: <%= @build.status %> -Commit: <%= @build.commit.short_sha %> -Author: <%= @build.commit.git_author_name %> -Branch: <%= @build.ref %> -Stage: <%= @build.stage %> -Job: <%= @build.name %> -Message: <%= @build.commit.git_commit_message %> - -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index f0b3f27b626..4b32c631d5c 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -50,18 +50,8 @@ = icon('retweet fw') %span Triggers - = nav_link path: 'ci_web_hooks#index' do - = link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do - = icon('link fw') - %span - CI Web Hooks = nav_link path: 'ci_settings#edit' do = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do = icon('building fw') %span CI Settings - = nav_link controller: 'ci_services' do - = link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do - = icon('share fw') - %span - CI Services diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml new file mode 100644 index 00000000000..3b251dc011a --- /dev/null +++ b/app/views/notify/build_fail_email.html.haml @@ -0,0 +1,23 @@ +- content_for :header do + %h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + GitLab (build failed) +%h3 + Project: + = link_to ci_project_url(@project) do + = @project.name + +%p + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} +%p + Author: #{@build.commit.git_author_name} +%p + Branch: #{@build.ref} +%p + Stage: #{@build.stage} +%p + Job: #{@build.name} +%p + Message: #{@build.commit.git_commit_message} + +%p + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/notify/build_fail_email.text.erb b/app/views/notify/build_fail_email.text.erb new file mode 100644 index 00000000000..17a3b9b1d33 --- /dev/null +++ b/app/views/notify/build_fail_email.text.erb @@ -0,0 +1,11 @@ +Build failed for <%= @project.name %> + +Status: <%= @build.status %> +Commit: <%= @build.commit.short_sha %> +Author: <%= @build.commit.git_author_name %> +Branch: <%= @build.ref %> +Stage: <%= @build.stage %> +Job: <%= @build.name %> +Message: <%= @build.commit.git_commit_message %> + +Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml new file mode 100644 index 00000000000..c23f4b7e45a --- /dev/null +++ b/app/views/notify/build_success_email.html.haml @@ -0,0 +1,24 @@ +- content_for :header do + %h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"} + GitLab (build successful) + +%h3 + Project: + = link_to ci_project_url(@project) do + = @project.name + +%p + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} +%p + Author: #{@build.commit.git_author_name} +%p + Branch: #{@build.ref} +%p + Stage: #{@build.stage} +%p + Job: #{@build.name} +%p + Message: #{@build.commit.git_commit_message} + +%p + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} diff --git a/app/views/notify/build_success_email.text.erb b/app/views/notify/build_success_email.text.erb new file mode 100644 index 00000000000..bc8b978c3d7 --- /dev/null +++ b/app/views/notify/build_success_email.text.erb @@ -0,0 +1,11 @@ +Build successful for <%= @project.name %> + +Status: <%= @build.status %> +Commit: <%= @build.commit.short_sha %> +Author: <%= @build.commit.git_author_name %> +Branch: <%= @build.ref %> +Stage: <%= @build.stage %> +Job: <%= @build.name %> +Message: <%= @build.commit.git_commit_message %> + +Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> diff --git a/app/views/projects/ci_services/_form.html.haml b/app/views/projects/ci_services/_form.html.haml deleted file mode 100644 index 397832e56db..00000000000 --- a/app/views/projects/ci_services/_form.html.haml +++ /dev/null @@ -1,54 +0,0 @@ -%h3.page-title - = @service.title - = boolean_to_icon @service.activated? - -%p= @service.description - - -%hr - -= 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 - - @service.errors.full_messages.each do |msg| - %li= msg - - - if @service.help.present? - .bs-callout - = @service.help - - .form-group - = f.label :active, "Active", class: "control-label" - .col-sm-10 - = f.check_box :active - - - @service.fields.each do |field| - - name = field[:name] - - label = field[:label] || name - - value = @service.send(name) - - type = field[:type] - - placeholder = field[:placeholder] - - choices = field[:choices] - - default_choice = field[:default_choice] - - help = field[:help] - - .form-group - = f.label label, class: "control-label" - .col-sm-10 - - if type == 'text' - = f.text_field name, class: "form-control", placeholder: placeholder - - elsif type == 'textarea' - = f.text_area name, rows: 5, class: "form-control", placeholder: placeholder - - elsif type == 'checkbox' - = f.check_box name - - elsif type == 'select' - = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" } - - if help - .light #{help} - - .form-actions - = f.submit 'Save', class: 'btn btn-save' -   - - if @service.valid? && @service.activated? && @service.can_test? - = link_to 'Test settings', test_namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), class: 'btn' diff --git a/app/views/projects/ci_services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml deleted file mode 100644 index dacb6b4f6f4..00000000000 --- a/app/views/projects/ci_services/edit.html.haml +++ /dev/null @@ -1,2 +0,0 @@ -- page_title @service.title, "CI Services" -= render 'form' diff --git a/app/views/projects/ci_services/index.html.haml b/app/views/projects/ci_services/index.html.haml deleted file mode 100644 index 3f26c7851d8..00000000000 --- a/app/views/projects/ci_services/index.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- page_title "CI Services" -%h3.page-title Project services -%p.light Project services allow you to integrate GitLab CI with other applications - -%table.table - %thead - %tr - %th - %th Service - %th Description - %th Last edit - - @services.sort_by(&:title).each do |service| - %tr - %td - = boolean_to_icon service.activated? - %td - = link_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) do - %strong= service.title - %td - = service.description - %td.light - = time_ago_in_words service.updated_at - ago diff --git a/app/views/projects/ci_web_hooks/index.html.haml b/app/views/projects/ci_web_hooks/index.html.haml deleted file mode 100644 index 2998fb08ff1..00000000000 --- a/app/views/projects/ci_web_hooks/index.html.haml +++ /dev/null @@ -1,94 +0,0 @@ -- page_title "CI Web Hooks" -%h3.page-title - CI Web hooks - -%p.light - Web Hooks can be used for binding events when build completed. - -%hr.clearfix - -= form_for @web_hook, url: namespace_project_ci_web_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| - -if @web_hook.errors.any? - .alert.alert-danger - - @web_hook.errors.full_messages.each do |msg| - %p= msg - .form-group - = f.label :url, "URL", class: 'control-label' - .col-sm-10 - = f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' - .form-actions - = f.submit "Add Web Hook", class: "btn btn-create" - --if @web_hooks.any? - %h4 Activated web hooks (#{@web_hooks.count}) - .table-holder - %table.table - - @web_hooks.each do |hook| - %tr - %td - .clearfix - %span.monospace= hook.url - %td - .pull-right - - if @ci_project.commits.any? - = link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" - = link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" - -%h4 Web Hook data example - -:erb -
    -    
    -      {
    -        "build_id": 2,
    -        "build_name":"rspec_linux"
    -        "build_status": "failed",
    -        "build_started_at": "2014-05-05T18:01:02.563Z",
    -        "build_finished_at": "2014-05-05T18:01:07.611Z",
    -        "project_id": 1,
    -        "project_name": "Brightbox \/ Brightbox Cli",
    -        "gitlab_url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli",
    -        "ref": "master",
    -        "sha": "a26cf5de9ed9827746d4970872376b10d9325f40",
    -        "before_sha": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    -        "push_data": {
    -          "before": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    -          "after": "a26cf5de9ed9827746d4970872376b10d9325f40",
    -          "ref": "refs\/heads\/master",
    -          "user_id": 1,
    -          "user_name": "Administrator",
    -          "project_id": 5,
    -          "repository": {
    -            "name": "Brightbox Cli",
    -            "url": "dzaporozhets@localhost:brightbox\/brightbox-cli.git",
    -            "description": "Voluptatibus quae error consectetur voluptas dolores vel excepturi possimus.",
    -            "homepage": "http:\/\/localhost:3000\/brightbox\/brightbox-cli"
    -          },
    -          "commits": [
    -            {
    -              "id": "a26cf5de9ed9827746d4970872376b10d9325f40",
    -              "message": "Release v1.2.2",
    -              "timestamp": "2014-04-22T16:46:42+03:00",
    -              "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/a26cf5de9ed9827746d4970872376b10d9325f40",
    -              "author": {
    -                "name": "Paul Thornthwaite",
    -                "email": "tokengeek@gmail.com"
    -              }
    -            },
    -            {
    -              "id": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    -              "message": "Fix server user data update\n\nIncorrect condition was being used so Base64 encoding option was having\nopposite effect from desired.",
    -              "timestamp": "2014-04-11T18:17:26+03:00",
    -              "url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/34f57f6ba3ed0c21c5e361bbb041c3591411176c",
    -              "author": {
    -                "name": "Paul Thornthwaite",
    -                "email": "tokengeek@gmail.com"
    -              }
    -            }
    -          ],
    -          "total_commits_count": 2,
    -          "ci_yaml_file":"rspec_linux:\r\n  script: ls\r\n"
    -        }
    -      }
    -    
    -  
    diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml index 3702aeaecba..b18d9197d0b 100644 --- a/app/views/projects/hooks/index.html.haml +++ b/app/views/projects/hooks/index.html.haml @@ -55,6 +55,13 @@ %strong Merge Request events %p.light This url will be triggered when a merge request is created + %div + = f.check_box :build_events, class: 'pull-left' + .prepend-left-20 + = f.label :build_events, class: 'list-label' do + %strong Build events + %p.light + This url will be triggered when the build status changes .form-group = f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' .col-sm-10 @@ -78,7 +85,7 @@ .clearfix %span.monospace= hook.url %p - - %w(push_events tag_push_events issues_events note_events merge_requests_events).each do |trigger| + - %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger| - if hook.send(trigger) %span.label.label-gray= trigger.titleize SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"} diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 16a98a7233c..28d6f421fea 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -59,6 +59,15 @@ %strong Merge Request events %p.light This url will be triggered when a merge request is created + - if @service.supported_events.include?("build") + %div + = form.check_box :build_events, class: 'pull-left' + .prepend-left-20 + = form.label :build_events, class: 'list-label' do + %strong Build events + %p.light + This url will be triggered when a build status changes + - @service.fields.each do |field| - type = field[:type] diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb new file mode 100644 index 00000000000..c5c8055bea7 --- /dev/null +++ b/app/workers/build_email_worker.rb @@ -0,0 +1,19 @@ +class BuildEmailWorker + include Sidekiq::Worker + + def perform(build_id, recipients, push_data) + recipients.split(' ').each do |recipient| + begin + case push_data['build_status'] + when 'success' + Notify.build_success_email(build_id, recipient).deliver_now + when 'failed' + Notify.build_fail_email(build_id, recipient).deliver_now + end + # These are input errors and won't be corrected even if Sidekiq retries + rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e + logger.info("Failed to send e-mail for project '#{push_data['project_name']}' to #{recipient}: #{e}") + end + end + end +end diff --git a/app/workers/ci/hip_chat_notifier_worker.rb b/app/workers/ci/hip_chat_notifier_worker.rb deleted file mode 100644 index ebb43570e2a..00000000000 --- a/app/workers/ci/hip_chat_notifier_worker.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Ci - class HipChatNotifierWorker - include Sidekiq::Worker - - def perform(message, options={}) - room = options.delete('room') - token = options.delete('token') - server = options.delete('server') - name = options.delete('service_name') - client_opts = { - api_version: 'v2', - server_url: server - } - - client = HipChat::Client.new(token, client_opts) - client[room].send(name, message, options.symbolize_keys) - end - end -end diff --git a/app/workers/ci/slack_notifier_worker.rb b/app/workers/ci/slack_notifier_worker.rb deleted file mode 100644 index 3bbb9b4bec7..00000000000 --- a/app/workers/ci/slack_notifier_worker.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Ci - class SlackNotifierWorker - include Sidekiq::Worker - - def perform(webhook_url, message, options={}) - notifier = Slack::Notifier.new(webhook_url) - notifier.ping(message, options) - end - end -end diff --git a/app/workers/ci/web_hook_worker.rb b/app/workers/ci/web_hook_worker.rb deleted file mode 100644 index 0bb83845572..00000000000 --- a/app/workers/ci/web_hook_worker.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Ci - class WebHookWorker - include Sidekiq::Worker - - def perform(hook_id, data) - Ci::WebHook.find(hook_id).execute data - end - end -end diff --git a/config/routes.rb b/config/routes.rb index 061a8fd5da4..a104e686ac6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -596,17 +596,6 @@ Rails.application.routes.draw do resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] resource :ci_settings, only: [:edit, :update, :destroy] - resources :ci_web_hooks, only: [:index, :create, :destroy] do - member do - get :test - end - end - - resources :ci_services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do - member do - get :test - end - end resources :builds, only: [:index, :show] do collection do diff --git a/db/migrate/20151203162134_add_build_events_to_services.rb b/db/migrate/20151203162134_add_build_events_to_services.rb new file mode 100644 index 00000000000..a84be7db3f1 --- /dev/null +++ b/db/migrate/20151203162134_add_build_events_to_services.rb @@ -0,0 +1,6 @@ +class AddBuildEventsToServices < ActiveRecord::Migration + def up + add_column :services, :build_events, :boolean, default: false, null: false + add_column :web_hooks, :build_events, :boolean, default: false, null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 94b87040d88..58595f8ae59 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: 20151203162133) do +ActiveRecord::Schema.define(version: 20151203162134) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -706,6 +706,7 @@ ActiveRecord::Schema.define(version: 20151203162133) do t.boolean "merge_requests_events", default: true t.boolean "tag_push_events", default: true t.boolean "note_events", default: true, null: false + t.boolean "build_events", default: false, null: false end add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree @@ -854,6 +855,7 @@ ActiveRecord::Schema.define(version: 20151203162133) do t.boolean "tag_push_events", default: false t.boolean "note_events", default: false, null: false t.boolean "enable_ssl_verification", default: true + t.boolean "build_events", default: false, null: false end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree diff --git a/features/project/service.feature b/features/project/service.feature index 5014b52b9f6..13edc6cb2b9 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -55,11 +55,11 @@ Feature: Project Services And I fill Pushover settings Then I should see Pushover service settings saved - Scenario: Activate email on push service + Scenario: Activate email service When I visit project "Shop" services page - And I click email on push service link - And I fill email on push settings - Then I should see email on push service settings saved + And I click email service link + And I fill email settings + Then I should see email service settings saved Scenario: Activate Irker (IRC Gateway) service When I visit project "Shop" services page diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb index 6acbf46eb20..037f7494a77 100644 --- a/features/steps/admin/settings.rb +++ b/features/steps/admin/settings.rb @@ -32,6 +32,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps page.check('Comments') page.check('Issues events') page.check('Merge Request events') + page.check('Build events') click_on 'Save' end @@ -39,6 +40,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps fill_in 'Webhook', with: 'http://localhost' fill_in 'Username', with: 'test_user' fill_in 'Channel', with: '#test_channel' + page.check('Notify only broken builds') end step 'I should see service template settings saved' do diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 1c700df0c63..2d564dac498 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -118,16 +118,16 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(find_field('Restrict to branch').value).to eq 'master' end - step 'I click email on push service link' do - click_link 'Emails on push' + step 'I click email service link' do + click_link 'Emails' end - step 'I fill email on push settings' do + step 'I fill email settings' do fill_in 'Recipients', with: 'qa@company.name' click_button 'Save' end - step 'I should see email on push service settings saved' do + step 'I should see email service settings saved' do expect(find_field('Recipients').value).to eq 'qa@company.name' end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 81bf7a8222b..03e3056a87e 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -45,7 +45,8 @@ module API class ProjectHook < Hook expose :project_id, :push_events - expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification + expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events + expose :enable_ssl_verification end class ForkedFromProject < Grape::Entity @@ -252,7 +253,7 @@ module API 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 :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events # Expose serialized properties expose :properties do |service, options| field_names = service.fields. diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 882d1a083ad..cf9938d25a7 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -45,6 +45,7 @@ module API :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification ] @hook = user_project.hooks.new(attrs) @@ -77,6 +78,7 @@ module API :merge_requests_events, :tag_push_events, :note_events, + :build_events, :enable_ssl_verification ] diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb index d719ad9e8d5..23c79f3879d 100644 --- a/lib/ci/api/projects.rb +++ b/lib/ci/api/projects.rb @@ -5,30 +5,6 @@ module Ci before { authenticate! } resource :projects do - # Register new webhook for project - # - # Parameters - # project_id (required) - The ID of a project - # web_hook (required) - WebHook URL - # Example Request - # POST /projects/:project_id/webhooks - post ":project_id/webhooks" do - required_attributes! [:web_hook] - - project = Ci::Project.find(params[:project_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - web_hook = project.web_hooks.new({ url: params[:web_hook] }) - - if web_hook.save - present web_hook, with: Entities::WebHook - else - errors = web_hook.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - # Retrieve all Gitlab CI projects that the user has access to # # Example Request: @@ -121,20 +97,6 @@ module Ci end end - # Remove a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # DELETE /projects/:id - delete ":id" do - project = Ci::Project.find(params[:id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - project.destroy - end - # Link a Gitlab CI project to a runner # # Parameters: diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb new file mode 100644 index 00000000000..fa2cd551cee --- /dev/null +++ b/lib/gitlab/build_data_builder.rb @@ -0,0 +1,64 @@ +module Gitlab + class BuildDataBuilder + class << self + def build(build) + project = build.gl_project + commit = build.commit + user = build.user + + data = { + object_kind: 'build', + + ref: build.ref, + tag: build.tag, + before_sha: build.before_sha, + sha: build.sha, + + # TODO: should this be not prefixed with build_? + # Leaving this way to have backward compatibility + build_id: build.id, + build_name: build.name, + build_stage: build.stage, + build_status: build.status, + build_started_at: build.started_at, + build_finished_at: build.finished_at, + build_duration: build.duration, + + # TODO: do we still need it? + project_id: project.id, + project_name: project.name_with_namespace, + + user: { + id: user.try(:id), + name: user.try(:name), + email: user.try(:email), + }, + + commit: { + id: commit.id, + sha: commit.sha, + message: commit.git_commit_message, + author_name: commit.git_author_name, + author_email: commit.git_author_email, + status: commit.status, + duration: commit.duration, + started_at: commit.started_at, + finished_at: commit.finished_at, + }, + + repository: { + name: project.name, + url: project.url_to_repo, + description: project.description, + homepage: project.web_url, + git_http_url: project.http_url_to_repo, + git_ssh_url: project.ssh_url_to_repo, + visibility_level: project.visibility_level + }, + } + + data + end + end + end +end diff --git a/spec/factories/ci/web_hook.rb b/spec/factories/ci/web_hook.rb deleted file mode 100644 index 40d878ecb3c..00000000000 --- a/spec/factories/ci/web_hook.rb +++ /dev/null @@ -1,6 +0,0 @@ -FactoryGirl.define do - factory :ci_web_hook, class: Ci::WebHook do - sequence(:url) { FFaker::Internet.uri('http') } - project factory: :ci_project - end -end diff --git a/spec/features/ci_web_hooks_spec.rb b/spec/features/ci_web_hooks_spec.rb deleted file mode 100644 index efae0a42c1e..00000000000 --- a/spec/features/ci_web_hooks_spec.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'spec_helper' - -describe 'CI web hooks' do - let(:user) { create(:user) } - before { login_as(user) } - - before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit namespace_project_ci_web_hooks_path(@gl_project.namespace, @gl_project) - end - - context 'create a trigger' do - before do - fill_in 'web_hook_url', with: 'http://example.com' - click_on 'Add Web Hook' - end - - it { expect(@project.web_hooks.count).to eq(1) } - - it 'revokes the trigger' do - click_on 'Remove' - expect(@project.web_hooks.count).to eq(0) - end - end -end diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb new file mode 100644 index 00000000000..af2de207eba --- /dev/null +++ b/spec/lib/gitlab/build_data_builder_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe 'Gitlab::BuildDataBuilder' do + let(:build) { create(:ci_build) } + + describe :build do + let(:data) do + Gitlab::BuildDataBuilder.build(build) + end + + it { expect(data).to be_a(Hash) } + it { expect(data[:ref]).to eq(build.ref) } + it { expect(data[:sha]).to eq(build.sha) } + it { expect(data[:tag]).to eq(build.tag) } + it { expect(data[:build_id]).to eq(build.id) } + it { expect(data[:build_status]).to eq(build.status) } + it { expect(data[:project_id]).to eq(build.gl_project.id) } + it { expect(data[:project_name]).to eq(build.gl_project.name_with_namespace) } + end +end diff --git a/spec/mailers/ci/notify_spec.rb b/spec/mailers/ci/notify_spec.rb deleted file mode 100644 index b83fb41603b..00000000000 --- a/spec/mailers/ci/notify_spec.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'spec_helper' - -describe Ci::Notify do - include EmailSpec::Helpers - include EmailSpec::Matchers - - before do - @commit = FactoryGirl.create :ci_commit - @build = FactoryGirl.create :ci_build, commit: @commit - end - - describe 'build success' do - subject { Ci::Notify.build_success_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build success for/ - end - - it 'contains name of project' do - should have_body_text /build successful/ - end - end - - describe 'build fail' do - subject { Ci::Notify.build_fail_email(@build.id, 'wow@example.com') } - - it 'has the correct subject' do - should have_subject /Build failed for/ - end - - it 'contains name of project' do - should have_body_text /build failed/ - end - end -end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 27e509933b2..154901a2fbc 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -13,6 +13,7 @@ describe Notify do let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to } let(:recipient) { create(:user, email: 'recipient@example.com') } let(:project) { create(:project) } + let(:build) { create(:ci_build) } before(:each) do ActionMailer::Base.deliveries.clear @@ -865,4 +866,32 @@ describe Notify do is_expected.to have_body_text /#{diff_path}/ end end + + describe 'build success' do + before { build.success } + + subject { Notify.build_success_email(build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build success for/ + end + + it 'contains name of project' do + should have_body_text build.project_name + end + end + + describe 'build fail' do + before { build.drop } + + subject { Notify.build_fail_email(build.id, 'wow@example.com') } + + it 'has the correct subject' do + should have_subject /Build failed for/ + end + + it 'contains name of project' do + should have_body_text build.project_name + end + end end diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb deleted file mode 100644 index 7d54b6cf84c..00000000000 --- a/spec/models/ci/project_services/hip_chat_message_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe Ci::HipChatMessage, models: true do - subject { Ci::HipChatMessage.new(build) } - - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - let(:build) do - commit.builds.first - end - - context 'when all matrix builds succeed' do - it 'returns a successful message' do - commit.create_builds('master', false, nil) - commit.builds.update_all(status: "success") - commit.reload - - expect(subject.status_color).to eq 'green' - expect(subject.notify?).to be_falsey - expect(subject.to_s).to match(/Commit #\d+/) - expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./) - end - end - - context 'when at least one matrix build fails' do - it 'returns a failure message' do - commit.create_builds('master', false, nil) - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect(subject.status_color).to eq 'red' - expect(subject.notify?).to be_truthy - expect(subject.to_s).to match(/Commit #\d+/) - expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./) - end - end -end diff --git a/spec/models/ci/project_services/hip_chat_service_spec.rb b/spec/models/ci/project_services/hip_chat_service_spec.rb deleted file mode 100644 index 714f1e17e0b..00000000000 --- a/spec/models/ci/project_services/hip_chat_service_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - - -require 'spec_helper' - -describe Ci::HipChatService, models: true do - - describe "Validations" do - - context "active" do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of :hipchat_room } - it { is_expected.to validate_presence_of :hipchat_token } - - end - end - - describe "Execute" do - - let(:service) { Ci::HipChatService.new } - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } - let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' } - - before do - allow(service).to receive_messages( - project: commit.project, - project_id: commit.project_id, - notify_only_broken_builds: false, - hipchat_room: 123, - hipchat_token: 'a1b2c3d4e5f6' - ) - - WebMock.stub_request(:post, api_url) - end - - - it "should call the HipChat API" do - service.execute(build) - Ci::HipChatNotifierWorker.drain - - expect( WebMock ).to have_requested(:post, api_url).once - end - - it "calls the worker with expected arguments" do - expect( Ci::HipChatNotifierWorker ).to receive(:perform_async) \ - .with(an_instance_of(String), hash_including( - token: 'a1b2c3d4e5f6', - room: 123, - server: 'https://api.hipchat.com', - color: 'red', - notify: true - )) - - service.execute(build) - end - end -end diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb deleted file mode 100644 index 638d9a4a626..00000000000 --- a/spec/models/ci/project_services/mail_service_spec.rb +++ /dev/null @@ -1,177 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::MailService, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - end - end - - describe 'Sends email for' do - let(:mail) { Ci::MailService.new } - let(:user) { User.new(notification_email: 'git@example.com')} - - describe 'failed build' 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) - expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) - end - end - end - - describe 'successfull build' 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1) - expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"]) - end - end - end - - describe 'successfull build and project has email_recipients' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2) - expect( - ActionMailer::Base.deliveries.map(&:to).flatten - ).to include("git@example.com", "jeroen@example.com") - end - end - end - - describe 'successful build and notify only broken builds' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - perform_enqueued_jobs do - expect do - mail.execute(build) if mail.can_execute?(build) - end.to_not change{ ActionMailer::Base.deliveries.size } - end - end - end - - describe 'successful build and can test service' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: false, - email_recipients: "jeroen@example.com") - 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - build - end - - it do - expect(mail.can_test?).to eq(true) - end - end - - describe 'retried build should not receive email' do - let(:project) do - FactoryGirl.create(:ci_project, - email_add_pusher: true, - email_only_broken_builds: true, - email_recipients: "jeroen@example.com") - 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) } - - before do - allow(mail).to receive_messages( - project: project - ) - end - - it do - Ci::Build.retry(build) - perform_enqueued_jobs do - expect do - mail.execute(build) if mail.can_execute?(build) - end.to_not change{ ActionMailer::Base.deliveries.size } - end - end - end - end -end diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb deleted file mode 100644 index 226032b4cda..00000000000 --- a/spec/models/ci/project_services/slack_message_spec.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'spec_helper' - -describe Ci::SlackMessage, models: true do - subject { Ci::SlackMessage.new(commit) } - - let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) } - - context 'when all matrix builds succeeded' do - let(:color) { 'good' } - - it 'returns a message with success' do - commit.create_builds('master', false, nil) - commit.builds.update_all(status: "success") - commit.reload - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('succeeded') - expect(subject.attachments.first[:fields]).to be_empty - end - end - - context 'when one of matrix builds failed' do - let(:color) { 'danger' } - - it 'returns a message with information about failed build' do - commit.create_builds('master', false, nil) - first_build = commit.builds.first - second_build = commit.builds.last - first_build.update(status: "success") - second_build.update(status: "failed") - - expect(subject.color).to eq(color) - expect(subject.fallback).to include('Commit') - expect(subject.fallback).to include("\##{commit.id}") - expect(subject.fallback).to include('failed') - expect(subject.attachments.first[:fields].size).to eq(1) - expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name) - expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}") - end - end -end diff --git a/spec/models/ci/project_services/slack_service_spec.rb b/spec/models/ci/project_services/slack_service_spec.rb deleted file mode 100644 index e7d7d5d6f4c..00000000000 --- a/spec/models/ci/project_services/slack_service_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::SlackService, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - context "active" do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of :webhook } - end - end - - describe "Execute" do - let(:slack) { Ci::SlackService.new } - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' } - let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' } - let(:notify_only_broken_builds) { false } - - before do - allow(slack).to receive_messages( - project: commit.project, - project_id: commit.project_id, - webhook: webhook_url, - notify_only_broken_builds: notify_only_broken_builds - ) - - WebMock.stub_request(:post, webhook_url) - end - - it "should call Slack API" do - slack.execute(build) - Ci::SlackNotifierWorker.drain - - expect(WebMock).to have_requested(:post, webhook_url).once - end - end -end diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb index 346471aa9b5..e358aa02741 100644 --- a/spec/models/ci/project_spec.rb +++ b/spec/models/ci/project_spec.rb @@ -34,11 +34,9 @@ describe Ci::Project, models: true do it { is_expected.to have_many(:runner_projects) } it { is_expected.to have_many(:runners) } - it { is_expected.to have_many(:web_hooks) } it { is_expected.to have_many(:events) } it { is_expected.to have_many(:variables) } it { is_expected.to have_many(:triggers) } - it { is_expected.to have_many(:services) } it { is_expected.to validate_presence_of :timeout } it { is_expected.to validate_presence_of :gitlab_id } diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb deleted file mode 100644 index 34e3af7f810..00000000000 --- a/spec/models/ci/service_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -# == Schema Information -# -# Table name: ci_services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# - -require 'spec_helper' - -describe Ci::Service, models: true do - - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Mass assignment" do - end - - describe "Test Button" do - before do - @service = Ci::Service.new - end - - describe "Testable" do - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - - before do - allow(@service).to receive_messages( - project: commit.project - ) - build - @testable = @service.can_test? - end - - describe :can_test do - it { expect(@testable).to eq(true) } - end - end - end -end diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb deleted file mode 100644 index 1a4edec9d4f..00000000000 --- a/spec/models/ci/web_hook_spec.rb +++ /dev/null @@ -1,63 +0,0 @@ -# == Schema Information -# -# Table name: ci_web_hooks -# -# id :integer not null, primary key -# url :string(255) not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - -require 'spec_helper' - -describe Ci::WebHook, models: true do - describe "Associations" do - it { is_expected.to belong_to :project } - end - - describe "Validations" do - it { is_expected.to validate_presence_of(:url) } - - context "url format" do - it { is_expected.to allow_value("http://example.com").for(:url) } - it { is_expected.to allow_value("https://excample.com").for(:url) } - it { is_expected.to allow_value("http://test.com/api").for(:url) } - it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) } - it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) } - - it { is_expected.not_to allow_value("example.com").for(:url) } - it { is_expected.not_to allow_value("ftp://example.com").for(:url) } - it { is_expected.not_to allow_value("herp-and-derp").for(:url) } - end - end - - describe "execute" do - before(:each) do - @web_hook = FactoryGirl.create(:ci_web_hook) - @project = @web_hook.project - @data = { before: 'oldrev', after: 'newrev', ref: 'ref' } - - WebMock.stub_request(:post, @web_hook.url) - end - - it "POSTs to the web hook URL" do - @web_hook.execute(@data) - expect(WebMock).to have_requested(:post, @web_hook.url).once - end - - it "POSTs the data as JSON" do - json = @data.to_json - - @web_hook.execute(@data) - expect(WebMock).to have_requested(:post, @web_hook.url).with(body: json).once - end - - it "catches exceptions" do - expect(Ci::WebHook).to receive(:post).and_raise("Some HTTP Post error") - - expect{ @web_hook.execute(@data) }. - to raise_error(RuntimeError, 'Some HTTP Post error') - end - end -end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index c96ab548149..a5662b08bda 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -247,6 +247,55 @@ describe HipchatService, models: true do end end + context 'build events' do + let(:build) { create(:ci_build) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + + context 'for failed' do + before { build.drop } + + it "should call Hipchat API" do + hipchat.execute(data) + + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should create a build message" do + message = hipchat.send(:create_build_message, data) + + project_url = project.web_url + project_name = project.name_with_namespace.gsub(/\s/, '') + sha = data[:sha] + ref = data[:ref] + ref_type = data[:tag] ? 'tag' : 'branch' + duration = data[:commit][:duration] + + expect(message).to eq("#{project_name}: " \ + "Commit #{Commit.truncate_sha(sha)} " \ + "of #{ref} #{ref_type} " \ + "by #{data[:commit][:author_name]} failed in #{duration} second(s)") + end + end + + context 'for succeeded' do + before do + build.success + end + + it "should call Hipchat API" do + hipchat.notify_only_broken_builds = false + hipchat.execute(data) + expect(WebMock).to have_requested(:post, api_url).once + end + + it "should notify only broken" do + hipchat.notify_only_broken_builds = true + hipchat.execute(data) + expect(WebMock).to_not have_requested(:post, api_url).once + end + end + end + context "#message_options" do it "should be set to the defaults" do expect(hipchat.send(:message_options)).to eq({ notify: false, color: 'yellow' }) diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/slack_service/build_message_spec.rb new file mode 100644 index 00000000000..d64c67da938 --- /dev/null +++ b/spec/models/project_services/slack_service/build_message_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe SlackService::BuildMessage do + subject { SlackService::BuildMessage.new(args) } + + let(:args) do + { + sha: '97de212e80737a608d939f648d959671fb0a0142', + ref: 'develop', + tag: false, + + project_name: 'project_name', + project_url: 'somewhere.com', + + commit: { + status: status, + author_name: 'hacker', + duration: 10, + }, + } + end + + context 'succeeded' do + let(:status) { 'success' } + let(:color) { 'good' } + + it 'returns a message with information about succeeded build' do + message = ': Commit of branch by hacker succeeded in 10 second(s)' + expect(subject.pretext).to be_empty + expect(subject.fallback).to eq(message) + expect(subject.attachments).to eq([text: message, color: color]) + end + end + + context 'failed' do + let(:status) { 'failed' } + let(:color) { 'danger' } + + it 'returns a message with information about failed build' do + message = ': Commit of branch by hacker failed in 10 second(s)' + expect(subject.pretext).to be_empty + expect(subject.fallback).to eq(message) + expect(subject.attachments).to eq([text: message, color: color]) + end + end +end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index 606b226ad77..142b637d291 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -1,11 +1,17 @@ require 'spec_helper' -describe API::API, 'ProjectHooks', api: true do +describe API::API, 'ProjectHooks', api: true do include ApiHelpers let(:user) { create(:user) } let(:user3) { create(:user) } let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } - let!(:hook) { create(:project_hook, project: project, url: "http://example.com", push_events: true, merge_requests_events: true, tag_push_events: true, issues_events: true, note_events: true, enable_ssl_verification: true) } + let!(:hook) do + create(:project_hook, + project: project, url: "http://example.com", + push_events: true, merge_requests_events: true, tag_push_events: true, + issues_events: true, note_events: true, build_events: true, + enable_ssl_verification: true) + end before do project.team << [user, :master] @@ -26,6 +32,7 @@ describe API::API, 'ProjectHooks', api: true do expect(json_response.first['merge_requests_events']).to eq(true) expect(json_response.first['tag_push_events']).to eq(true) expect(json_response.first['note_events']).to eq(true) + expect(json_response.first['build_events']).to eq(true) expect(json_response.first['enable_ssl_verification']).to eq(true) end end @@ -83,6 +90,7 @@ describe API::API, 'ProjectHooks', api: true do expect(json_response['merge_requests_events']).to eq(false) expect(json_response['tag_push_events']).to eq(false) expect(json_response['note_events']).to eq(false) + expect(json_response['build_events']).to eq(false) expect(json_response['enable_ssl_verification']).to eq(true) end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb index 893fd168d3e..b1fbdac5970 100644 --- a/spec/requests/ci/api/projects_spec.rb +++ b/spec/requests/ci/api/projects_spec.rb @@ -58,57 +58,6 @@ describe Ci::API::API do end end - describe "POST /projects/:project_id/webhooks" do - let!(:project) { FactoryGirl.create(:ci_project) } - - context "Valid Webhook URL" do - let!(:webhook) { { web_hook: "http://example.com/sth/1/ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "should create webhook for specified project" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(201) - expect(json_response["url"]).to eq(webhook[:web_hook]) - end - - it "fails to create webhook for non existsing project" do - post ci_api("/projects/non-existant-id/webhooks"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(401) - end - end - - context "Invalid Webhook URL" do - let!(:webhook) { { web_hook: "ala_ma_kota" } } - - before do - options.merge!(webhook) - end - - it "fails to create webhook for not valid url" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(400) - end - end - - context "Missed web_hook parameter" do - it "fails to create webhook for not provided url" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/webhooks"), options - expect(response.status).to eq(400) - end - end - end - describe "GET /projects/:id" do let!(:project) { FactoryGirl.create(:ci_project) } @@ -158,28 +107,6 @@ describe Ci::API::API do end end - describe "DELETE /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - - it "should delete a specific project" do - project.gl_project.team << [user, :master] - delete ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect { project.reload }. - to raise_error(ActiveRecord::RecordNotFound) - end - - it "non-manager is not authorized" do - delete ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(401) - end - - it "is getting not found error" do - delete ci_api("/projects/not-existing_id"), options - expect(response.status).to eq(404) - end - end - describe "POST /projects/:id/runners/:id" do let(:project) { FactoryGirl.create(:ci_project) } let(:runner) { FactoryGirl.create(:ci_runner) } diff --git a/spec/services/ci/web_hook_service_spec.rb b/spec/services/ci/web_hook_service_spec.rb deleted file mode 100644 index e7d8ab30652..00000000000 --- a/spec/services/ci/web_hook_service_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'spec_helper' - -describe Ci::WebHookService, services: true do - let(:project) { FactoryGirl.create :ci_project } - 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 } - let(:hook) { FactoryGirl.create :ci_web_hook, project: project } - - describe :execute do - it "should execute successfully" do - stub_request(:post, hook.url).to_return(status: 200) - expect(Ci::WebHookService.new.build_end(build)).to be_truthy - end - end - - context 'build_data' do - it "contains all needed fields" do - expect(build_data(build)).to include( - :build_id, - :project_id, - :ref, - :build_status, - :build_started_at, - :build_finished_at, - :before_sha, - :project_name, - :gitlab_url, - :build_name - ) - end - end - - def build_data(build) - Ci::WebHookService.new.send :build_data, build - end -end diff --git a/spec/workers/build_email_worker_spec.rb b/spec/workers/build_email_worker_spec.rb new file mode 100644 index 00000000000..7e379502f99 --- /dev/null +++ b/spec/workers/build_email_worker_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe BuildEmailWorker do + include RepoHelpers + + let(:build) { create(:ci_build) } + let(:user) { create(:user) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + + subject { BuildEmailWorker.new } + + before do + allow(build).to receive(:execute_hooks).and_return(false) + build.success + end + + describe "#perform" do + it "sends mail" do + subject.perform(build.id, user.email, data.stringify_keys) + + email = ActionMailer::Base.deliveries.last + expect(email.subject).to include('Build success for') + expect(email.to).to eq([user.email]) + end + + it "gracefully handles an input SMTP error" do + ActionMailer::Base.deliveries.clear + allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError) + + subject.perform(build.id, user.email, data.stringify_keys) + + expect(ActionMailer::Base.deliveries.count).to eq(0) + end + end +end -- cgit v1.2.1 From d5c91bb9a601a1a344d94763654f0b0996857497 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Wed, 9 Dec 2015 16:31:42 +0100 Subject: Migrate CI WebHooks and Emails to new tables --- app/models/project_services/builds_email_service.rb | 11 +++++++---- app/workers/build_email_worker.rb | 2 +- db/migrate/20151209144329_migrate_ci_web_hooks.rb | 12 ++++++++++++ db/migrate/20151209145909_migrate_ci_emails.rb | 16 ++++++++++++++++ db/schema.rb | 2 +- lib/gitlab/database.rb | 18 ++++++++++++++++++ 6 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20151209144329_migrate_ci_web_hooks.rb create mode 100644 db/migrate/20151209145909_migrate_ci_emails.rb diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 9c9b5a4d08c..4514f55cf56 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -54,7 +54,7 @@ class BuildsEmailService < Service def fields [ - { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' }, + { type: 'textarea', name: 'recipients', placeholder: 'Emails separated by comma' }, { type: 'checkbox', name: 'add_pusher', label: 'Add pusher to recipients list' }, { type: 'checkbox', name: 'notify_only_broken_builds' }, ] @@ -72,10 +72,13 @@ class BuildsEmailService < Service end def all_recipients(data) + all_recipients = [] + all_recipients <<= recipients.split(',') + if add_pusher? && data[:user][:email] - recipients + " #{data[:user][:email]}" - else - recipients + all_recipients << "#{data[:user][:email]}" end + + all_recipients end end diff --git a/app/workers/build_email_worker.rb b/app/workers/build_email_worker.rb index c5c8055bea7..1c7a04a66a8 100644 --- a/app/workers/build_email_worker.rb +++ b/app/workers/build_email_worker.rb @@ -2,7 +2,7 @@ class BuildEmailWorker include Sidekiq::Worker def perform(build_id, recipients, push_data) - recipients.split(' ').each do |recipient| + recipients.each do |recipient| begin case push_data['build_status'] when 'success' diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb new file mode 100644 index 00000000000..0293be3f6ce --- /dev/null +++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb @@ -0,0 +1,12 @@ +class MigrateCiWebHooks < ActiveRecord::Migration + include Gitlab::Database + + def up + execute( + 'INSERT INTO web_hooks (url, project_id, type, created_at, updated_at, push_events, build_events) ' \ + "SELECT ci_web_hooks.url, projects.id, 'ProjectHook', ci_web_hooks.created_at, ci_web_hooks.updated_at, #{false_value}, #{true_value} FROM ci_web_hooks " \ + 'JOIN ci_projects ON ci_web_hooks.project_id = ci_projects.id ' \ + 'JOIN projects ON ci_projects.gitlab_id = projects.id' + ) + end +end diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb new file mode 100644 index 00000000000..ebf0e7d264f --- /dev/null +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -0,0 +1,16 @@ +class MigrateCiEmails < ActiveRecord::Migration + include Gitlab::Database + + def up + execute( + 'INSERT INTO services (project_id, type, created_at, updated_at, active, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events, properties) ' \ + "SELECT projects.id, 'BuildsEmailService', ci_services.created_at, ci_services.updated_at, #{true_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value}, " \ + "CONCAT('{\"notify_only_broken_builds\":\"', ci_projects.email_only_broken_builds, " \ + "'\",\"add_pusher\":\"', ci_projects.email_add_pusher, '\",\"recipients\":\"', ci_projects.email_recipients, '\"}') " \ + 'FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id = ci_projects.id ' \ + 'JOIN projects ON ci_projects.gitlab_id = projects.id ' \ + "WHERE ci_services.type = 'Ci::MailService' AND ci_services.active" + ) + end +end diff --git a/db/schema.rb b/db/schema.rb index 58595f8ae59..50b6650ac40 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: 20151203162134) do +ActiveRecord::Schema.define(version: 20151209145909) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index 71f37f1fef8..de77a6fbff1 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -7,5 +7,23 @@ module Gitlab def self.postgresql? ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql' end + + def true_value + case ActiveRecord::Base.connection.adapter_name.downcase + when 'postgresql' + "'t'" + else + 1 + end + end + + def false_value + case ActiveRecord::Base.connection.adapter_name.downcase + when 'postgresql' + "'f'" + else + 0 + end + end end end -- cgit v1.2.1 From 80f8074d01a310141984dad9dfe01a27b533e78a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 14:08:09 +0100 Subject: Migrate SlackService and HipChat service --- .../project_services/builds_email_service.rb | 2 ++ app/models/project_services/hipchat_service.rb | 2 ++ app/models/project_services/slack_service.rb | 2 ++ db/migrate/20151209145909_migrate_ci_emails.rb | 6 +++++ .../20151210125232_migrate_ci_slack_service.rb | 29 +++++++++++++++++++++ .../20151210125927_migrate_ci_hip_chat_service.rb | 30 ++++++++++++++++++++++ db/schema.rb | 2 +- 7 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20151210125232_migrate_ci_slack_service.rb create mode 100644 db/migrate/20151210125927_migrate_ci_hip_chat_service.rb diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 4514f55cf56..e01648f0596 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -24,6 +24,8 @@ class BuildsEmailService < Service boolean_accessor :notify_only_broken_builds validates :recipients, presence: true, if: :activated? + default_value_for :notify_only_broken_builds, true + def title 'Builds emails' end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 6f96907ec18..4045d16fa22 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -25,6 +25,8 @@ class HipchatService < Service boolean_accessor :notify_only_broken_builds validates :token, presence: true, if: :activated? + default_value_for :notify_only_broken_builds, true + def title 'HipChat' end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 35819491575..553d4b025e1 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -23,6 +23,8 @@ class SlackService < Service boolean_accessor :notify_only_broken_builds validates :webhook, presence: true, if: :activated? + default_value_for :notify_only_broken_builds, true + def title 'Slack' end diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb index ebf0e7d264f..964dde841ad 100644 --- a/db/migrate/20151209145909_migrate_ci_emails.rb +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -2,6 +2,9 @@ class MigrateCiEmails < ActiveRecord::Migration include Gitlab::Database def up + # This inserts a new service: BuildsEmailService + # It also "manually" constructs the properties (JSON-encoded) + # Migrating all ci_projects e-mail related columns execute( 'INSERT INTO services (project_id, type, created_at, updated_at, active, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events, properties) ' \ "SELECT projects.id, 'BuildsEmailService', ci_services.created_at, ci_services.updated_at, #{true_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value}, " \ @@ -13,4 +16,7 @@ class MigrateCiEmails < ActiveRecord::Migration "WHERE ci_services.type = 'Ci::MailService' AND ci_services.active" ) end + + def down + end end diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb new file mode 100644 index 00000000000..4a5dfe866a5 --- /dev/null +++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb @@ -0,0 +1,29 @@ +class MigrateCiSlackService < ActiveRecord::Migration + include Gitlab::Database + + def up + properties_query = 'SELECT properties FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + 'WHERE ci_projects.gitlab_id=services.project_id' + + active_query = 'SELECT 1 FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active" + + # We update the service since services are always generated for project, even if they are inactive + # Activate service and migrate properties if currently the service is not active + execute( + "UPDATE services SET properties=(#{properties_query}), build_events=#{true_value}, active=#{true_value} " \ + "WHERE NOT services.active AND services.type='SlackService' AND (#{active_query}) IS NOT NULL" + ) + + # Tick only build_events if the service is already active + execute( + "UPDATE services SET build_events=#{true_value} " \ + "WHERE services.active AND services.type='SlackService' AND (#{active_query}) IS NOT NULL" + ) + end + + def down + end +end diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb new file mode 100644 index 00000000000..2fdaf5fb917 --- /dev/null +++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb @@ -0,0 +1,30 @@ +class MigrateCiHipChatService < ActiveRecord::Migration + include Gitlab::Database + + def up + # From properties strip `hipchat_` key + properties_query = "SELECT REPLACE(properties, '\"hipchat_', '\"') FROM ci_services " \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + 'WHERE ci_projects.gitlab_id=services.project_id' + + active_query = 'SELECT 1 FROM ci_services ' \ + 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipchatService' AND ci_services.active" + + # We update the service since services are always generated for project, even if they are inactive + # Activate service and migrate properties if currently the service is not active + execute( + "UPDATE services SET properties=(#{properties_query}), build_events=#{true_value}, active=#{true_value} " \ + "WHERE NOT services.active AND services.type='HipchatService' AND (#{active_query}) IS NOT NULL" + ) + + # Tick only build_events if the service is already active + execute( + "UPDATE services SET build_events=#{true_value} " \ + "WHERE services.active AND services.type='HipchatService' AND (#{active_query}) IS NOT NULL" + ) + end + + def down + end +end diff --git a/db/schema.rb b/db/schema.rb index 50b6650ac40..6ccd4404219 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: 20151209145909) do +ActiveRecord::Schema.define(version: 20151210125927) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From c4fa894de22a6ba20f3078f490b708c81b6d6464 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 16:16:34 +0100 Subject: Fix specs --- app/models/service.rb | 1 + features/project/service.feature | 8 ++++---- features/steps/project/services.rb | 8 ++++---- spec/models/project_services/slack_service/build_message_spec.rb | 2 +- spec/workers/build_email_worker_spec.rb | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/models/service.rb b/app/models/service.rb index 195c4690e8f..0ccb8b410d1 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -31,6 +31,7 @@ class Service < ActiveRecord::Base default_value_for :tag_push_events, true default_value_for :note_events, true default_value_for :build_events, true + default_value_for :properties, {} after_initialize :initialize_properties diff --git a/features/project/service.feature b/features/project/service.feature index 13edc6cb2b9..5014b52b9f6 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -55,11 +55,11 @@ Feature: Project Services And I fill Pushover settings Then I should see Pushover service settings saved - Scenario: Activate email service + Scenario: Activate email on push service When I visit project "Shop" services page - And I click email service link - And I fill email settings - Then I should see email service settings saved + And I click email on push service link + And I fill email on push settings + Then I should see email on push service settings saved Scenario: Activate Irker (IRC Gateway) service When I visit project "Shop" services page diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 2d564dac498..1c700df0c63 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -118,16 +118,16 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(find_field('Restrict to branch').value).to eq 'master' end - step 'I click email service link' do - click_link 'Emails' + step 'I click email on push service link' do + click_link 'Emails on push' end - step 'I fill email settings' do + step 'I fill email on push settings' do fill_in 'Recipients', with: 'qa@company.name' click_button 'Save' end - step 'I should see email service settings saved' do + step 'I should see email on push service settings saved' do expect(find_field('Recipients').value).to eq 'qa@company.name' end diff --git a/spec/models/project_services/slack_service/build_message_spec.rb b/spec/models/project_services/slack_service/build_message_spec.rb index d64c67da938..621c83c0cda 100644 --- a/spec/models/project_services/slack_service/build_message_spec.rb +++ b/spec/models/project_services/slack_service/build_message_spec.rb @@ -25,7 +25,7 @@ describe SlackService::BuildMessage do let(:color) { 'good' } it 'returns a message with information about succeeded build' do - message = ': Commit of branch by hacker succeeded in 10 second(s)' + message = ': Commit of branch by hacker passed in 10 second(s)' expect(subject.pretext).to be_empty expect(subject.fallback).to eq(message) expect(subject.attachments).to eq([text: message, color: color]) diff --git a/spec/workers/build_email_worker_spec.rb b/spec/workers/build_email_worker_spec.rb index 7e379502f99..98deae0a588 100644 --- a/spec/workers/build_email_worker_spec.rb +++ b/spec/workers/build_email_worker_spec.rb @@ -16,7 +16,7 @@ describe BuildEmailWorker do describe "#perform" do it "sends mail" do - subject.perform(build.id, user.email, data.stringify_keys) + subject.perform(build.id, [user.email], data.stringify_keys) email = ActionMailer::Base.deliveries.last expect(email.subject).to include('Build success for') @@ -27,7 +27,7 @@ describe BuildEmailWorker do ActionMailer::Base.deliveries.clear allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError) - subject.perform(build.id, user.email, data.stringify_keys) + subject.perform(build.id, [user.email], data.stringify_keys) expect(ActionMailer::Base.deliveries.count).to eq(0) end -- cgit v1.2.1 From 5fd280f3d3264aec3656cb61cd8728f2ca4d61ce Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 12 Nov 2015 17:00:39 +0100 Subject: Licence also accepted as license file --- CHANGELOG | 2 +- app/models/repository.rb | 22 +++++++++++++++------- spec/models/repository_spec.rb | 11 ++++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 69a293d70d6..891004f2ffb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -32,7 +32,7 @@ v 8.2.0 (unreleased) - Fix incoming email config defaults - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - - Accept COPYING as licence file (Zeger-Jan van de Weg) + - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) v 8.1.4 - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu) diff --git a/app/models/repository.rb b/app/models/repository.rb index 622d6303bfe..cc70aa2b737 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -244,16 +244,24 @@ class Repository def license cache.fetch(:license) do licenses = tree(:head).blobs.find_all do |file| - file.name =~ /\A(copying|license)/i + file.name =~ /\A(copying|license|licence)/i end - # If `licence`, `copying` and `copying.lesser` are found, return in the - # following order: licence, copying, copying.lesser - regex = [/\Alicense(\..*)?\z/i, /\Acopying(\..{0,3})?\z/i, /\Acopying.lesser/i] + preferences = [ + /\Alicen[sc]e\z/i, # LICENSE, LICENCE + /\Alicen[sc]e\./i, # LICENSE.md, LICENSE.txt + /\Acopying\z/i, # COPYING + /\Acopying\.(?!lesser)/i, # COPYING.txt + /Acopying.lesser/i # COPYING.LESSER + ] + + license = nil + preferences.each do |r| + license = licenses.find { |l| l.name =~ r } + break if license + end - regex.map do |re| - licenses.find { |l| l.name =~ re } - end.compact.first + license end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 04437d64fee..49af0229bb5 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -102,13 +102,22 @@ describe Repository do end describe "#license" do - it 'test selection preference' do + before do repository.send(:cache).expire(:license) TestBlob = Struct.new(:name) + end + + it 'test selection preference' do files = [TestBlob.new('file'), TestBlob.new('license'), TestBlob.new('copying')] expect(repository.tree).to receive(:blobs).and_return(files) expect(repository.license.name).to eq('license') end + + it 'also accepts licence instead of license' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('licence')]) + + expect(repository.license.name).to eq('licence') + end end end -- cgit v1.2.1 From 01cafa1d90b387abd017b83794eb55dd7e6fca3b Mon Sep 17 00:00:00 2001 From: Borja Aparicio Date: Tue, 8 Dec 2015 11:01:24 +0100 Subject: Aling project js with EE --- app/assets/javascripts/project.js.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index ec919f0cd67..1f221945c06 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -4,8 +4,11 @@ class @Project $('.js-protocol-switch').click -> return if $(@).hasClass('active') - # Toggle 'active' for both buttons - $('.js-protocol-switch').toggleClass('active') + + # Remove the active class for all buttons (ssh, http, kerberos if shown) + $('.active').not($(@)).removeClass('active'); + # Add the active class for the clicked button + $(@).toggleClass('active') url = $(@).data('clone') -- cgit v1.2.1 From a2798e8d06641be61e8a44682984c2d4b123e70f Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 10 Dec 2015 16:29:25 +0100 Subject: Target right release --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b7e86bf83d3..1b0491f2641 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -28,6 +28,7 @@ v 8.3.0 (unreleased) - Automatically select default clone protocol based on user preferences (Eirik Lygre) - Make Network page as sub tab of Commits - Add copy-to-clipboard button for Snippets + - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) @@ -102,7 +103,6 @@ v 8.2.0 - Show specific runners from projects where user is master or owner - MR target branch is now visible on a list view when it is different from project's default one - Improve Continuous Integration graphs page - - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) - Make color of "Accept Merge Request" button consistent with current build status - Add ignore white space option in merge request diff and commit and compare view - Ability to add release notes (markdown text and attachments) to git tags (aka Releases) -- cgit v1.2.1 From 0c54c9a8a3cb025d7a7aec7aaf8ab17f61bb6ed6 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 10 Dec 2015 13:42:10 -0200 Subject: USe innerHtml to avoid slow performance on a MR with very large diff --- app/assets/javascripts/merge_request_tabs.js.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 1f0b6d9ced0..e4fa03c0ac5 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -144,9 +144,8 @@ class @MergeRequestTabs @_get url: "#{source}.json" + @_location.search success: (data) => - html = $(data.html) - html.syntaxHighlight() - $('#diffs').html(html) + document.getElementById('diffs').innerHTML = data.html + $('div#diffs .js-syntax-highlight').syntaxHighlight() @diffsLoaded = true @scrollToElement("#diffs") -- cgit v1.2.1 From 67913670dcc6ca228654f2c6e14d0992636c5bb7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 10 Dec 2015 13:50:31 -0200 Subject: Make selectors more specific when setting the tab content Reason: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2023 --- app/assets/javascripts/merge_request_tabs.js.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index e4fa03c0ac5..79ef746d57f 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -133,7 +133,7 @@ class @MergeRequestTabs @_get url: "#{source}.json" success: (data) => - document.getElementById('commits').innerHTML = data.html + document.querySelector("div#commits").innerHTML = data.html $('.js-timeago').timeago() @commitsLoaded = true @scrollToElement("#commits") @@ -144,7 +144,7 @@ class @MergeRequestTabs @_get url: "#{source}.json" + @_location.search success: (data) => - document.getElementById('diffs').innerHTML = data.html + document.querySelector("div#diffs").innerHTML = data.html $('div#diffs .js-syntax-highlight').syntaxHighlight() @diffsLoaded = true @scrollToElement("#diffs") @@ -155,7 +155,7 @@ class @MergeRequestTabs @_get url: "#{source}.json" success: (data) => - document.getElementById('builds').innerHTML = data.html + document.querySelector("div#builds").innerHTML = data.html $('.js-timeago').timeago() @buildsLoaded = true @scrollToElement("#builds") -- cgit v1.2.1 From dc4e2744ba13cd7d75147787550a1272b9d34a95 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 17:21:06 +0100 Subject: Fix issue tracker service --- app/models/project_services/issue_tracker_service.rb | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 936e574cccd..46ba9808175 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -56,16 +56,12 @@ class IssueTrackerService < Service end def initialize_properties - if properties.nil? + if new_record? if enabled_in_gitlab_config - self.properties = { - title: issues_tracker['title'], - project_url: add_issues_tracker_id(issues_tracker['project_url']), - issues_url: add_issues_tracker_id(issues_tracker['issues_url']), - new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url']) - } - else - self.properties = {} + self.title = issues_tracker['title'] + self.project_url = add_issues_tracker_id(issues_tracker['project_url']) + self.issues_url = add_issues_tracker_id(issues_tracker['issues_url']) + self.new_issue_url = add_issues_tracker_id(issues_tracker['new_issue_url']) end end end @@ -98,8 +94,8 @@ class IssueTrackerService < Service def enabled_in_gitlab_config Gitlab.config.issues_tracker && - Gitlab.config.issues_tracker.values.any? && - issues_tracker + Gitlab.config.issues_tracker.values.any? && + issues_tracker end def issues_tracker -- cgit v1.2.1 From 63a1a581e937ff6d21e7e6ca4774b7907c6a0c1b Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Thu, 10 Dec 2015 10:24:08 -0600 Subject: Document file upload random uuid security --- doc/security/README.md | 3 ++- doc/security/user_file_uploads.md | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 doc/security/user_file_uploads.md diff --git a/doc/security/README.md b/doc/security/README.md index 473f3632dcd..fba6013d9c1 100644 --- a/doc/security/README.md +++ b/doc/security/README.md @@ -4,4 +4,5 @@ - [Rack attack](rack_attack.md) - [Web Hooks and insecure internal web services](webhooks.md) - [Information exclusivity](information_exclusivity.md) -- [Reset your root password](reset_root_password.md) \ No newline at end of file +- [Reset your root password](reset_root_password.md) +- [User File Uploads](user_file_uploads.md) diff --git a/doc/security/user_file_uploads.md b/doc/security/user_file_uploads.md new file mode 100644 index 00000000000..98493d33b00 --- /dev/null +++ b/doc/security/user_file_uploads.md @@ -0,0 +1,11 @@ +# User File Uploads + +Images attached to issues, merge requests or comments do not require authentication +to be viewed if someone knows the direct URL. This direct URL contains a random +32-character ID that prevents unauthorized people from guessing the URL to an +image containing sensitive information. We don't enable authentication because +these images need to be visible in the body of notification emails, which are +often read from email clients that are not authenticated with GitLab, like +Outlook, Apple Mail, or the Mail app on your mobile device. + +Note that non-image attachments do require authentication to be viewed. -- cgit v1.2.1 From b1fc53134471f6c934bedde7ecab74996c8dc86e Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Wed, 9 Dec 2015 10:41:37 +0100 Subject: Fix typo --- app/views/projects/merge_requests/widget/_merged.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index 8c2b5366a06..6f52c963a53 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -33,7 +33,7 @@ .remove_source_branch_in_progress.hide %p = icon('spinner spin') - Removing source branch '#{@merge_request.source_branch}'. Please wait. This page will be automatically reload. + Removing source branch '#{@merge_request.source_branch}'. Please wait, this page will be automatically reloaded. :javascript $('.remove_source_branch').on('click', function() { -- cgit v1.2.1 From 9d6e5f0a28cda3dcbf2dcbcb8a869c0873ea3afc Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 10 Dec 2015 15:42:00 -0200 Subject: Make selectors more specific when scroll to element --- app/assets/javascripts/merge_request_tabs.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 79ef746d57f..9e2dc1250c9 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -77,7 +77,7 @@ class @MergeRequestTabs scrollToElement: (container) -> if window.location.hash - $el = $("#{container} #{window.location.hash}") + $el = $("div#{container} #{window.location.hash}") $('body').scrollTo($el.offset().top) if $el.length # Activate a tab based on the current action -- cgit v1.2.1 From cdda82757781bbae12bb06c92ad58cd9532f1b27 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 10 Dec 2015 20:45:54 +0200 Subject: Remove check icon from merged requests Now that GitLab CI exposes the status of each MR in the MR page it seems "redundant" to have an extra icon next to the CI statuses. --- app/views/projects/merge_requests/_merge_request.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index cf9570f7c7e..105c731c7e1 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -5,7 +5,6 @@ %ul.controls.light - if merge_request.merged? %li - = icon('check') MERGED - elsif merge_request.closed? %li -- cgit v1.2.1 From 3e5b24d46a7fe904a5f934a54dafbb05a01f837b Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Thu, 10 Dec 2015 15:45:36 -0200 Subject: Upgraded Sidekiq to 4.x --- CHANGELOG | 1 + Gemfile | 5 +++-- Gemfile.lock | 16 +++++++++------- config/initializers/sidekiq.rb | 8 ++++++++ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 750ee1016d8..83d422633b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -36,6 +36,7 @@ v 8.3.0 (unreleased) - Use new style for milestone detail page - Fix sidebar tooltips when collapsed - Prevent possible XSS attack with award-emoji + - Upgraded Sidekiq to 4.x v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/Gemfile b/Gemfile index 473770f53e5..d1ed47d2307 100644 --- a/Gemfile +++ b/Gemfile @@ -119,8 +119,9 @@ gem 'acts-as-taggable-on', '~> 3.4' # Background jobs gem 'sinatra', '~> 1.4.4', require: nil -gem 'sidekiq', '~> 3.5.0' -gem 'sidekiq-cron', '~> 0.3.0' +gem 'sidekiq', '~> 4.0' +gem 'sidekiq-cron', '~> 0.4.0' +gem 'redis-namespace' # HTTP requests gem "httparty", '~> 0.13.3' diff --git a/Gemfile.lock b/Gemfile.lock index 37ba22b7ceb..20084d08d26 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -147,6 +147,7 @@ GEM execjs coffee-script-source (1.10.0) colorize (0.7.7) + concurrent-ruby (1.0.0) connection_pool (2.2.0) coveralls (0.8.9) json (~> 1.8) @@ -682,15 +683,15 @@ GEM rack shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (3.5.3) - celluloid (~> 0.17.2) + sidekiq (4.0.1) + concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) json (~> 1.0) redis (~> 3.2, >= 3.2.1) - redis-namespace (~> 1.5, >= 1.5.2) - sidekiq-cron (0.3.1) + sidekiq-cron (0.4.0) + redis-namespace (>= 1.5.2) rufus-scheduler (>= 2.0.24) - sidekiq (>= 2.17.3) + sidekiq (>= 4.0.0) simple_oauth (0.1.9) simplecov (0.10.0) docile (~> 1.1.0) @@ -934,6 +935,7 @@ DEPENDENCIES rblineprof rdoc (~> 3.6) redcarpet (~> 3.3.3) + redis-namespace redis-rails (~> 4.0.0) request_store (~> 1.2.0) rerun (~> 0.10.0) @@ -951,8 +953,8 @@ DEPENDENCIES settingslogic (~> 2.0.9) sham_rack shoulda-matchers (~> 2.8.0) - sidekiq (~> 3.5.0) - sidekiq-cron (~> 0.3.0) + sidekiq (~> 4.0) + sidekiq-cron (~> 0.4.0) simplecov (~> 0.10.0) sinatra (~> 1.4.4) six (~> 0.2.0) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 6e5701e33da..2e3a71912ef 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -23,6 +23,14 @@ Sidekiq.configure_server do |config| if File.exists?(schedule_file) Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) end + + # Database pool should be at least `sidekiq_concurrency` + 2 + # For more info, see: https://github.com/mperham/sidekiq/blob/master/4.0-Upgrade.md + config = ActiveRecord::Base.configurations[Rails.env] || + Rails.application.config.database_configuration[Rails.env] + config['pool'] = Sidekiq.options[:concurrency] + 2 + ActiveRecord::Base.establish_connection(config) + Rails.logger.debug("Connection Pool size for Sidekiq Server is now: #{ActiveRecord::Base.connection.pool.instance_variable_get('@size')}") end Sidekiq.configure_client do |config| -- cgit v1.2.1 From 7f1b60cc66d27d1596b881884734532cdc62efd7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Dec 2015 09:02:57 -0800 Subject: Bump devise to 3.5.3 to fix reset token expiring after account creation Also fixes an incorrect redirect after login with relative URL root: Closes https://github.com/gitlabhq/gitlabhq/issues/8228 Closes #2750 --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2fa1dac3463..2078e2dc67d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.3.0 (unreleased) - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Merge when build succeeds (Zeger-Jan van de Weg) - Provide better diagnostic message upon project creation errors (Stan Hu) + - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu) - Bump gollum-lib to 4.1.0 (Stan Hu) - Fix broken group avatar upload under "New group" (Stan Hu) - Update project repositorize size and commit count during import:repos task (Stan Hu) diff --git a/Gemfile b/Gemfile index 473770f53e5..edeab0a9d5d 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql gem "pg", '~> 0.18.2', group: :postgres # Authentication libraries -gem 'devise', '~> 3.5.2' +gem 'devise', '~> 3.5.3' gem 'devise-async', '~> 0.9.0' gem 'doorkeeper', '~> 2.2.0' gem 'omniauth', '~> 1.2.2' diff --git a/Gemfile.lock b/Gemfile.lock index 37ba22b7ceb..c3d7536b6c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -168,7 +168,7 @@ GEM activerecord (>= 3.2.0, < 5.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devise (3.5.2) + devise (3.5.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 3.2.6, < 5) @@ -858,7 +858,7 @@ DEPENDENCIES d3_rails (~> 3.5.5) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) - devise (~> 3.5.2) + devise (~> 3.5.3) devise-async (~> 0.9.0) devise-two-factor (~> 2.0.0) diffy (~> 3.0.3) -- cgit v1.2.1 From d8b3c3274c63d5fa62c441bac6e78a16e94422c3 Mon Sep 17 00:00:00 2001 From: Corey Hinshaw Date: Thu, 14 May 2015 11:46:48 -0400 Subject: AuthHash should not parameterize email user --- lib/gitlab/o_auth/auth_hash.rb | 2 +- spec/lib/gitlab/o_auth/auth_hash_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb index d94b104bbf8..ba31599432b 100644 --- a/lib/gitlab/o_auth/auth_hash.rb +++ b/lib/gitlab/o_auth/auth_hash.rb @@ -62,7 +62,7 @@ module Gitlab # Get the first part of the email address (before @) # In addtion in removes illegal characters def generate_username(email) - email.match(/^[^@]*/)[0].parameterize + email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/,'').to_s end def generate_temporarily_email(username) diff --git a/spec/lib/gitlab/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/o_auth/auth_hash_spec.rb index a4f8b44e38e..8aaeb5779d3 100644 --- a/spec/lib/gitlab/o_auth/auth_hash_spec.rb +++ b/spec/lib/gitlab/o_auth/auth_hash_spec.rb @@ -14,7 +14,7 @@ describe Gitlab::OAuth::AuthHash, lib: true do let(:uid_raw) do "CN=Onur K\xC3\xBC\xC3\xA7\xC3\xBCk,OU=Test,DC=example,DC=net" end - let(:email_raw) { "onur.k\xC3\xBC\xC3\xA7\xC3\xBCk@example.net" } + let(:email_raw) { "onur.k\xC3\xBC\xC3\xA7\xC3\xBCk_ABC-123@example.net" } let(:nickname_raw) { "ok\xC3\xBC\xC3\xA7\xC3\xBCk" } let(:first_name_raw) { 'Onur' } let(:last_name_raw) { "K\xC3\xBC\xC3\xA7\xC3\xBCk" } @@ -66,7 +66,7 @@ describe Gitlab::OAuth::AuthHash, lib: true do before { info_hash.delete(:nickname) } it 'takes the first part of the email as username' do - expect(auth_hash.username).to eql 'onur-kucuk' + expect(auth_hash.username).to eql 'onur.kucuk_ABC-123' end end -- cgit v1.2.1 From 1f893022fdb9e5d23c5006e9960c1965a9415f2d Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 10 Dec 2015 22:07:25 -0200 Subject: Remove note from MR discussion when deleting it in the changes tab --- app/assets/javascripts/notes.js.coffee | 24 ++++++++++++++++-------- features/project/merge_requests.feature | 10 ++++++++++ features/steps/project/merge_requests.rb | 19 +++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b1df56b24fe..30aaa364ba3 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -350,18 +350,26 @@ class @Notes ### removeNote: -> note = $(this).closest(".note") - notes = note.closest(".notes") + note_id = note.attr('id') - # check if this is the last note for this line - if notes.find(".note").length is 1 + $('.note[id="' + note_id + '"]').each -> + note = $(this) + notes = note.closest(".notes") + count = notes.closest(".notes_holder").find(".discussion-notes-count") - # for discussions - notes.closest(".discussion").remove() + # check if this is the last note for this line + if notes.find(".note").length is 1 - # for diff lines - notes.closest("tr").remove() + # for discussions + notes.closest(".discussion").remove() - note.remove() + # for diff lines + notes.closest("tr").remove() + else + # update notes count + count.get(0).lastChild.nodeValue = " #{notes.children().length - 1}" + + note.remove() ### Called in response to clicking the delete attachment link diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index 7490ad400b5..aa9078b878f 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -94,6 +94,16 @@ Feature: Project Merge Requests Then I should not see a diff comment saying "Line is wrong" And I should see a diff comment saying "Typo, please fix" + @javascript + Scenario: I delete a comment on a merge request diff + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And I click on the Changes tab + And I leave a comment like "Line is wrong" on diff + And I delete the comment "Line is wrong" on diff + And I click on the Discussion tab + Then I should not see any discussion + @javascript Scenario: I comment on a line of a commit in merge request Given project "Shop" have "Bug NS-05" open merge request with diffs inside diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 13d9d180dd1..f0cf4818d0c 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -211,6 +211,25 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end + step 'I delete the comment "Line is wrong" on diff' do + page.within('.diff-file:nth-of-type(5) .note') do + find('.js-note-delete').click + end + end + + step 'I click on the Discussion tab' do + page.within '.merge-request-tabs' do + click_link 'Discussion' + end + + # Waits for load + expect(page).to have_css('.tab-content #notes.active') + end + + step 'I should not see any discussion' do + expect(page).not_to have_css('.notes .discussion') + end + step 'I should see a discussion has started on diff' do page.within(".notes .discussion") do page.should have_content "#{current_user.name} started a discussion" -- cgit v1.2.1 From 1a9d6ec71eafddf8cd6542b47b5473a6a3ba19b9 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 10 Dec 2015 22:30:06 -0200 Subject: [ci skip] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 2bde388d0fb..8dd7e510d8a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -40,6 +40,7 @@ v 8.3.0 (unreleased) - Fix sidebar tooltips when collapsed - Prevent possible XSS attack with award-emoji - Upgraded Sidekiq to 4.x + - Fix deleting notes on a merge request diff v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 74dcbec369aca9dfa181c9a82e1978ba2396773a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 10 Dec 2015 21:26:32 -0800 Subject: Update CHANGELOG for 7bb8bb85 --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 1cc780ae729..e38c8b363e7 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.3.0 (unreleased) + - Expand character set of usernames created by Omniauth (Corey Hinshaw) - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Merge when build succeeds (Zeger-Jan van de Weg) - Provide better diagnostic message upon project creation errors (Stan Hu) -- cgit v1.2.1 From 4a32b07d9e16a7926c42e11a462bf76133617f0c Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 07:32:46 +0000 Subject: Add `runners_registration_token` to ApplicationSettings --- app/models/application_setting.rb | 2 +- ...0072243_add_runners_registration_token_to_application_settings.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 1880ad9f33c..764ecd4ee20 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -27,6 +27,7 @@ # admin_notification_email :string(255) # shared_runners_enabled :boolean default(TRUE), not null # max_artifacts_size :integer default(100), not null +# runners_registration_token :string(255) # class ApplicationSetting < ActiveRecord::Base @@ -128,5 +129,4 @@ class ApplicationSetting < ActiveRecord::Base /x) self.restricted_signup_domains.reject! { |d| d.empty? } end - end diff --git a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb new file mode 100644 index 00000000000..00f88180e46 --- /dev/null +++ b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb @@ -0,0 +1,5 @@ +class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration + def change + add_column :application_settings, :runners_registration_token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 94b87040d88..a3b649f3fd0 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: 20151203162133) do +ActiveRecord::Schema.define(version: 20151210072243) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -49,6 +49,7 @@ ActiveRecord::Schema.define(version: 20151203162133) do t.string "admin_notification_email" t.boolean "shared_runners_enabled", default: true, null: false t.integer "max_artifacts_size", default: 100, null: false + t.string "runners_registration_token" end create_table "audit_events", force: :cascade do |t| -- cgit v1.2.1 From 9948e5bcdd9e2b9c99bba0bac119ac08daf82564 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 10:48:37 +0100 Subject: Refactor `TokenAuthenticatable` to improve reusability This adds a ability to use multiple different authentication token fields in other models. From now on it is necessary to add authentication token field manually in each class that implements this mixin. --- app/models/application_setting.rb | 3 +++ app/models/concerns/token_authenticatable.rb | 40 +++++++++++++++++----------- app/models/user.rb | 4 ++- 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 764ecd4ee20..b49a5ce9054 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -31,6 +31,9 @@ # class ApplicationSetting < ActiveRecord::Base + include TokenAuthenticatable + add_authentication_token_field :runners_registration_token + CACHE_KEY = 'application_setting.last' serialize :restricted_visibility_levels diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index 9b88ec1cc38..46f8ec84e25 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -1,31 +1,39 @@ module TokenAuthenticatable extend ActiveSupport::Concern - module ClassMethods - def find_by_authentication_token(authentication_token = nil) - if authentication_token - where(authentication_token: authentication_token).first - end + class_methods do + def authentication_token_fields + @token_fields || [] end - end - def ensure_authentication_token - if authentication_token.blank? - self.authentication_token = generate_authentication_token - end - end + private + + def add_authentication_token_field(token_field) + @token_fields = [] unless @token_fields + @token_fields << token_field - def reset_authentication_token! - self.authentication_token = generate_authentication_token - save + define_singleton_method("find_by_#{token_field}") do |token| + where(token_field => token).first if token + end + + define_method("ensure_#{token_field}") do + write_attribute(token_field, generate_token_for(token_field)) if + read_attribute(token_field).blank? + end + + define_method("reset_#{token_field}!") do + write_attribute(token_field, generate_token_for(token_field)) + save + end + end end private - def generate_authentication_token + def generate_token_for(token_field) loop do token = Devise.friendly_token - break token unless self.class.unscoped.where(authentication_token: token).first + break token unless self.class.unscoped.where(token_field => token).first end end end diff --git a/app/models/user.rb b/app/models/user.rb index 7155dd2bea7..1a8d8f1e249 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -69,8 +69,10 @@ class User < ActiveRecord::Base include Gitlab::CurrentSettings include Referable include Sortable - include TokenAuthenticatable include CaseSensitivity + include TokenAuthenticatable + + add_authentication_token_field :authentication_token default_value_for :admin, false default_value_for :can_create_group, gitlab_config.default_can_create_group -- cgit v1.2.1 From d90d3db32bdc5f2180651297939490821e3f7fc9 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 12:43:35 +0100 Subject: Use `save!` when generating new token in `TokenAuthenticatable` --- app/models/concerns/token_authenticatable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index 46f8ec84e25..fe764e8da41 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -23,7 +23,7 @@ module TokenAuthenticatable define_method("reset_#{token_field}!") do write_attribute(token_field, generate_token_for(token_field)) - save + save! end end end -- cgit v1.2.1 From 30e29bb9244e6e0395a42dd966f72dd966a59a03 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 12:44:40 +0100 Subject: Add specs for `TokenAuthenticatable` concern --- spec/models/concerns/token_authenticatable_spec.rb | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 spec/models/concerns/token_authenticatable_spec.rb diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb new file mode 100644 index 00000000000..1b553173415 --- /dev/null +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +shared_examples 'TokenAuthenticatable' do + describe 'dynamically defined methods' do + it { expect(described_class).to be_private_method_defined(:generate_token_for) } + it { expect(described_class).to respond_to("find_by_#{token_field}") } + it { is_expected.to respond_to("ensure_#{token_field}") } + it { is_expected.to respond_to("reset_#{token_field}!") } + end +end + +describe User, 'TokenAuthenticatable' do + let(:token_field) { :authentication_token } + it_behaves_like 'TokenAuthenticatable' + + describe 'ensured authentication token' do + subject { create(:user).send(token_field) } + it { is_expected.to be_a String } + end +end + +describe ApplicationSetting, 'TokenAuthenticatable' do + let(:token_field) { :runners_registration_token } + it_behaves_like 'TokenAuthenticatable' + + describe 'generating new token' do + subject { described_class.new } + let(:token) { subject.send(token_field) } + + context 'token is not generated yet' do + it { expect(token).to be nil } + end + + context 'token is generated' do + before { subject.send("reset_#{token_field}!") } + it { expect(token).to be_a String } + end + end + + describe 'multiple token fields' do + before do + described_class.send(:add_authentication_token_field, :yet_another_token) + end + + describe '.token_fields' do + subject { described_class.authentication_token_fields } + it { is_expected.to include(:runners_registration_token, :yet_another_token) } + end + end +end -- cgit v1.2.1 From 5a7b94f6d1bfd7c376ecb6af45a31bb774358826 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 13:05:59 +0100 Subject: Ensure that app settings contains runners registration token --- app/models/application_setting.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index b49a5ce9054..faa0bdf840b 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -78,6 +78,8 @@ class ApplicationSetting < ActiveRecord::Base end end + before_save :ensure_runners_registration_token + after_commit do Rails.cache.write(CACHE_KEY, self) end -- cgit v1.2.1 From 219afbad2adf0c1b7b3d4ccc116f44ba82818cf1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 13:06:44 +0100 Subject: Display runners registration token in CI runners config --- app/views/ci/admin/runners/index.html.haml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index bacaccfbffa..c71710a831c 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -1,6 +1,9 @@ %p.lead - %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. - %code #{GitlabCi::REGISTRATION_TOKEN} + %span + To register a new runner you should enter the following registration token. + With this token the runner will request a unique runner token and use that for future communication. + Registration token is + %code #{current_application_settings.runners_registration_token} .bs-callout %p -- cgit v1.2.1 From e2e43a114b5af63aad6d9badcb727c8829abb167 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 10 Dec 2015 13:50:00 +0100 Subject: Use new runners registration token to register CI runners --- config/initializers/4_ci_app.rb | 2 -- lib/ci/api/helpers.rb | 6 +++++- lib/ci/api/runners.rb | 2 +- spec/requests/ci/api/runners_spec.rb | 9 ++++++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/config/initializers/4_ci_app.rb b/config/initializers/4_ci_app.rb index cac8edb32bf..d252e403102 100644 --- a/config/initializers/4_ci_app.rb +++ b/config/initializers/4_ci_app.rb @@ -1,8 +1,6 @@ module GitlabCi VERSION = Gitlab::VERSION REVISION = Gitlab::REVISION - - REGISTRATION_TOKEN = SecureRandom.hex(10) def self.config Settings diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 02502333756..12d7396189d 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -6,7 +6,7 @@ module Ci UPDATE_RUNNER_EVERY = 60 def authenticate_runners! - forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN + forbidden! unless runner_registration_token_valid? end def authenticate_runner! @@ -22,6 +22,10 @@ module Ci forbidden! unless token && build.valid_token?(token) end + def runner_registration_token_valid? + params[:token] == current_application_settings.runners_registration_token + end + def update_runner_last_contact # Use a random threshold to prevent beating DB updates contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 1466fe4356e..3edf067f46d 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -40,7 +40,7 @@ module Ci required_attributes! [:token] runner = - if params[:token] == GitlabCi::REGISTRATION_TOKEN + if runner_registration_token_valid? # Create shared runner. Requires admin access Ci::Runner.create( description: params[:description], diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 11dc089e1f5..962d34a0f95 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -4,8 +4,11 @@ describe Ci::API::API do include ApiHelpers include StubGitlabCalls + let(:registration_token) { 'abcdefg123456' } + before do stub_gitlab_calls + stub_application_setting(runners_registration_token: registration_token) end describe "GET /runners" do @@ -33,20 +36,20 @@ describe Ci::API::API do describe "POST /runners/register" do describe "should create a runner if token provided" do - before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } + before { post ci_api("/runners/register"), token: registration_token } it { expect(response.status).to eq(201) } end describe "should create a runner with description" do - before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, description: "server.hostname" } + before { post ci_api("/runners/register"), token: registration_token, description: "server.hostname" } it { expect(response.status).to eq(201) } it { expect(Ci::Runner.first.description).to eq("server.hostname") } end describe "should create a runner with tags" do - before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN, tag_list: "tag1, tag2" } + before { post ci_api("/runners/register"), token: registration_token, tag_list: "tag1, tag2" } it { expect(response.status).to eq(201) } it { expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) } -- cgit v1.2.1 From 2da3cf314651d22f85059d99476ec7952950b44f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 10:22:05 +0100 Subject: Add CI runners registration token reset button --- app/controllers/admin/application_settings_controller.rb | 6 ++++++ app/views/ci/admin/runners/index.html.haml | 13 ++++++++++++- config/routes.rb | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index a9bcfc7456a..48040359389 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -13,6 +13,12 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end end + def reset_runners_token + @application_setting.reset_runners_registration_token! + flash[:notice] = 'New runners registration token has been generated!' + redirect_to ci_admin_runners_path + end + private def set_application_setting diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index c71710a831c..95bc8ebd40f 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -3,7 +3,18 @@ To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. Registration token is - %code #{current_application_settings.runners_registration_token} + %code{ id: 'runners-token' } #{current_application_settings.runners_registration_token} + +.bs-callout.clearfix + .pull-left + %p + You can reset runners registration token by pressing a button below. + %p + = button_to reset_runners_token_admin_application_settings_path, + method: :put, class: 'btn btn-default', + data: { confirm: 'Are you sure you want to reset registration token?' } do + = icon('refresh') + Reset runners registration token .bs-callout %p diff --git a/config/routes.rb b/config/routes.rb index 061a8fd5da4..54d6afe2dca 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -276,6 +276,7 @@ Rails.application.routes.draw do resource :application_settings, only: [:show, :update] do resources :services + put :reset_runners_token end resources :labels -- cgit v1.2.1 From 28ad40d9740076677420612443358c2976fe0916 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 11:14:55 +0100 Subject: Add specs for feature that regenerates runners token --- spec/features/ci/admin/runners_spec.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index b83744f53a8..0c5aebb3a7b 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -61,4 +61,26 @@ describe "Admin Runners" do it { expect(page).not_to have_content(@project2.name_with_namespace) } end end + + describe 'runners registration token' do + let!(:token) { current_application_settings.runners_registration_token } + before { visit ci_admin_runners_path } + + it 'has a registration token' do + expect(page).to have_content("Registration token is #{token}") + expect(page).to have_selector('#runners-token', text: token) + end + + describe 'reload registration token' do + let(:page_token) { find('#runners-token').text } + + before do + click_button 'Reset runners registration token' + end + + it 'changes registration token' do + expect(page_token).to_not eq token + end + end + end end -- cgit v1.2.1 From 72b7d1f59d4fb26fb9119c5a059528f0fc951904 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 11 Dec 2015 13:10:00 +0200 Subject: emoji aliases problem --- CHANGELOG | 1 + app/assets/javascripts/awards_handler.coffee | 7 +++++- app/models/note.rb | 3 ++- app/views/votes/_votes_block.html.haml | 3 ++- lib/award_emoji.rb | 35 ++++++++++++++++++++++++++++ spec/models/note_spec.rb | 9 +++++++ 6 files changed, 55 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 585e6c42043..3ec005c58f6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -43,6 +43,7 @@ v 8.3.0 (unreleased) - Prevent possible XSS attack with award-emoji - Upgraded Sidekiq to 4.x - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) + - Fix emoji aliases problem v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 96fd8f8773e..3ff9ba77dfc 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -1,11 +1,13 @@ class @AwardsHandler - constructor: (@post_emoji_url, @noteable_type, @noteable_id) -> + constructor: (@post_emoji_url, @noteable_type, @noteable_id, @aliases) -> addAward: (emoji) -> + emoji = @normilizeEmojiName(emoji) @postEmoji emoji, => @addAwardToEmojiBar(emoji) addAwardToEmojiBar: (emoji, custom_path = '') -> + emoji = @normilizeEmojiName(emoji) if @exist(emoji) if @isActive(emoji) @decrementCounter(emoji) @@ -94,3 +96,6 @@ class @AwardsHandler $('body, html').animate({ scrollTop: $('.awards').offset().top - 80 }, 200) + + normilizeEmojiName: (emoji) -> + @aliases[emoji] || emoji diff --git a/app/models/note.rb b/app/models/note.rb index 8f0efa8d4b7..04053ccc61e 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -377,6 +377,7 @@ class Note < ActiveRecord::Base end def award_emoji_name - note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] + original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] + AwardEmoji.normilize_emoji_name(original_name) end end diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 7eb27c12d33..6071f1484c6 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -19,7 +19,8 @@ post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}" noteable_type = "#{votable.class.name.underscore}" noteable_id = "#{votable.id}" - window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id) + aliases = #{AwardEmoji::ALIASES.to_json} + window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id, aliases) $(".awards-menu li").click (e)-> emoji = $(this).data("emoji") diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index d58a196c4ef..4d99164bc33 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -6,7 +6,42 @@ class AwardEmoji "ambulance", "anguished", "two_hearts", "wink" ] + ALIASES = { + pout: "rage", + satisfied: "laughing", + hankey: "shit", + poop: "shit", + collision: "boom", + thumbsup: "+1", + thumbsdown: "-1", + punch: "facepunch", + raised_hand: "hand", + running: "runner", + ng_woman: "no_good", + shoe: "mans_shoe", + tshirt: "shirt", + honeybee: "bee", + flipper: "dolphin", + paw_prints: "feet", + waxing_gibbous_moon: "moon", + telephone: "phone", + knife: "hocho", + envelope: "email", + pencil: "memo", + open_book: "book", + sailboat: "boat", + red_car: "car", + lantern: "izakaya_lantern", + uk: "gb", + heavy_exclamation_mark: "exclamation", + squirrel: "shipit" + }.with_indifferent_access + def self.path_to_emoji_image(name) "emoji/#{Emoji.emoji_filename(name)}.png" end + + def self.normilize_emoji_name(name) + ALIASES[name] || name + end end diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 5b6f177ebb2..216c7dabae0 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -159,4 +159,13 @@ describe Note, models: true do expect(note.editable?).to be_falsy end end + + describe "set_award!" do + let(:issue) { create :issue } + + it "converts aliases to actual name" do + note = create :note, note: ":thumbsup:", noteable: issue + expect(note.reload.note).to eq("+1") + end + end end -- cgit v1.2.1 From e365750199802a7c0b45dc88c99e4bf7eb6f111c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 13:24:38 +0100 Subject: Enhance migrate CI emails --- db/migrate/20151209145909_migrate_ci_emails.rb | 27 ++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb index 964dde841ad..5ee11893582 100644 --- a/db/migrate/20151209145909_migrate_ci_emails.rb +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -3,13 +3,16 @@ class MigrateCiEmails < ActiveRecord::Migration def up # This inserts a new service: BuildsEmailService - # It also "manually" constructs the properties (JSON-encoded) + # It "manually" constructs the properties (JSON-encoded) # Migrating all ci_projects e-mail related columns execute( 'INSERT INTO services (project_id, type, created_at, updated_at, active, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events, properties) ' \ - "SELECT projects.id, 'BuildsEmailService', ci_services.created_at, ci_services.updated_at, #{true_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value}, " \ - "CONCAT('{\"notify_only_broken_builds\":\"', ci_projects.email_only_broken_builds, " \ - "'\",\"add_pusher\":\"', ci_projects.email_add_pusher, '\",\"recipients\":\"', ci_projects.email_recipients, '\"}') " \ + "SELECT projects.id, 'BuildsEmailService', ci_services.created_at, ci_services.updated_at, " \ + "#{true_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value}, " \ + "CONCAT('{\"notify_only_broken_builds\":\"', #{convert_bool('ci_projects.email_only_broken_builds')}, " \ + "'\",\"add_pusher\":\"', #{convert_bool('ci_projects.email_add_pusher')}, " \ + "'\",\"recipients\":\"', #{escape_text('ci_projects.email_recipients')}, " \ + "'\"}') " \ 'FROM ci_services ' \ 'JOIN ci_projects ON ci_services.project_id = ci_projects.id ' \ 'JOIN projects ON ci_projects.gitlab_id = projects.id ' \ @@ -19,4 +22,20 @@ class MigrateCiEmails < ActiveRecord::Migration def down end + + # This function escapes double-quotes and slash + def escape_text(name) + "REPLACE(REPLACE(#{name}, '\\', '\\\\'), '\"', '\\\"')" + end + + # This function returns 0 or 1 for column + def convert_bool(name) + if self.postgresql? + # PostgreSQL uses BOOLEAN type + "CASE WHEN #{name} IS TRUE THEN '1' ELSE '0' END;" + else + # MySQL uses TINYINT + "#{name}" + end + end end -- cgit v1.2.1 From 71e6a93db979165073161f0dc13d3bfe7a461e4a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 13:28:40 +0100 Subject: Change default values --- app/models/project_services/builds_email_service.rb | 7 ++++++- app/models/project_services/hipchat_service.rb | 7 ++++++- app/models/project_services/issue_tracker_service.rb | 18 +++++++++++------- app/models/project_services/slack_service.rb | 7 ++++++- app/models/service.rb | 1 - 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index e01648f0596..b8bd48aca69 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -24,7 +24,12 @@ class BuildsEmailService < Service boolean_accessor :notify_only_broken_builds validates :recipients, presence: true, if: :activated? - default_value_for :notify_only_broken_builds, true + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end def title 'Builds emails' diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 4045d16fa22..1e1686a11c6 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -25,7 +25,12 @@ class HipchatService < Service boolean_accessor :notify_only_broken_builds validates :token, presence: true, if: :activated? - default_value_for :notify_only_broken_builds, true + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end def title 'HipChat' diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 46ba9808175..936e574cccd 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -56,12 +56,16 @@ class IssueTrackerService < Service end def initialize_properties - if new_record? + if properties.nil? if enabled_in_gitlab_config - self.title = issues_tracker['title'] - self.project_url = add_issues_tracker_id(issues_tracker['project_url']) - self.issues_url = add_issues_tracker_id(issues_tracker['issues_url']) - self.new_issue_url = add_issues_tracker_id(issues_tracker['new_issue_url']) + self.properties = { + title: issues_tracker['title'], + project_url: add_issues_tracker_id(issues_tracker['project_url']), + issues_url: add_issues_tracker_id(issues_tracker['issues_url']), + new_issue_url: add_issues_tracker_id(issues_tracker['new_issue_url']) + } + else + self.properties = {} end end end @@ -94,8 +98,8 @@ class IssueTrackerService < Service def enabled_in_gitlab_config Gitlab.config.issues_tracker && - Gitlab.config.issues_tracker.values.any? && - issues_tracker + Gitlab.config.issues_tracker.values.any? && + issues_tracker end def issues_tracker diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 553d4b025e1..375b4534d07 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -23,7 +23,12 @@ class SlackService < Service boolean_accessor :notify_only_broken_builds validates :webhook, presence: true, if: :activated? - default_value_for :notify_only_broken_builds, true + def initialize_properties + if properties.nil? + self.properties = {} + self.notify_only_broken_builds = true + end + end def title 'Slack' diff --git a/app/models/service.rb b/app/models/service.rb index 0ccb8b410d1..195c4690e8f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -31,7 +31,6 @@ class Service < ActiveRecord::Base default_value_for :tag_push_events, true default_value_for :note_events, true default_value_for :build_events, true - default_value_for :properties, {} after_initialize :initialize_properties -- cgit v1.2.1 From d1211e0ada61896c3a7a02c4fe7138705b086022 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 13:03:09 +0100 Subject: Fix award-emojis alert flash message This adds a new feature to `Flash` that allows to pin it after a specified selector. This removes a fixed position from such award-emoji alert, and makes it responsive by design of selector that this alert is pinned to. Closes #3996 --- app/assets/javascripts/flash.js.coffee | 4 ++-- app/assets/javascripts/notes.js.coffee | 2 +- app/assets/stylesheets/framework/flash.scss | 10 ---------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee index 9b59d4e57f7..5de012e409f 100644 --- a/app/assets/javascripts/flash.js.coffee +++ b/app/assets/javascripts/flash.js.coffee @@ -12,5 +12,5 @@ class @Flash @flash.click -> $(@).fadeOut() @flash.show() - pin: -> - @flash.addClass('flash-pinned flash-raised') + pinTo: (selector) -> + @flash.detach().appendTo(selector) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index b1df56b24fe..a7d720fff4b 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -114,7 +114,7 @@ class @Notes unless note.valid if note.award flash = new Flash('You have already used this award emoji!', 'alert') - flash.pin() + flash.pinTo('.header-content') return # render note if it not present in loaded list diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss index 1b723021d76..82eb50ad4be 100644 --- a/app/assets/stylesheets/framework/flash.scss +++ b/app/assets/stylesheets/framework/flash.scss @@ -15,13 +15,3 @@ @extend .alert-danger; } } - -.flash-pinned { - position: fixed; - top: 80px; - width: 80%; -} - -.flash-raised { - z-index: 1000; -} -- cgit v1.2.1 From ef37041ab6ac41ed1de3fc62c193566d779b105a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 13:23:59 +0100 Subject: Update changelog --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 3ec005c58f6..61068d3efb1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -44,6 +44,7 @@ v 8.3.0 (unreleased) - Upgraded Sidekiq to 4.x - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) - Fix emoji aliases problem + - Fix award-emojis Flash alert's width v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From cbeb06eb420f294b3a406f869f11de554048e93d Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 13:00:24 +0000 Subject: Mix url helpers in into `RepositoryPush` --- lib/gitlab/email/message/repository_push.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb index c53148d2ed8..a2eb7a70bd2 100644 --- a/lib/gitlab/email/message/repository_push.rb +++ b/lib/gitlab/email/message/repository_push.rb @@ -5,6 +5,8 @@ module Gitlab attr_accessor :recipient attr_reader :author_id, :ref, :action + include Gitlab::Application.routes.url_helpers + delegate :namespace, :name_with_namespace, to: :project, prefix: :project delegate :name, to: :author, prefix: :author @@ -16,7 +18,6 @@ module Gitlab @project_id = project_id @recipient = recipient @opts = opts.dup - @urls = Gitlab::Application.routes.url_helpers @author_id = @opts.delete(:author_id) @ref = @opts.delete(:ref) @@ -86,18 +87,18 @@ module Gitlab def target_url if @action == :push && commits if commits.length > 1 - @urls.namespace_project_compare_url(project_namespace, - project, - from: Commit.new(compare.base, project), - to: Commit.new(compare.head, project)) + namespace_project_compare_url(project_namespace, + project, + from: Commit.new(compare.base, project), + to: Commit.new(compare.head, project)) else - @urls.namespace_project_commit_url(project_namespace, - project, commits.first) + namespace_project_commit_url(project_namespace, + project, commits.first) end else unless @action == :delete - @urls.namespace_project_tree_url(project_namespace, - project, ref_name) + namespace_project_tree_url(project_namespace, + project, ref_name) end end end -- cgit v1.2.1 From 917effb7379f8e871dbc113d7c7dab89473d4bc8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 13:37:54 +0000 Subject: Make sure that token `ensure_*` method always returns a token --- app/models/concerns/token_authenticatable.rb | 8 ++++++-- spec/models/concerns/token_authenticatable_spec.rb | 9 ++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index fe764e8da41..56d38fe8250 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -17,8 +17,12 @@ module TokenAuthenticatable end define_method("ensure_#{token_field}") do - write_attribute(token_field, generate_token_for(token_field)) if - read_attribute(token_field).blank? + current_token = read_attribute(token_field) + if current_token.blank? + write_attribute(token_field, generate_token_for(token_field)) + else + current_token + end end define_method("reset_#{token_field}!") do diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb index 1b553173415..a9b0b64e5de 100644 --- a/spec/models/concerns/token_authenticatable_spec.rb +++ b/spec/models/concerns/token_authenticatable_spec.rb @@ -13,7 +13,7 @@ describe User, 'TokenAuthenticatable' do let(:token_field) { :authentication_token } it_behaves_like 'TokenAuthenticatable' - describe 'ensured authentication token' do + describe 'ensures authentication token' do subject { create(:user).send(token_field) } it { is_expected.to be_a String } end @@ -29,6 +29,13 @@ describe ApplicationSetting, 'TokenAuthenticatable' do context 'token is not generated yet' do it { expect(token).to be nil } + + describe 'ensured token' do + subject { described_class.new.send("ensure_#{token_field}") } + + it { is_expected.to be_a String } + it { is_expected.to_not be_blank } + end end context 'token is generated' do -- cgit v1.2.1 From 6b0c0d5bcc7940c7d84b8af965b2c4f9ceeb4175 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 14:43:44 +0100 Subject: Ensure that runners registration token is present --- app/views/ci/admin/runners/index.html.haml | 2 +- lib/ci/api/helpers.rb | 2 +- spec/features/ci/admin/runners_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml index 95bc8ebd40f..0574ed38015 100644 --- a/app/views/ci/admin/runners/index.html.haml +++ b/app/views/ci/admin/runners/index.html.haml @@ -3,7 +3,7 @@ To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. Registration token is - %code{ id: 'runners-token' } #{current_application_settings.runners_registration_token} + %code{ id: 'runners-token' } #{current_application_settings.ensure_runners_registration_token} .bs-callout.clearfix .pull-left diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 12d7396189d..abd1869efc0 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -23,7 +23,7 @@ module Ci end def runner_registration_token_valid? - params[:token] == current_application_settings.runners_registration_token + params[:token] == current_application_settings.ensure_runners_registration_token end def update_runner_last_contact diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb index 0c5aebb3a7b..fd1967d0698 100644 --- a/spec/features/ci/admin/runners_spec.rb +++ b/spec/features/ci/admin/runners_spec.rb @@ -63,7 +63,7 @@ describe "Admin Runners" do end describe 'runners registration token' do - let!(:token) { current_application_settings.runners_registration_token } + let!(:token) { current_application_settings.ensure_runners_registration_token } before { visit ci_admin_runners_path } it 'has a registration token' do -- cgit v1.2.1 From bc1cfd384f4f972ad887b0501280b9f94d4c94ac Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 11 Dec 2015 15:23:43 +0100 Subject: Stub also `ensure_*` method in CI runners API specs --- spec/requests/ci/api/runners_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 962d34a0f95..ef40d757665 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -8,6 +8,7 @@ describe Ci::API::API do before do stub_gitlab_calls + stub_application_setting(ensure_runners_registration_token: registration_token) stub_application_setting(runners_registration_token: registration_token) end -- cgit v1.2.1 From 5983e9bdf2aebb455aa7395541bb9d93d6d4de38 Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Fri, 11 Dec 2015 16:06:17 +0100 Subject: Minor fix in flow 'Merge when build succeeds' When a user, which is not the merge user, want to removes the source branch after the automatic merge this might fail because of checks in the DeleteBranchService --- .../merge_requests/widget/open/_merge_when_build_succeeds.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml index 08af124274b..2168294c683 100644 --- a/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml +++ b/app/views/projects/merge_requests/widget/open/_merge_when_build_succeeds.html.haml @@ -12,7 +12,7 @@ - else The source branch will not be removed. - - remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch + - remove_source_branch_button = @merge_request.can_remove_source_branch?(current_user) && !should_remove_source_branch && @merge_request.merge_user == current_user - user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user) - if remove_source_branch_button || user_can_cancel_automatic_merge .clearfix.prepend-top-10 -- cgit v1.2.1 From eec4751bd0865498a925e89930465b40ea849ea2 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 11 Dec 2015 16:34:07 +0200 Subject: Add hover-state for emoji in emoji-picker --- CHANGELOG | 1 + app/assets/stylesheets/pages/issuable.scss | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 61068d3efb1..155048b95c9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -45,6 +45,7 @@ v 8.3.0 (unreleased) - Accept COPYING,COPYING.lesser, and licence as license file (Zeger-Jan van de Weg) - Fix emoji aliases problem - Fix award-emojis Flash alert's width + - Emoji picker improvements v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 957da5c182e..5b5e73e465d 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -129,10 +129,18 @@ border-color: $border-color; cursor: pointer; + &:hover { + background-color: #dce0e5; + } + &.active { border-color: $border-gray-light; background-color: $gray-light; + &:hover { + background-color: #dce0e5; + } + .counter { font-weight: bold; } @@ -170,7 +178,18 @@ > li { cursor: pointer; - margin: 5px; + width: 30px; + height: 30px; + text-align: center; + @include border-radius(5px); + + img { + margin-bottom: 2px; + } + + &:hover { + background-color: #ccc; + } } } } -- cgit v1.2.1 From 22999a6fa6b44a835536307da8e5d6a6b349cb49 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 11 Dec 2015 16:16:07 +0100 Subject: Revert "Merge branch 'remove-redcloth' into 'master' " This reverts commit e426c027b0a2a3aa0dea1d833008f2bfd814f483, reversing changes made to c3676aa156981092b7f03f1a3e74bb819cfa2fc3. --- .gitlab-ci.yml | 3 ++- Gemfile | 1 + Gemfile.lock | 2 ++ spec/features/atom/users_spec.rb | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c614e14e243..a8da3de83f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -117,9 +117,10 @@ flay: - mysql bundler:audit: - script: + script: - "bundle exec bundle-audit update" - "bundle exec bundle-audit check" tags: - ruby - mysql + allow_failure: true diff --git a/Gemfile b/Gemfile index 582a1624f05..7298e21ce66 100644 --- a/Gemfile +++ b/Gemfile @@ -93,6 +93,7 @@ gem 'html-pipeline', '~> 1.11.0' gem 'task_list', '~> 1.0.2', require: 'task_list/railtie' gem 'github-markup', '~> 1.3.1' gem 'redcarpet', '~> 3.3.3' +gem 'RedCloth', '~> 4.2.9' gem 'rdoc', '~>3.6' gem 'org-ruby', '~> 0.9.12' gem 'creole', '~> 0.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0db1c6760f8..ff57460f5bb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (2.3.2) + RedCloth (4.2.9) ace-rails-ap (2.0.1) actionmailer (4.2.4) actionpack (= 4.2.4) @@ -826,6 +827,7 @@ PLATFORMS ruby DEPENDENCIES + RedCloth (~> 4.2.9) ace-rails-ap (~> 2.0.1) activerecord-deprecated_finders (~> 1.0.3) activerecord-session_store (~> 0.1.0) diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb index 52134556339..dc41be8246f 100644 --- a/spec/features/atom/users_spec.rb +++ b/spec/features/atom/users_spec.rb @@ -79,6 +79,6 @@ describe "User Feed", feature: true do end def safe_name - CGI.escapeHTML(user.name) + html_escape(user.name) end end -- cgit v1.2.1 From d597a0a21ab7a589eb8c959e3062632f472ee099 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Fri, 11 Dec 2015 16:42:40 +0100 Subject: Pass all requests from NGINX to gitlab-workhorse --- lib/support/nginx/gitlab | 146 +----------------------------------------- lib/support/nginx/gitlab-ssl | 147 +------------------------------------------ 2 files changed, 3 insertions(+), 290 deletions(-) diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 2a79fbdcf93..fc5475c4eef 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -10,34 +10,12 @@ ## If you change this file in a Merge Request, please also create ## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests ## -################################## -## CHUNKED TRANSFER ## -################################## -## -## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0] -## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object -## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get -## around this by tweaking this configuration file and either: -## - installing an old version of Nginx with the chunkin module [2] compiled in, or -## - using a newer version of Nginx. -## -## At the time of writing we do not know if either of these theoretical solutions works. -## As a workaround users can use Git over SSH to push large files. -## -## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99 -## [1] https://github.com/agentzh/chunkin-nginx-module#status -## [2] https://github.com/agentzh/chunkin-nginx-module -## ################################### ## configuration ## ################################### ## ## See installation.md#using-https for additional HTTPS configuration details. -upstream gitlab { - server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; -} - upstream gitlab-workhorse { server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } @@ -54,10 +32,6 @@ server { server_tokens off; ## Don't show the nginx version number, a security best practice root /home/git/gitlab/public; - ## Increase this if you want to upload large attachments - ## Or if you want to accept large git objects over http - client_max_body_size 20m; - ## See app/controllers/application_controller.rb for headers set ## Individual nginx logs for this GitLab vhost @@ -65,103 +39,8 @@ server { error_log /var/log/nginx/gitlab_error.log; location / { - ## Serve static files from defined root folder. - ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri /index.html $uri.html @gitlab; - } - - ## We route uploads through GitLab to prevent XSS and enforce access control. - location /uploads/ { - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. - # gzip off; - - ## https://github.com/gitlabhq/gitlabhq/issues/694 - ## Some requests take more than 30 seconds. - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - - proxy_pass http://gitlab; - } - - ## If a file, which is not found in the root folder is requested, - ## then the proxy passes the request to the upsteam (gitlab unicorn). - location @gitlab { - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. - # gzip off; - - ## https://github.com/gitlabhq/gitlabhq/issues/694 - ## Some requests take more than 30 seconds. - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - - proxy_pass http://gitlab; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/api/v3/projects/.*/repository/archive { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - # Build artifacts should be submitted to this location - location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - # Build artifacts should be submitted to this location - location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location @gitlab-workhorse { - client_max_body_size 0; - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. - # gzip off; + gzip off; ## https://github.com/gitlabhq/gitlabhq/issues/694 ## Some requests take more than 30 seconds. @@ -169,14 +48,7 @@ server { proxy_connect_timeout 300; proxy_redirect off; - # Do not buffer Git HTTP responses - proxy_buffering off; - - # The following settings only work with NGINX 1.7.11 or newer - # - # # Pass chunked request bodies to gitlab-workhorse as-is - # proxy_request_buffering off; - # proxy_http_version 1.1; + proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; @@ -185,18 +57,4 @@ server { proxy_pass http://gitlab-workhorse; } - - ## Enable gzip compression as per rails guide: - ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression - ## WARNING: If you are using relative urls remove the block below - ## See config/application.rb under "Relative url support" for the list of - ## other files that need to be changed for relative url support - location ~ ^/(assets)/ { - root /home/git/gitlab/public; - gzip_static on; # to serve pre-gzipped version - expires max; - add_header Cache-Control public; - } - - error_page 502 /502.html; } diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 79fe1474821..1e5f85413ec 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -14,34 +14,12 @@ ## If you change this file in a Merge Request, please also create ## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests ## -################################## -## CHUNKED TRANSFER ## -################################## -## -## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0] -## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object -## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get -## around this by tweaking this configuration file and either: -## - installing an old version of Nginx with the chunkin module [2] compiled in, or -## - using a newer version of Nginx. -## -## At the time of writing we do not know if either of these theoretical solutions works. -## As a workaround users can use Git over SSH to push large files. -## -## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99 -## [1] https://github.com/agentzh/chunkin-nginx-module#status -## [2] https://github.com/agentzh/chunkin-nginx-module -## ################################### ## configuration ## ################################### ## ## See installation.md#using-https for additional HTTPS configuration details. -upstream gitlab { - server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; -} - upstream gitlab-workhorse { server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0; } @@ -61,7 +39,6 @@ server { error_log /var/log/nginx/gitlab_error.log; } - ## HTTPS host server { listen 0.0.0.0:443 ssl; @@ -70,10 +47,6 @@ server { server_tokens off; ## Don't show the nginx version number, a security best practice root /home/git/gitlab/public; - ## Increase this if you want to upload large attachments - ## Or if you want to accept large git objects over http - client_max_body_size 20m; - ## Strong SSL Security ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/ ssl on; @@ -110,104 +83,7 @@ server { error_log /var/log/nginx/gitlab_error.log; location / { - ## Serve static files from defined root folder. - ## @gitlab is a named location for the upstream fallback, see below. - try_files $uri /index.html $uri.html @gitlab; - } - - ## We route uploads through GitLab to prevent XSS and enforce access control. - location /uploads/ { - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. - gzip off; - - ## https://github.com/gitlabhq/gitlabhq/issues/694 - ## Some requests take more than 30 seconds. - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Ssl on; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - - proxy_pass http://gitlab; - } - - ## If a file, which is not found in the root folder is requested, - ## then the proxy passes the request to the upsteam (gitlab unicorn). - location @gitlab { - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. - gzip off; - - ## https://github.com/gitlabhq/gitlabhq/issues/694 - ## Some requests take more than 30 seconds. - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Ssl on; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Frame-Options SAMEORIGIN; - - proxy_pass http://gitlab; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive { client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location ~ ^/api/v3/projects/.*/repository/archive { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - # Build artifacts should be submitted to this location - location ~ ^/[\w\.-]+/[\w\.-]+/builds/download { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - # Build artifacts should be submitted to this location - location ~ /ci/api/v1/builds/[0-9]+/artifacts { - client_max_body_size 0; - # 'Error' 418 is a hack to re-use the @gitlab-workhorse block - error_page 418 = @gitlab-workhorse; - return 418; - } - - location @gitlab-workhorse { - client_max_body_size 0; - ## If you use HTTPS make sure you disable gzip compression - ## to be safe against BREACH attack. gzip off; ## https://github.com/gitlabhq/gitlabhq/issues/694 @@ -216,14 +92,7 @@ server { proxy_connect_timeout 300; proxy_redirect off; - # Do not buffer Git HTTP responses - proxy_buffering off; - - # The following settings only work with NGINX 1.7.11 or newer - # - # # Pass chunked request bodies to gitlab-workhorse as-is - # proxy_request_buffering off; - # proxy_http_version 1.1; + proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; @@ -232,18 +101,4 @@ server { proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://gitlab-workhorse; } - - ## Enable gzip compression as per rails guide: - ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression - ## WARNING: If you are using relative urls remove the block below - ## See config/application.rb under "Relative url support" for the list of - ## other files that need to be changed for relative url support - location ~ ^/(assets)/ { - root /home/git/gitlab/public; - gzip_static on; # to serve pre-gzipped version - expires max; - add_header Cache-Control public; - } - - error_page 502 /502.html; } -- cgit v1.2.1 From 0272f27401d25faed97419611a78a968f801a42f Mon Sep 17 00:00:00 2001 From: Greg Smethells Date: Fri, 4 Dec 2015 13:00:07 -0600 Subject: display referenced merge requests in issue description with CI status --- app/assets/stylesheets/pages/issues.scss | 20 +++++++++++++++++ app/controllers/projects/issues_controller.rb | 1 + app/helpers/ci_status_helper.rb | 2 +- app/models/issue.rb | 8 +++++++ .../projects/issues/_merge_requests.html.haml | 25 ++++++++++++++++++++++ app/views/projects/issues/show.html.haml | 4 ++++ 6 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 app/views/projects/issues/_merge_requests.html.haml diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index f5548c5b88b..a652b65502f 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -60,6 +60,26 @@ form.edit-issue { margin: 0; } +.merge-requests-title { + font-size: 16px; + font-weight: 600; +} + +.merge-request-id { + display: inline-block; + width: 3em; +} + +.merge-request-info { + padding-left: 5px; +} + +.merge-request-status { + color: $gl-gray; + font-size: 15px; + font-weight: bold; +} + .merge-request, .issue { &.today { diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index ae474cf8d68..cf617d53ed6 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -62,6 +62,7 @@ class Projects::IssuesController < Projects::ApplicationController @note = @project.notes.new(noteable: @issue) @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue + @merge_requests = @issue.referenced_merge_requests respond_with(@issue) end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e1f8f9ba6d..f8f2cbf1319 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -52,7 +52,7 @@ module CiStatusHelper 'circle' end - icon(icon_name) + icon(icon_name + ' fw') end def render_ci_status(ci_commit) diff --git a/app/models/issue.rb b/app/models/issue.rb index 187b6482b6c..e04035b3af8 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -83,6 +83,14 @@ class Issue < ActiveRecord::Base reference end + def referenced_merge_requests + references = [self, *notes].flat_map do |note| + note.all_references(load_lazy_references: false).merge_requests + end.uniq + + Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid) + end + # Reset issue events cache # # Since we do cache @event we need to reset cache in special cases: diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml new file mode 100644 index 00000000000..fe856ac991e --- /dev/null +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -0,0 +1,25 @@ +-if @merge_requests.any? + %h2.merge-requests-title + = pluralize(@merge_requests.count, 'Related Merge Request') + %ul.bordered-list + - has_any_ci = @merge_requests.any?(&:ci_commit) + - @merge_requests.each do |merge_request| + %li + %span.merge-request-ci-status + - if merge_request.ci_commit + = render_ci_status(merge_request.ci_commit) + - elsif has_any_ci + = icon('blank fw') + %span.merge-request-id + \##{merge_request.iid} + %span.merge-request-info + %strong + = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" + in + - project = merge_request.target_project + = link_to project.name_with_namespace, namespace_project_path(project.namespace, project) + %span.merge-request-status.prepend-left-10 + - if merge_request.merged? + MERGED + - elsif merge_request.closed? + CLOSED diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index a78d20cc07e..38254403fa6 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -47,6 +47,10 @@ = markdown(@issue.description, cache_key: [@issue, "description"]) %textarea.hidden.js-task-list-field = @issue.description + + .merge-requests + = render 'merge_requests' + - if @closed_by_merge_requests.present? = render 'projects/issues/closed_by_box' .issue-discussion -- cgit v1.2.1 From 8b4cdc50fca816b4f56f8579e17c4dba836ec797 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 18:01:57 +0100 Subject: Fix indentation and BuildsEmailService --- app/models/project_services/builds_email_service.rb | 3 +-- app/models/service.rb | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index b8bd48aca69..8247c79fc33 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -79,8 +79,7 @@ class BuildsEmailService < Service end def all_recipients(data) - all_recipients = [] - all_recipients <<= recipients.split(',') + all_recipients = recipients.split(',') if add_pusher? && data[:user][:email] all_recipients << "#{data[:user][:email]}" diff --git a/app/models/service.rb b/app/models/service.rb index 195c4690e8f..4159e367d8c 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -143,10 +143,10 @@ class Service < ActiveRecord::Base args.each do |arg| class_eval %{ - def #{arg}? - ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) - end - } + def #{arg}? + ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg}) + end + } end end -- cgit v1.2.1 From e80e3f5372d6bcad1fbe04a85b3086bb66794828 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 4 Dec 2015 12:55:23 +0100 Subject: Migrate CI::Project to Project --- app/controllers/admin/builds_controller.rb | 23 ++ .../admin/runner_projects_controller.rb | 35 +++ app/controllers/admin/runners_controller.rb | 63 ++++++ app/controllers/ci/admin/application_controller.rb | 10 - .../ci/admin/application_settings_controller.rb | 31 --- app/controllers/ci/admin/builds_controller.rb | 18 -- app/controllers/ci/admin/events_controller.rb | 9 - app/controllers/ci/admin/projects_controller.rb | 19 -- .../ci/admin/runner_projects_controller.rb | 34 --- app/controllers/ci/admin/runners_controller.rb | 73 ------ app/controllers/ci/application_controller.rb | 12 +- app/controllers/ci/lints_controller.rb | 2 +- app/controllers/ci/projects_controller.rb | 12 +- app/controllers/ci/runner_projects_controller.rb | 34 --- app/controllers/projects/application_controller.rb | 4 - app/controllers/projects/builds_controller.rb | 7 +- app/controllers/projects/ci_settings_controller.rb | 36 --- app/controllers/projects/commit_controller.rb | 1 - app/controllers/projects/graphs_controller.rb | 10 +- .../projects/runner_projects_controller.rb | 26 +++ app/controllers/projects/runners_controller.rb | 15 +- app/controllers/projects/triggers_controller.rb | 9 +- app/controllers/projects/variables_controller.rb | 7 +- app/controllers/projects_controller.rb | 4 +- app/helpers/ci/gitlab_helper.rb | 17 -- app/helpers/ci/projects_helper.rb | 36 --- app/helpers/ci_badge_helper.rb | 11 + app/helpers/ci_status_helper.rb | 7 +- app/helpers/graph_helper.rb | 10 + app/helpers/runners_helper.rb | 2 +- app/helpers/yaml_helper.rb | 11 + app/models/ci/application_setting.rb | 38 ---- app/models/ci/build.rb | 51 ++--- app/models/ci/commit.rb | 22 +- app/models/ci/event.rb | 27 --- app/models/ci/project.rb | 151 ------------- app/models/ci/project_status.rb | 31 --- app/models/ci/runner.rb | 8 +- app/models/ci/runner_project.rb | 4 +- app/models/ci/trigger.rb | 2 +- app/models/ci/variable.rb | 4 +- app/models/commit_status.rb | 4 +- app/models/project.rb | 72 +++--- app/models/project_services/gitlab_ci_service.rb | 73 +----- app/models/service.rb | 3 +- app/models/user.rb | 5 +- app/services/ci/create_builds_service.rb | 3 +- app/services/ci/create_commit_service.rb | 2 +- app/services/ci/create_trigger_request_service.rb | 6 +- app/services/ci/event_service.rb | 31 --- app/services/ci/image_for_build_service.rb | 4 +- app/services/ci/register_build_service.rb | 9 +- app/services/ci/test_hook_service.rb | 7 - app/services/projects/fork_service.rb | 15 +- app/views/admin/builds/_build.html.haml | 73 ++++++ app/views/admin/builds/index.html.haml | 50 +++++ app/views/admin/runners/_runner.html.haml | 48 ++++ app/views/admin/runners/index.html.haml | 53 +++++ app/views/admin/runners/show.html.haml | 125 +++++++++++ app/views/admin/runners/update.js.haml | 2 + .../ci/admin/application_settings/_form.html.haml | 24 -- .../ci/admin/application_settings/show.html.haml | 3 - app/views/ci/admin/builds/_build.html.haml | 34 --- app/views/ci/admin/builds/index.html.haml | 28 --- app/views/ci/admin/events/index.html.haml | 18 -- app/views/ci/admin/projects/_project.html.haml | 29 --- app/views/ci/admin/projects/index.html.haml | 16 -- app/views/ci/admin/runner_projects/index.html.haml | 57 ----- app/views/ci/admin/runners/_runner.html.haml | 48 ---- app/views/ci/admin/runners/index.html.haml | 53 ----- app/views/ci/admin/runners/show.html.haml | 130 ----------- app/views/ci/admin/runners/update.js.haml | 2 - app/views/ci/commits/_commit.html.haml | 5 +- app/views/ci/shared/_guide.html.haml | 2 +- app/views/ci/user_sessions/new.html.haml | 7 - app/views/layouts/ci/_nav_admin.html.haml | 35 --- app/views/layouts/ci/_nav_project.html.haml | 12 - app/views/layouts/ci/admin.html.haml | 11 - app/views/layouts/ci/application.html.haml | 11 - app/views/layouts/nav/_admin.html.haml | 15 +- app/views/layouts/nav/_project_settings.html.haml | 5 - app/views/notify/build_fail_email.html.haml | 4 +- app/views/notify/build_fail_email.text.erb | 2 +- app/views/notify/build_success_email.html.haml | 4 +- app/views/notify/build_success_email.text.erb | 2 +- app/views/projects/builds/index.html.haml | 4 +- app/views/projects/builds/show.html.haml | 4 +- app/views/projects/ci_settings/_form.html.haml | 120 ---------- .../projects/ci_settings/_no_runners.html.haml | 8 - app/views/projects/ci_settings/edit.html.haml | 6 - app/views/projects/commit/_builds.html.haml | 18 +- .../commit_statuses/_commit_status.html.haml | 2 +- app/views/projects/edit.html.haml | 56 +++++ app/views/projects/graphs/ci/_overall.haml | 11 +- app/views/projects/runners/_runner.html.haml | 6 +- .../projects/runners/_shared_runners.html.haml | 6 +- .../projects/runners/_specific_runners.html.haml | 2 +- app/views/projects/triggers/index.html.haml | 6 +- app/views/projects/variables/show.html.haml | 10 +- config/routes.rb | 55 ++--- db/migrate/20151203162135_add_ci_to_project.rb | 10 + db/migrate/20151204110124_add_project_id_to_ci.rb | 8 + db/migrate/20151204110613_migrate_ci_to_project.rb | 36 +++ .../20151204110832_add_index_to_ci_tables.rb | 11 + .../20151204123933_drop_null_for_ci_tables.rb | 9 + db/schema.rb | 29 ++- doc/api/projects.md | 3 - features/project/service.feature | 6 - features/steps/project/commits/commits.rb | 2 +- features/steps/project/merge_requests.rb | 2 +- features/steps/project/services.rb | 10 - features/steps/shared/project.rb | 2 +- lib/api/commit_statuses.rb | 2 +- lib/api/entities.rb | 1 + lib/api/helpers.rb | 7 +- lib/api/projects.rb | 6 + lib/ci/api/api.rb | 2 - lib/ci/api/commits.rb | 66 ------ lib/ci/api/projects.rb | 157 ------------- lib/ci/api/runners.rb | 15 +- lib/ci/api/triggers.rb | 2 +- lib/ci/charts.rb | 6 +- lib/ci/current_settings.rb | 22 -- lib/ci/scheduler.rb | 16 -- lib/gitlab/backend/grack_auth.rb | 4 +- lib/gitlab/build_data_builder.rb | 2 +- lib/tasks/ci/schedule_builds.rake | 6 - spec/factories/ci/builds.rb | 4 + spec/factories/ci/commits.rb | 2 +- spec/factories/ci/events.rb | 24 -- spec/factories/ci/projects.rb | 50 ----- spec/factories/ci/runner_projects.rb | 2 +- spec/features/atom/builds_spec.rb | 69 ++++++ spec/features/atom/runners_spec.rb | 64 ++++++ spec/features/builds_spec.rb | 24 +- spec/features/ci/admin/builds_spec.rb | 70 ------ spec/features/ci/admin/events_spec.rb | 20 -- spec/features/ci/admin/projects_spec.rb | 19 -- spec/features/ci/admin/runners_spec.rb | 64 ------ spec/features/ci/lint_spec.rb | 28 --- spec/features/ci_settings_spec.rb | 22 -- spec/features/commits_spec.rb | 3 +- spec/features/lint_spec.rb | 28 +++ spec/features/runners_spec.rb | 38 ++-- spec/features/triggers_spec.rb | 13 +- spec/features/variables_spec.rb | 9 +- spec/lib/gitlab/backend/grack_auth_spec.rb | 9 +- spec/lib/gitlab/build_data_builder_spec.rb | 4 +- spec/models/application_setting_spec.rb | 22 -- spec/models/build_spec.rb | 89 +++----- spec/models/ci/commit_spec.rb | 28 ++- spec/models/ci/project_spec.rb | 244 --------------------- spec/models/ci/runner_spec.rb | 14 +- spec/models/ci/trigger_spec.rb | 2 +- spec/models/commit_status_spec.rb | 2 +- .../project_services/gitlab_ci_service_spec.rb | 57 ----- spec/models/project_spec.rb | 89 +++++++- spec/requests/api/projects_spec.rb | 12 - spec/requests/ci/api/builds_spec.rb | 28 ++- spec/requests/ci/api/commits_spec.rb | 65 ------ spec/requests/ci/api/projects_spec.rb | 159 -------------- spec/requests/ci/api/runners_spec.rb | 27 +-- spec/requests/ci/api/triggers_spec.rb | 27 ++- spec/services/ci/create_commit_service_spec.rb | 4 +- .../ci/create_trigger_request_service_spec.rb | 5 +- spec/services/ci/event_service_spec.rb | 34 --- spec/services/ci/image_for_build_service_spec.rb | 8 +- spec/services/ci/register_build_service_spec.rb | 10 +- 168 files changed, 1314 insertions(+), 3002 deletions(-) create mode 100644 app/controllers/admin/builds_controller.rb create mode 100644 app/controllers/admin/runner_projects_controller.rb create mode 100644 app/controllers/admin/runners_controller.rb delete mode 100644 app/controllers/ci/admin/application_controller.rb delete mode 100644 app/controllers/ci/admin/application_settings_controller.rb delete mode 100644 app/controllers/ci/admin/builds_controller.rb delete mode 100644 app/controllers/ci/admin/events_controller.rb delete mode 100644 app/controllers/ci/admin/projects_controller.rb delete mode 100644 app/controllers/ci/admin/runner_projects_controller.rb delete mode 100644 app/controllers/ci/admin/runners_controller.rb delete mode 100644 app/controllers/ci/runner_projects_controller.rb delete mode 100644 app/controllers/projects/ci_settings_controller.rb create mode 100644 app/controllers/projects/runner_projects_controller.rb delete mode 100644 app/helpers/ci/gitlab_helper.rb delete mode 100644 app/helpers/ci/projects_helper.rb create mode 100644 app/helpers/ci_badge_helper.rb create mode 100644 app/helpers/yaml_helper.rb delete mode 100644 app/models/ci/application_setting.rb delete mode 100644 app/models/ci/event.rb delete mode 100644 app/models/ci/project.rb delete mode 100644 app/models/ci/project_status.rb delete mode 100644 app/services/ci/event_service.rb delete mode 100644 app/services/ci/test_hook_service.rb create mode 100644 app/views/admin/builds/_build.html.haml create mode 100644 app/views/admin/builds/index.html.haml create mode 100644 app/views/admin/runners/_runner.html.haml create mode 100644 app/views/admin/runners/index.html.haml create mode 100644 app/views/admin/runners/show.html.haml create mode 100644 app/views/admin/runners/update.js.haml delete mode 100644 app/views/ci/admin/application_settings/_form.html.haml delete mode 100644 app/views/ci/admin/application_settings/show.html.haml delete mode 100644 app/views/ci/admin/builds/_build.html.haml delete mode 100644 app/views/ci/admin/builds/index.html.haml delete mode 100644 app/views/ci/admin/events/index.html.haml delete mode 100644 app/views/ci/admin/projects/_project.html.haml delete mode 100644 app/views/ci/admin/projects/index.html.haml delete mode 100644 app/views/ci/admin/runner_projects/index.html.haml delete mode 100644 app/views/ci/admin/runners/_runner.html.haml delete mode 100644 app/views/ci/admin/runners/index.html.haml delete mode 100644 app/views/ci/admin/runners/show.html.haml delete mode 100644 app/views/ci/admin/runners/update.js.haml delete mode 100644 app/views/ci/user_sessions/new.html.haml delete mode 100644 app/views/layouts/ci/_nav_admin.html.haml delete mode 100644 app/views/layouts/ci/_nav_project.html.haml delete mode 100644 app/views/layouts/ci/admin.html.haml delete mode 100644 app/views/layouts/ci/application.html.haml delete mode 100644 app/views/projects/ci_settings/_form.html.haml delete mode 100644 app/views/projects/ci_settings/_no_runners.html.haml delete mode 100644 app/views/projects/ci_settings/edit.html.haml create mode 100644 db/migrate/20151203162135_add_ci_to_project.rb create mode 100644 db/migrate/20151204110124_add_project_id_to_ci.rb create mode 100644 db/migrate/20151204110613_migrate_ci_to_project.rb create mode 100644 db/migrate/20151204110832_add_index_to_ci_tables.rb create mode 100644 db/migrate/20151204123933_drop_null_for_ci_tables.rb delete mode 100644 lib/ci/api/commits.rb delete mode 100644 lib/ci/api/projects.rb delete mode 100644 lib/ci/current_settings.rb delete mode 100644 lib/ci/scheduler.rb delete mode 100644 lib/tasks/ci/schedule_builds.rake delete mode 100644 spec/factories/ci/events.rb delete mode 100644 spec/factories/ci/projects.rb create mode 100644 spec/features/atom/builds_spec.rb create mode 100644 spec/features/atom/runners_spec.rb delete mode 100644 spec/features/ci/admin/builds_spec.rb delete mode 100644 spec/features/ci/admin/events_spec.rb delete mode 100644 spec/features/ci/admin/projects_spec.rb delete mode 100644 spec/features/ci/admin/runners_spec.rb delete mode 100644 spec/features/ci/lint_spec.rb delete mode 100644 spec/features/ci_settings_spec.rb create mode 100644 spec/features/lint_spec.rb delete mode 100644 spec/models/ci/project_spec.rb delete mode 100644 spec/models/project_services/gitlab_ci_service_spec.rb delete mode 100644 spec/requests/ci/api/commits_spec.rb delete mode 100644 spec/requests/ci/api/projects_spec.rb delete mode 100644 spec/services/ci/event_service_spec.rb diff --git a/app/controllers/admin/builds_controller.rb b/app/controllers/admin/builds_controller.rb new file mode 100644 index 00000000000..83d9684c706 --- /dev/null +++ b/app/controllers/admin/builds_controller.rb @@ -0,0 +1,23 @@ +class Admin::BuildsController < Admin::ApplicationController + def index + @scope = params[:scope] + @all_builds = Ci::Build + @builds = @all_builds.order('created_at DESC') + @builds = + case @scope + when 'all' + @builds + when 'finished' + @builds.finished + else + @builds.running_or_pending.reverse_order + end + @builds = @builds.page(params[:page]).per(30) + end + + def cancel_all + Ci::Build.running_or_pending.each(&:cancel) + + redirect_to admin_builds_path + end +end diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb new file mode 100644 index 00000000000..20d621742f9 --- /dev/null +++ b/app/controllers/admin/runner_projects_controller.rb @@ -0,0 +1,35 @@ +class Admin::RunnerProjectsController < Admin::ApplicationController + before_action :project, only: [:create] + + def index + @runner_projects = project.ci_runner_projects.all + @runner_project = project.ci_runner_projects.new + end + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + if @runner.assign_to(@project, current_user) + redirect_to admin_runner_path(@runner) + else + redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project' + end + end + + def destroy + rp = Ci::RunnerProject.find(params[:id]) + runner = rp.runner + rp.destroy + + redirect_to admin_runner_path(runner) + end + + private + + def project + @project = Project.find_with_namespace( + [params[:namespace_id], '/', params[:project_id]].join('') + ) + @project || render_404 + end +end diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb new file mode 100644 index 00000000000..a701d49b844 --- /dev/null +++ b/app/controllers/admin/runners_controller.rb @@ -0,0 +1,63 @@ +class Admin::RunnersController < Admin::ApplicationController + before_action :runner, except: :index + + def index + @runners = Ci::Runner.order('id DESC') + @runners = @runners.search(params[:search]) if params[:search].present? + @runners = @runners.page(params[:page]).per(30) + @active_runners_cnt = Ci::Runner.online.count + end + + def show + @builds = @runner.builds.order('id DESC').first(30) + @projects = + if params[:search].present? + ::Project.search(params[:search]) + else + Project.all + end + @projects = @projects.where.not(id: @runner.projects.select(:id)) if @runner.projects.any? + @projects = @projects.page(params[:page]).per(30) + end + + def update + @runner.update_attributes(runner_params) + + respond_to do |format| + format.js + format.html { redirect_to admin_runner_path(@runner) } + end + end + + def destroy + @runner.destroy + + redirect_to admin_runners_path + end + + def resume + if @runner.update_attributes(active: true) + redirect_to admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to admin_runners_path, alert: 'Runner was not updated.' + end + end + + def pause + if @runner.update_attributes(active: false) + redirect_to admin_runners_path, notice: 'Runner was successfully updated.' + else + redirect_to admin_runners_path, alert: 'Runner was not updated.' + end + end + + private + + def runner + @runner ||= Ci::Runner.find(params[:id]) + end + + def runner_params + params.require(:runner).permit(:token, :description, :tag_list, :active) + end +end diff --git a/app/controllers/ci/admin/application_controller.rb b/app/controllers/ci/admin/application_controller.rb deleted file mode 100644 index 4ec2dc9c2cf..00000000000 --- a/app/controllers/ci/admin/application_controller.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Ci - module Admin - class ApplicationController < Ci::ApplicationController - before_action :authenticate_user! - before_action :authenticate_admin! - - layout "ci/admin" - end - end -end diff --git a/app/controllers/ci/admin/application_settings_controller.rb b/app/controllers/ci/admin/application_settings_controller.rb deleted file mode 100644 index 71e253fac67..00000000000 --- a/app/controllers/ci/admin/application_settings_controller.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - class Admin::ApplicationSettingsController < Ci::Admin::ApplicationController - before_action :set_application_setting - - def show - end - - def update - if @application_setting.update_attributes(application_setting_params) - redirect_to ci_admin_application_settings_path, - notice: 'Application settings saved successfully' - else - render :show - end - end - - private - - def set_application_setting - @application_setting = Ci::ApplicationSetting.current - @application_setting ||= Ci::ApplicationSetting.create_from_defaults - end - - def application_setting_params - params.require(:application_setting).permit( - :all_broken_builds, - :add_pusher, - ) - end - end -end diff --git a/app/controllers/ci/admin/builds_controller.rb b/app/controllers/ci/admin/builds_controller.rb deleted file mode 100644 index 38abfdeafbf..00000000000 --- a/app/controllers/ci/admin/builds_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Ci - class Admin::BuildsController < Ci::Admin::ApplicationController - def index - @scope = params[:scope] - @builds = Ci::Build.order('created_at DESC').page(params[:page]).per(30) - - @builds = - case @scope - when "pending" - @builds.pending - when "running" - @builds.running - else - @builds - end - end - end -end diff --git a/app/controllers/ci/admin/events_controller.rb b/app/controllers/ci/admin/events_controller.rb deleted file mode 100644 index 5939efff980..00000000000 --- a/app/controllers/ci/admin/events_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Ci - class Admin::EventsController < Ci::Admin::ApplicationController - EVENTS_PER_PAGE = 50 - - def index - @events = Ci::Event.admin.order('created_at DESC').page(params[:page]).per(EVENTS_PER_PAGE) - end - end -end diff --git a/app/controllers/ci/admin/projects_controller.rb b/app/controllers/ci/admin/projects_controller.rb deleted file mode 100644 index 5bbd0ce7396..00000000000 --- a/app/controllers/ci/admin/projects_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Ci - class Admin::ProjectsController < Ci::Admin::ApplicationController - def index - @projects = Ci::Project.ordered_by_last_commit_date.page(params[:page]).per(30) - end - - def destroy - project.destroy - - redirect_to ci_projects_url - end - - protected - - def project - @project ||= Ci::Project.find(params[:id]) - end - end -end diff --git a/app/controllers/ci/admin/runner_projects_controller.rb b/app/controllers/ci/admin/runner_projects_controller.rb deleted file mode 100644 index e7de6eb12ca..00000000000 --- a/app/controllers/ci/admin/runner_projects_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Ci - class Admin::RunnerProjectsController < Ci::Admin::ApplicationController - layout 'ci/project' - - def index - @runner_projects = project.runner_projects.all - @runner_project = project.runner_projects.new - end - - def create - @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - - if @runner.assign_to(project, current_user) - redirect_to ci_admin_runner_path(@runner) - else - redirect_to ci_admin_runner_path(@runner), alert: 'Failed adding runner to project' - end - end - - def destroy - rp = Ci::RunnerProject.find(params[:id]) - runner = rp.runner - rp.destroy - - redirect_to ci_admin_runner_path(runner) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/ci/admin/runners_controller.rb b/app/controllers/ci/admin/runners_controller.rb deleted file mode 100644 index 0cafad27418..00000000000 --- a/app/controllers/ci/admin/runners_controller.rb +++ /dev/null @@ -1,73 +0,0 @@ -module Ci - class Admin::RunnersController < Ci::Admin::ApplicationController - before_action :runner, except: :index - - def index - @runners = Ci::Runner.order('id DESC') - @runners = @runners.search(params[:search]) if params[:search].present? - @runners = @runners.page(params[:page]).per(30) - @active_runners_cnt = Ci::Runner.online.count - end - - def show - @builds = @runner.builds.order('id DESC').first(30) - @projects = Ci::Project.all - if params[:search].present? - @gl_projects = ::Project.search(params[:search]) - @projects = @projects.where(gitlab_id: @gl_projects.select(:id)) - end - @projects = @projects.where("ci_projects.id NOT IN (?)", @runner.projects.pluck(:id)) if @runner.projects.any? - @projects = @projects.joins(:gl_project) - @projects = @projects.page(params[:page]).per(30) - end - - def update - @runner.update_attributes(runner_params) - - respond_to do |format| - format.js - format.html { redirect_to ci_admin_runner_path(@runner) } - end - end - - def destroy - @runner.destroy - - redirect_to ci_admin_runners_path - end - - def resume - if @runner.update_attributes(active: true) - redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' - else - redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' - end - end - - def pause - if @runner.update_attributes(active: false) - redirect_to ci_admin_runners_path, notice: 'Runner was successfully updated.' - else - redirect_to ci_admin_runners_path, alert: 'Runner was not updated.' - end - end - - def assign_all - Ci::Project.unassigned(@runner).all.each do |project| - @runner.assign_to(project, current_user) - end - - redirect_to ci_admin_runner_path(@runner), notice: "Runner was assigned to all projects" - end - - private - - def runner - @runner ||= Ci::Runner.find(params[:id]) - end - - def runner_params - params.require(:runner).permit(:token, :description, :tag_list, :active) - end - end -end diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index 848f2b4e314..bc7f48b3c87 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -4,8 +4,6 @@ module Ci "app/helpers/ci" end - helper_method :gl_project - private def authenticate_token! @@ -15,13 +13,13 @@ module Ci end def authorize_access_project! - unless can?(current_user, :read_project, gl_project) + unless can?(current_user, :read_project, project) return page_404 end end def authorize_manage_builds! - unless can?(current_user, :manage_builds, gl_project) + unless can?(current_user, :manage_builds, project) return page_404 end end @@ -31,7 +29,7 @@ module Ci end def authorize_manage_project! - unless can?(current_user, :admin_project, gl_project) + unless can?(current_user, :admin_project, project) return page_404 end end @@ -58,9 +56,5 @@ module Ci count: count } end - - def gl_project - ::Project.find(@project.gitlab_id) - end end end diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb index a4f6aff49b4..7ed78ff8e98 100644 --- a/app/controllers/ci/lints_controller.rb +++ b/app/controllers/ci/lints_controller.rb @@ -1,5 +1,5 @@ module Ci - class LintsController < Ci::ApplicationController + class LintsController < ApplicationController before_action :authenticate_user! def show diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 8406399fb60..7e62320bf21 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -3,13 +3,12 @@ module Ci before_action :project, except: [:index] before_action :authenticate_user!, except: [:index, :build, :badge] before_action :authorize_access_project!, except: [:index, :badge] - before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml] before_action :no_cache, only: [:badge] protect_from_forgery def show # Temporary compatibility with CI badges pointing to CI project page - redirect_to namespace_project_path(project.gl_project.namespace, project.gl_project) + redirect_to namespace_project_path(project.namespace, project) end # Project status badge @@ -20,16 +19,11 @@ module Ci send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" end - def toggle_shared_runners - project.toggle!(:shared_runners_enabled) - - redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project) - end - protected def project - @project ||= Ci::Project.find(params[:id]) + # TODO: what to do here? + @project ||= Project.find_by_ci_id(params[:id]) end def no_cache diff --git a/app/controllers/ci/runner_projects_controller.rb b/app/controllers/ci/runner_projects_controller.rb deleted file mode 100644 index 9d555313369..00000000000 --- a/app/controllers/ci/runner_projects_controller.rb +++ /dev/null @@ -1,34 +0,0 @@ -module Ci - class RunnerProjectsController < Ci::ApplicationController - before_action :authenticate_user! - before_action :project - before_action :authorize_manage_project! - - def create - @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - - return head(403) unless current_user.ci_authorized_runners.include?(@runner) - - path = runners_path(@project.gl_project) - - if @runner.assign_to(project, current_user) - redirect_to path - else - redirect_to path, alert: 'Failed adding runner to project' - end - end - - def destroy - runner_project = project.runner_projects.find(params[:id]) - runner_project.destroy - - redirect_to runners_path(@project.gl_project) - end - - private - - def project - @project ||= Ci::Project.find(params[:project_id]) - end - end -end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 7d0d57858e0..dd32d509191 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -31,8 +31,4 @@ class Projects::ApplicationController < ApplicationController def builds_enabled return render_404 unless @project.builds_enabled? end - - def ci_project - @ci_project ||= @project.ensure_gitlab_ci_project - end end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 4638f77b887..e7e2ab43130 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -1,5 +1,4 @@ class Projects::BuildsController < Projects::ApplicationController - before_action :ci_project before_action :build, except: [:index, :cancel_all] before_action :authorize_manage_builds!, except: [:index, :show, :status] @@ -30,7 +29,7 @@ class Projects::BuildsController < Projects::ApplicationController end def show - @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC') + @builds = @project.ci_commits.find_by_sha(@build.sha).builds.order('id DESC') @builds = @builds.where("id not in (?)", @build.id) @commit = @build.commit @@ -77,7 +76,7 @@ class Projects::BuildsController < Projects::ApplicationController private def build - @build ||= ci_project.builds.unscoped.find_by!(id: params[:id]) + @build ||= project.ci_builds.unscoped.find_by!(id: params[:id]) end def artifacts_file @@ -85,7 +84,7 @@ class Projects::BuildsController < Projects::ApplicationController end def build_path(build) - namespace_project_build_path(build.gl_project.namespace, build.gl_project, build) + namespace_project_build_path(build.project.namespace, build.project, build) end def authorize_manage_builds! diff --git a/app/controllers/projects/ci_settings_controller.rb b/app/controllers/projects/ci_settings_controller.rb deleted file mode 100644 index a263242a850..00000000000 --- a/app/controllers/projects/ci_settings_controller.rb +++ /dev/null @@ -1,36 +0,0 @@ -class Projects::CiSettingsController < Projects::ApplicationController - before_action :ci_project - before_action :authorize_admin_project! - - layout "project_settings" - - def edit - end - - def update - if ci_project.update_attributes(project_params) - Ci::EventService.new.change_project_settings(current_user, ci_project) - - redirect_to edit_namespace_project_ci_settings_path(project.namespace, project), notice: 'Project was successfully updated.' - else - render action: "edit" - end - end - - def destroy - ci_project.destroy - Ci::EventService.new.remove_project(current_user, ci_project) - project.gitlab_ci_service.update_attributes(active: false) - - redirect_to project_path(project), notice: "CI was disabled for this project" - end - - protected - - def project_params - params.require(:project).permit(:path, :timeout, :timeout_in_minutes, :default_ref, :always_build, - :polling_interval, :public, :ssh_url_to_repo, :allow_git_fetch, :email_recipients, - :email_add_pusher, :email_only_broken_builds, :coverage_regex, :shared_runners_enabled, :token, - { variables_attributes: [:id, :key, :value, :_destroy] }) - end -end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index e8af205b788..0aaba3792bf 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -31,7 +31,6 @@ class Projects::CommitController < Projects::ApplicationController end def builds - @ci_project = @project.gitlab_ci_project end def cancel_builds diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index a8f47069bb4..d13ea9f34b6 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -25,13 +25,11 @@ class Projects::GraphsController < Projects::ApplicationController end def ci - ci_project = @project.gitlab_ci_project - @charts = {} - @charts[:week] = Ci::Charts::WeekChart.new(ci_project) - @charts[:month] = Ci::Charts::MonthChart.new(ci_project) - @charts[:year] = Ci::Charts::YearChart.new(ci_project) - @charts[:build_times] = Ci::Charts::BuildTime.new(ci_project) + @charts[:week] = Ci::Charts::WeekChart.new(project) + @charts[:month] = Ci::Charts::MonthChart.new(project) + @charts[:year] = Ci::Charts::YearChart.new(project) + @charts[:build_times] = Ci::Charts::BuildTime.new(project) end def languages diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb new file mode 100644 index 00000000000..69863387354 --- /dev/null +++ b/app/controllers/projects/runner_projects_controller.rb @@ -0,0 +1,26 @@ +class Projects::RunnerProjectsController < Projects::ApplicationController + before_action :authorize_admin_project! + + layout 'project_settings' + + def create + @runner = Ci::Runner.find(params[:runner_project][:runner_id]) + + return head(403) unless current_user.ci_authorized_runners.include?(@runner) + + path = runners_path(project) + + if @runner.assign_to(project, current_user) + redirect_to path + else + redirect_to path, alert: 'Failed adding runner to project' + end + end + + def destroy + runner_project = project.ci_runner_projects.find(params[:id]) + runner_project.destroy + + redirect_to runners_path(project) + end +end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index bfbcf2567f3..863c5d131ab 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -1,14 +1,13 @@ class Projects::RunnersController < Projects::ApplicationController - before_action :ci_project before_action :set_runner, only: [:edit, :update, :destroy, :pause, :resume, :show] before_action :authorize_admin_project! layout 'project_settings' def index - @runners = @ci_project.runners.ordered + @runners = project.ci_runners.ordered @specific_runners = current_user.ci_authorized_runners. - where.not(id: @ci_project.runners). + where.not(id: project.ci_runners). ordered.page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) @@ -26,7 +25,7 @@ class Projects::RunnersController < Projects::ApplicationController end def destroy - if @runner.only_for?(@ci_project) + if @runner.only_for?(project) @runner.destroy end @@ -52,10 +51,16 @@ class Projects::RunnersController < Projects::ApplicationController def show end + def toggle_shared_runners + project.toggle!(:shared_runners_enabled) + + redirect_to namespace_project_runners_path(project.namespace, project) + end + protected def set_runner - @runner ||= @ci_project.runners.find(params[:id]) + @runner ||= project.ci_runners.find(params[:id]) end def runner_params diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 782ebd01b05..421e648a2dd 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -1,22 +1,21 @@ class Projects::TriggersController < Projects::ApplicationController - before_action :ci_project before_action :authorize_admin_project! layout 'project_settings' def index - @triggers = @ci_project.triggers + @triggers = project.ci_triggers @trigger = Ci::Trigger.new end def create - @trigger = @ci_project.triggers.new + @trigger = project.ci_triggers.new @trigger.save if @trigger.valid? redirect_to namespace_project_triggers_path(@project.namespace, @project) else - @triggers = @ci_project.triggers.select(&:persisted?) + @triggers = project.ci_triggers.select(&:persisted?) render :index end end @@ -30,6 +29,6 @@ class Projects::TriggersController < Projects::ApplicationController private def trigger - @trigger ||= @ci_project.triggers.find(params[:id]) + @trigger ||= project.ci_triggers.find(params[:id]) end end diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index d6561a45a70..1dab978f462 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -1,5 +1,4 @@ class Projects::VariablesController < Projects::ApplicationController - before_action :ci_project before_action :authorize_admin_project! layout 'project_settings' @@ -8,9 +7,7 @@ class Projects::VariablesController < Projects::ApplicationController end def update - if ci_project.update_attributes(project_params) - Ci::EventService.new.change_project_settings(current_user, ci_project) - + if project.update_attributes(project_params) redirect_to namespace_project_variables_path(project.namespace, project), notice: 'Variables were successfully updated.' else render action: 'show' @@ -20,6 +17,6 @@ class Projects::VariablesController < Projects::ApplicationController private def project_params - params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] }) + params.require(:project).permit({ ci_variables_attributes: [:id, :key, :value, :_destroy] }) end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 10c75370d7b..e9917109f3e 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -210,10 +210,10 @@ class ProjectsController < ApplicationController def project_params params.require(:project).permit( - :name, :path, :description, :issues_tracker, :tag_list, + :name, :path, :description, :issues_tracker, :tag_list, :token, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, - :builds_enabled + :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, ) end diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb deleted file mode 100644 index e34c8be1dfc..00000000000 --- a/app/helpers/ci/gitlab_helper.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Ci - module GitlabHelper - def no_turbolink - { :"data-no-turbolink" => "data-no-turbolink" } - end - - def yaml_web_editor_link(project) - commits = project.commits - - if commits.any? && commits.last.ci_yaml_file - "#{project.gitlab_url}/edit/master/.gitlab-ci.yml" - else - "#{project.gitlab_url}/new/master" - end - end - end -end diff --git a/app/helpers/ci/projects_helper.rb b/app/helpers/ci/projects_helper.rb deleted file mode 100644 index fd991a4165a..00000000000 --- a/app/helpers/ci/projects_helper.rb +++ /dev/null @@ -1,36 +0,0 @@ -module Ci - module ProjectsHelper - def ref_tab_class ref = nil - 'active' if ref == @ref - end - - def success_ratio(success_builds, failed_builds) - failed_builds = failed_builds.count(:all) - success_builds = success_builds.count(:all) - - return 100 if failed_builds.zero? - - ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100 - ratio.to_i - end - - def markdown_badge_code(project, ref) - url = status_ci_project_url(project, ref: ref, format: 'png') - "[![build status](#{url})](#{ci_project_url(project, ref: ref)})" - end - - def html_badge_code(project, ref) - url = status_ci_project_url(project, ref: ref, format: 'png') - "" - end - - def project_uses_specific_runner?(project) - project.runners.any? - end - - def no_runners_for_project?(project) - project.runners.blank? && - Ci::Runner.shared.blank? - end - end -end diff --git a/app/helpers/ci_badge_helper.rb b/app/helpers/ci_badge_helper.rb new file mode 100644 index 00000000000..a81edbcb416 --- /dev/null +++ b/app/helpers/ci_badge_helper.rb @@ -0,0 +1,11 @@ +module CiBadgeHelper + def markdown_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + "[![build status](#{url})](#{ci_project_url(project, ref: ref)})" + end + + def html_badge_code(project, ref) + url = status_ci_project_url(project, ref: ref, format: 'png') + "" + end +end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 8e1f8f9ba6d..7feeaa17306 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -1,6 +1,6 @@ module CiStatusHelper def ci_status_path(ci_commit) - project = ci_commit.gl_project + project = ci_commit.project builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha) end @@ -63,4 +63,9 @@ module CiStatusHelper ci_status_icon(ci_commit) end end + + def no_runners_for_project?(project) + project.ci_runners.blank? && + Ci::Runner.shared.blank? + end end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index 1e372d5631d..c2ab80f2e0d 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -16,4 +16,14 @@ module GraphHelper ids = parents.map { |p| p.id } ids.zip(parent_spaces) end + + def success_ratio(success_builds, failed_builds) + failed_builds = failed_builds.count(:all) + success_builds = success_builds.count(:all) + + return 100 if failed_builds.zero? + + ratio = (success_builds.to_f / (success_builds + failed_builds)) * 100 + ratio.to_i + end end diff --git a/app/helpers/runners_helper.rb b/app/helpers/runners_helper.rb index 46eb82a354f..9fb42487a75 100644 --- a/app/helpers/runners_helper.rb +++ b/app/helpers/runners_helper.rb @@ -19,7 +19,7 @@ module RunnersHelper id = "\##{runner.id}" if current_user && current_user.admin - link_to ci_admin_runner_path(runner) do + link_to admin_runner_path(runner) do display_name + id end else diff --git a/app/helpers/yaml_helper.rb b/app/helpers/yaml_helper.rb new file mode 100644 index 00000000000..17990e1f475 --- /dev/null +++ b/app/helpers/yaml_helper.rb @@ -0,0 +1,11 @@ +module YamlHelper + def yaml_web_editor_link(project) + commits = project.ci_commits + + if commits.any? && commits.last.ci_yaml_file + "#{project.gitlab_url}/edit/master/.gitlab-ci.yml" + else + "#{project.gitlab_url}/new/master" + end + end +end diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb deleted file mode 100644 index 7f5df8ce6c4..00000000000 --- a/app/models/ci/application_setting.rb +++ /dev/null @@ -1,38 +0,0 @@ -# == Schema Information -# -# Table name: ci_application_settings -# -# id :integer not null, primary key -# all_broken_builds :boolean -# add_pusher :boolean -# created_at :datetime -# updated_at :datetime -# - -module Ci - class ApplicationSetting < ActiveRecord::Base - extend Ci::Model - CACHE_KEY = 'ci_application_setting.last' - - after_commit do - Rails.cache.write(CACHE_KEY, self) - end - - def self.expire - Rails.cache.delete(CACHE_KEY) - end - - def self.current - Rails.cache.fetch(CACHE_KEY) do - Ci::ApplicationSetting.last - end - end - - def self.create_from_defaults - create( - all_broken_builds: Settings.gitlab_ci['all_broken_builds'], - add_pusher: Settings.gitlab_ci['add_pusher'], - ) - end - end -end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 564041e3214..43ed8eb518b 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -84,6 +84,7 @@ module Ci new_build.options = build.options new_build.commands = build.commands new_build.tag_list = build.tag_list + new_build.gl_project_id = build.gl_project_id new_build.commit_id = build.commit_id new_build.name = build.name new_build.allow_failure = build.allow_failure @@ -101,14 +102,9 @@ module Ci end after_transition any => [:success, :failed, :canceled] do |build, transition| - return unless build.gl_project - - project = build.project - - if project.coverage_enabled? - build.update_coverage - end + return unless build.project + build.update_coverage build.commit.create_next_builds(build) build.execute_hooks end @@ -119,7 +115,7 @@ module Ci end def retryable? - commands.present? + project.builds_enabled? && commands.present? end def retried? @@ -132,7 +128,7 @@ module Ci end def timeout - project.timeout + project.build_timeout end def variables @@ -151,26 +147,21 @@ module Ci project.name end - def project_recipients - recipients = project.email_recipients.split(' ') - - if project.email_add_pusher? && user.present? && user.notification_email.present? - recipients << user.notification_email - end - - recipients.uniq - end - def repo_url - project.repo_url_with_auth + auth = "gitlab-ci-token:#{token}@" + project.http_url_to_repo.sub(/^https?:\/\//) do |prefix| + prefix + auth + end end def allow_git_fetch - project.allow_git_fetch + project.build_allow_git_fetch end def update_coverage - coverage = extract_coverage(trace, project.coverage_regex) + coverage_regex = project.build_coverage_regex + return unless coverage_regex + coverage = extract_coverage(trace, coverage_regex) if coverage.is_a? Numeric update_attributes(coverage: coverage) @@ -239,20 +230,20 @@ module Ci def target_url Gitlab::Application.routes.url_helpers. - namespace_project_build_url(gl_project.namespace, gl_project, self) + namespace_project_build_url(project.namespace, project, self) end def cancel_url if active? Gitlab::Application.routes.url_helpers. - cancel_namespace_project_build_path(gl_project.namespace, gl_project, self) + cancel_namespace_project_build_path(project.namespace, project, self) end end def retry_url if retryable? Gitlab::Application.routes.url_helpers. - retry_namespace_project_build_path(gl_project.namespace, gl_project, self) + retry_namespace_project_build_path(project.namespace, project, self) end end @@ -271,16 +262,18 @@ module Ci def download_url if artifacts_file.exists? Gitlab::Application.routes.url_helpers. - download_namespace_project_build_path(gl_project.namespace, gl_project, self) + download_namespace_project_build_path(project.namespace, project, self) end end def execute_hooks build_data = Gitlab::BuildDataBuilder.build(self) - gl_project.execute_hooks(build_data.dup, :build_hooks) - gl_project.execute_services(build_data.dup, :build_hooks) + project.execute_hooks(build_data.dup, :build_hooks) + project.execute_services(build_data.dup, :build_hooks) end + + private def yaml_variables @@ -294,7 +287,7 @@ module Ci end def project_variables - project.variables.map do |variable| + project.ci_variables.map do |variable| { key: variable.key, value: variable.value, public: false } end end diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index e63f7790946..79193344545 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -20,8 +20,8 @@ module Ci class Commit < ActiveRecord::Base extend Ci::Model - belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id - has_many :statuses, dependent: :destroy, class_name: 'CommitStatus' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id + has_many :statuses, class_name: 'CommitStatus' has_many :builds, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' @@ -38,10 +38,6 @@ module Ci sha end - def project - @project ||= gl_project.ensure_gitlab_ci_project - end - def project_id project.id end @@ -79,7 +75,7 @@ module Ci end def commit_data - @commit ||= gl_project.commit(sha) + @commit ||= project.commit(sha) rescue nil end @@ -187,11 +183,9 @@ module Ci end def coverage - if project.coverage_enabled? - coverage_array = latest_builds.map(&:coverage).compact - if coverage_array.size >= 1 - '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) - end + coverage_array = latest_builds.map(&:coverage).compact + if coverage_array.size >= 1 + '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) end end @@ -201,7 +195,7 @@ module Ci def config_processor return nil unless ci_yaml_file - @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace) rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e save_yaml_error(e.message) nil @@ -211,7 +205,7 @@ module Ci end def ci_yaml_file - @ci_yaml_file ||= gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data + @ci_yaml_file ||= project.repository.blob_at(sha, '.gitlab-ci.yml').data rescue nil end diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb deleted file mode 100644 index 8c39be42677..00000000000 --- a/app/models/ci/event.rb +++ /dev/null @@ -1,27 +0,0 @@ -# == Schema Information -# -# Table name: ci_events -# -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# is_admin :integer -# description :text -# created_at :datetime -# updated_at :datetime -# - -module Ci - class Event < ActiveRecord::Base - extend Ci::Model - - belongs_to :project, class_name: 'Ci::Project' - - validates :description, - presence: true, - length: { in: 5..200 } - - scope :admin, ->(){ where(is_admin: true) } - scope :project_wide, ->(){ where(is_admin: false) } - end -end diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb deleted file mode 100644 index 79ff7e1dcd4..00000000000 --- a/app/models/ci/project.rb +++ /dev/null @@ -1,151 +0,0 @@ -# == Schema Information -# -# Table name: ci_projects -# -# id :integer not null, primary key -# name :string(255) -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -module Ci - class Project < ActiveRecord::Base - extend Ci::Model - - include Ci::ProjectStatus - - belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id - - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' - has_many :runners, through: :runner_projects, class_name: 'Ci::Runner' - has_many :events, dependent: :destroy, class_name: 'Ci::Event' - has_many :variables, dependent: :destroy, class_name: 'Ci::Variable' - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - - accepts_nested_attributes_for :variables, allow_destroy: true - - delegate :name_with_namespace, :path_with_namespace, :web_url, :http_url_to_repo, :ssh_url_to_repo, to: :gl_project - - # - # Validations - # - validates_presence_of :timeout, :token, :default_ref, :gitlab_id - - validates_uniqueness_of :gitlab_id - - validates :polling_interval, - presence: true, - if: ->(project) { project.always_build.present? } - - before_validation :set_default_values - - class << self - include Ci::CurrentSettings - - def unassigned(runner) - joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \ - "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}"). - where("#{Ci::RunnerProject.table_name}.project_id" => nil) - end - - def ordered_by_last_commit_date - last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)" - joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id"). - joins(:gl_project). - order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC") - end - end - - def name - name_with_namespace - end - - def path - path_with_namespace - end - - def gitlab_url - web_url - end - - def any_runners?(&block) - if runners.active.any?(&block) - return true - end - - shared_runners_enabled && Ci::Runner.shared.active.any?(&block) - end - - def set_default_values - self.token = SecureRandom.hex(15) if self.token.blank? - self.default_ref ||= 'master' - end - - def tracked_refs - @tracked_refs ||= default_ref.split(",").map { |ref| ref.strip } - end - - def valid_token? token - self.token && self.token == token - end - - def no_running_builds? - # Get running builds not later than 3 days ago to ignore hangs - builds.running.where("updated_at > ?", 3.days.ago).empty? - end - - def email_notification? - email_add_pusher || email_recipients.present? - end - - def timeout_in_minutes - timeout / 60 - end - - def timeout_in_minutes=(value) - self.timeout = value.to_i * 60 - end - - def coverage_enabled? - coverage_regex.present? - end - - # Build a clone-able repo url - # using http and basic auth - def repo_url_with_auth - auth = "gitlab-ci-token:#{token}@" - http_url_to_repo.sub(/^https?:\/\//) do |prefix| - prefix + auth - end - end - - def setup_finished? - commits.any? - end - - def commits - gl_project.ci_commits.ordered - end - - def builds - gl_project.ci_builds - end - end -end diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb deleted file mode 100644 index 2d35aeac225..00000000000 --- a/app/models/ci/project_status.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - module ProjectStatus - def status - last_commit.status if last_commit - end - - def broken? - last_commit.failed? if last_commit - end - - def success? - last_commit.success? if last_commit - end - - def broken_or_success? - broken? || success? - end - - def last_commit - @last_commit ||= commits.last if commits.any? - end - - def last_commit_date - last_commit.try(:created_at) - end - - def human_status - status - end - end -end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 89710485811..aa445db7ebf 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -25,7 +25,7 @@ module Ci has_many :builds, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' - has_many :projects, through: :runner_projects, class_name: 'Ci::Project' + has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' @@ -45,10 +45,6 @@ module Ci query: "%#{query.try(:downcase)}%") end - def gl_projects_ids - projects.select(:gitlab_id) - end - def set_default_values self.token = SecureRandom.hex(15) if self.token.blank? end @@ -56,7 +52,7 @@ module Ci def assign_to(project, current_user = nil) self.is_shared = false if shared? self.save - project.runner_projects.create!(runner_id: self.id) + project.ci_runner_projects.create!(runner_id: self.id) end def display_name diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 3f4fc43873e..93d9be144e8 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -14,8 +14,8 @@ module Ci extend Ci::Model belongs_to :runner, class_name: 'Ci::Runner' - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id - validates_uniqueness_of :runner_id, scope: :project_id + validates_uniqueness_of :runner_id, scope: :gl_project_id end end diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index b73c35d5ae5..23516709a41 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -16,7 +16,7 @@ module Ci acts_as_paranoid - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' validates_presence_of :token diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index b3d2b809e03..56759d3e50f 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -15,10 +15,10 @@ module Ci class Variable < ActiveRecord::Base extend Ci::Model - belongs_to :project, class_name: 'Ci::Project' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id validates_presence_of :key - validates_uniqueness_of :key, scope: :project_id + validates_uniqueness_of :key, scope: :gl_project_id attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index ff619965a57..579b638706d 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -30,6 +30,7 @@ class CommitStatus < ActiveRecord::Base self.table_name = 'ci_builds' + belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :commit, class_name: 'Ci::Commit' belongs_to :user @@ -49,6 +50,7 @@ class CommitStatus < ActiveRecord::Base 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 :has_coverage?, -> { where.not(coverage: nil).any? } state_machine :status, initial: :pending do event :run do @@ -86,7 +88,7 @@ class CommitStatus < ActiveRecord::Base state :canceled, value: 'canceled' end - delegate :sha, :short_sha, :gl_project, + delegate :sha, :short_sha, :project, to: :commit, prefix: false # TODO: this should be removed with all references diff --git a/app/models/project.rb b/app/models/project.rb index 60ca2cad6ac..e3eee36c253 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -56,6 +56,7 @@ class Project < ActiveRecord::Base default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wall_enabled, false default_value_for :snippets_enabled, gitlab_config_features.snippets + default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } # set last_activity_at to the same as created_at after_create :set_last_activity_at @@ -77,7 +78,6 @@ class Project < ActiveRecord::Base # Project services has_many :services - has_one :gitlab_ci_service, dependent: :destroy has_one :campfire_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy @@ -122,14 +122,21 @@ class Project < ActiveRecord::Base has_many :deploy_keys, through: :deploy_keys_projects has_many :users_star_projects, dependent: :destroy has_many :starrers, through: :users_star_projects, source: :user - has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id - has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build' has_many :releases, dependent: :destroy has_many :lfs_objects_projects, dependent: :destroy has_many :lfs_objects, through: :lfs_objects_projects has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" - has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id + + has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id + has_many :ci_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id + has_many :ci_builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the ci_statuses + has_many :ci_runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id + has_many :ci_runners, through: :ci_runner_projects, source: :runner, class_name: 'Ci::Runner' + has_many :ci_variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id + has_many :ci_triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id + + accepts_nested_attributes_for :ci_variables, allow_destroy: true delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true @@ -162,6 +169,11 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } + before_validation :set_random_token + def set_random_token + self.token = SecureRandom.hex(15) if self.token.blank? + end + mount_uploader :avatar, AvatarUploader # Scopes @@ -257,6 +269,12 @@ class Project < ActiveRecord::Base projects.iwhere('projects.path' => project_path).take end + def find_by_ci_id(id) + ci_projects = Arel::Table.new(:ci_projects) + gitlab_id = ci_projects.where(ci_projects[:id].eq(id)).project(ci_projects[:gitlab_id]) + find_by("id=(#{gitlab_id.to_sql})") + end + def visibility_levels Gitlab::VisibilityLevel.options end @@ -791,28 +809,6 @@ class Project < ActiveRecord::Base ci_commit(sha) || ci_commits.create(sha: sha) end - def ensure_gitlab_ci_project - gitlab_ci_project || create_gitlab_ci_project( - shared_runners_enabled: current_application_settings.shared_runners_enabled - ) - end - - # TODO: this should be migrated to Project table, - # the same as issues_enabled - def builds_enabled - gitlab_ci_service && gitlab_ci_service.active - end - - def builds_enabled? - builds_enabled - end - - def builds_enabled=(value) - service = gitlab_ci_service || create_gitlab_ci_service - service.active = value - service.save - end - def enable_ci self.builds_enabled = true end @@ -826,4 +822,28 @@ class Project < ActiveRecord::Base forked_project_link.destroy end end + + def any_runners?(&block) + if ci_runners.active.any?(&block) + return true + end + + shared_runners_enabled? && Ci::Runner.shared.active.any?(&block) + end + + def valid_token? token + self.token && self.token == token + end + + def build_coverage_enabled? + build_coverage_regex.present? + end + + def build_timeout_in_minutes + build_timeout / 60 + end + + def build_timeout_in_minutes=(value) + self.build_timeout = value.to_i * 60 + end end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 234e8e8b580..d73182d40ac 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -19,76 +19,5 @@ # class GitlabCiService < CiService - include Gitlab::Application.routes.url_helpers - - after_save :compose_service_hook, if: :activated? - after_save :ensure_gitlab_ci_project, if: :activated? - - def compose_service_hook - hook = service_hook || build_service_hook - hook.save - end - - def ensure_gitlab_ci_project - return unless project - project.ensure_gitlab_ci_project - end - - def supported_events - %w(push tag_push) - end - - def execute(data) - return unless supported_events.include?(data[:object_kind]) - - ci_project = project.gitlab_ci_project - if ci_project - current_user = User.find_by(id: data[:user_id]) - Ci::CreateCommitService.new.execute(ci_project, current_user, data) - end - end - - def token - if project.gitlab_ci_project.present? - project.gitlab_ci_project.token - end - end - - def get_ci_commit(sha, ref) - Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha) - end - - def commit_status(sha, ref) - get_ci_commit(sha, ref).status - rescue ActiveRecord::RecordNotFound - :error - end - - def commit_coverage(sha, ref) - get_ci_commit(sha, ref).coverage - rescue ActiveRecord::RecordNotFound - :error - end - - def build_page(sha, ref) - if project.gitlab_ci_project.present? - builds_namespace_project_commit_url(project.namespace, project, sha) - end - end - - def title - 'GitLab CI' - end - - def description - 'Continuous integration server from GitLab' - end - - def to_param - 'gitlab_ci' - end - - def fields - [] - end + # this is no longer used end diff --git a/app/models/service.rb b/app/models/service.rb index 4159e367d8c..d3bf7f0ebd1 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -41,7 +41,7 @@ class Service < ActiveRecord::Base validates :project_id, presence: true, unless: Proc.new { |service| service.template? } - scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } + scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :push_hooks, -> { where(push_events: true, active: true) } scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) } @@ -188,7 +188,6 @@ class Service < ActiveRecord::Base external_wiki flowdock gemnasium - gitlab_ci hipchat irker jira diff --git a/app/models/user.rb b/app/models/user.rb index 7155dd2bea7..da06b6f3ade 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -769,10 +769,9 @@ class User < ActiveRecord::Base def ci_authorized_runners @ci_authorized_runners ||= begin - runner_ids = Ci::RunnerProject.joins(:project). - where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})"). + runner_ids = Ci::RunnerProject. + where("ci_runner_projects.gl_project_id IN (#{ci_projects_union.to_sql})"). select(:runner_id) - Ci::Runner.specific.where(id: runner_ids) end end diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb index 847db2d48a7..ba7c4632f49 100644 --- a/app/services/ci/create_builds_service.rb +++ b/app/services/ci/create_builds_service.rb @@ -29,7 +29,8 @@ module Ci build_attrs.merge!(ref: ref, tag: tag, trigger_request: trigger_request, - user: user) + user: user, + gl_project_id: commit.gl_project_id) build = commit.builds.create!(build_attrs) build.execute_hooks diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb index 479a2d6defc..6401ce3619e 100644 --- a/app/services/ci/create_commit_service.rb +++ b/app/services/ci/create_commit_service.rb @@ -16,7 +16,7 @@ module Ci end tag = origin_ref.start_with?('refs/tags/') - commit = project.gl_project.ensure_ci_commit(sha) + commit = project.ensure_ci_commit(sha) unless commit.skip_ci? commit.update_committed! commit.create_builds(ref, tag, user) diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb index 4b86cb0a1f5..b3dfc707221 100644 --- a/app/services/ci/create_trigger_request_service.rb +++ b/app/services/ci/create_trigger_request_service.rb @@ -1,13 +1,13 @@ module Ci class CreateTriggerRequestService def execute(project, trigger, ref, variables = nil) - commit = project.gl_project.commit(ref) + commit = project.commit(ref) return unless commit # check if ref is tag - tag = project.gl_project.repository.find_tag(ref).present? + tag = project.repository.find_tag(ref).present? - ci_commit = project.gl_project.ensure_ci_commit(commit.sha) + ci_commit = project.ensure_ci_commit(commit.sha) trigger_request = trigger.trigger_requests.create!( variables: variables, diff --git a/app/services/ci/event_service.rb b/app/services/ci/event_service.rb deleted file mode 100644 index 3f4e02dd26c..00000000000 --- a/app/services/ci/event_service.rb +++ /dev/null @@ -1,31 +0,0 @@ -module Ci - class EventService - def remove_project(user, project) - create( - description: "Project \"#{project.name}\" has been removed by #{user.username}", - user_id: user.id, - is_admin: true - ) - end - - def create_project(user, project) - create( - description: "Project \"#{project.name}\" has been created by #{user.username}", - user_id: user.id, - is_admin: true - ) - end - - def change_project_settings(user, project) - create( - project_id: project.id, - user_id: user.id, - description: "User \"#{user.username}\" updated projects settings" - ) - end - - def create(*args) - Ci::Event.create!(*args) - end - end -end diff --git a/app/services/ci/image_for_build_service.rb b/app/services/ci/image_for_build_service.rb index b8d24193035..f469b13e902 100644 --- a/app/services/ci/image_for_build_service.rb +++ b/app/services/ci/image_for_build_service.rb @@ -4,10 +4,10 @@ module Ci sha = params[:sha] sha ||= if params[:ref] - project.gl_project.commit(params[:ref]).try(:sha) + project.commit(params[:ref]).try(:sha) end - commit = project.commits.ordered.find_by(sha: sha) + commit = project.ci_commits.ordered.find_by(sha: sha) image_name = image_for_commit(commit) image_path = Rails.root.join('public/ci', image_name) diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index 7beb098659c..eba602da992 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -8,10 +8,10 @@ module Ci builds = if current_runner.shared? # don't run projects which have not enables shared runners - builds.joins(commit: { gl_project: :gitlab_ci_project }).where(ci_projects: { shared_runners_enabled: true }) + builds.joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }) else # do run projects which are only assigned to this runner - builds.joins(:commit).where(ci_commits: { gl_project_id: current_runner.gl_projects_ids }) + builds.where(project: current_runner.projects) end builds = builds.order('created_at ASC') @@ -20,10 +20,9 @@ module Ci build.can_be_served?(current_runner) end - if build # In case when 2 runners try to assign the same build, second runner will be declined - # with StateMachine::InvalidTransition in run! method. + # with StateMachines::InvalidTransition in run! method. build.with_lock do build.runner_id = current_runner.id build.save! @@ -33,7 +32,7 @@ module Ci build - rescue StateMachine::InvalidTransition + rescue StateMachines::InvalidTransition nil end end diff --git a/app/services/ci/test_hook_service.rb b/app/services/ci/test_hook_service.rb deleted file mode 100644 index 3a17596aaeb..00000000000 --- a/app/services/ci/test_hook_service.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Ci - class TestHookService - def execute(hook, current_user) - Ci::WebHookService.new.build_end(hook.project.commits.last.last_build) - end - end -end diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 5da1c7afd92..0577ae778d5 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -7,6 +7,8 @@ module Projects description: @project.description, name: @project.name, path: @project.path, + shared_runners_enabled: @project.shared_runners_enabled, + builds_enabled: @project.builds_enabled, namespace_id: @params[:namespace].try(:id) || current_user.namespace.id } @@ -15,19 +17,6 @@ module Projects end new_project = CreateService.new(current_user, new_params).execute - - if new_project.persisted? - if @project.builds_enabled? - new_project.enable_ci - - settings = @project.gitlab_ci_project.attributes.select do |attr_name, value| - ["public", "shared_runners_enabled", "allow_git_fetch"].include? attr_name - end - - new_project.gitlab_ci_project.update(settings) - end - end - new_project end end diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml new file mode 100644 index 00000000000..6936e614346 --- /dev/null +++ b/app/views/admin/builds/_build.html.haml @@ -0,0 +1,73 @@ +- project = build.project +%tr.build + %td.status + = ci_status_with_icon(build.status) + + %td.build-link + - if build.target_url + = link_to build.target_url do + %strong Build ##{build.id} + - else + %strong Build ##{build.id} + + - if build.show_warning? + %i.fa.fa-warning.text-warning + + %td + - if project + = link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project), class: "monospace" + + %td + = link_to build.short_sha, namespace_project_commit_path(project.namespace, project, build.sha), class: "monospace" + + %td + - if build.ref + = link_to build.ref, namespace_project_commits_path(project.namespace, project, build.ref) + - else + .light none + + %td + - if build.try(:runner) + = runner_link(build.runner) + - else + .light none + + %td + #{build.stage} / #{build.name} + + .pull-right + - if build.tags.any? + - build.tags.each do |tag| + %span.label.label-primary + = tag + - if build.try(:trigger_request) + %span.label.label-info triggered + - if build.try(: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_with_tooltip(build.finished_at)} + + - if defined?(coverage) && coverage + %td.coverage + - if build.try(:coverage) + #{build.coverage}% + + %td + .pull-right + - if current_user && can?(current_user, :download_build_artifacts, project) && build.download_url + = link_to build.download_url, title: 'Download artifacts' do + %i.fa.fa-download + - if current_user && can?(current_user, :manage_builds, build.project) + - if build.active? + - if build.cancel_url + = link_to build.cancel_url, method: :post, title: 'Cancel' do + %i.fa.fa-remove.cred + - elsif defined?(allow_retry) && allow_retry && build.retry_url + = link_to build.retry_url, method: :post, title: 'Retry' do + %i.fa.fa-repeat diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml new file mode 100644 index 00000000000..55da06a7fe9 --- /dev/null +++ b/app/views/admin/builds/index.html.haml @@ -0,0 +1,50 @@ +.project-issuable-filter + .controls + .pull-left.hidden-xs + - if @all_builds.running_or_pending.any? + = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post + + %ul.center-top-menu + %li{class: ('active' if @scope.nil?)} + = link_to admin_builds_path do + Running + %span.badge.js-running-count= @all_builds.running_or_pending.count(:id) + + %li{class: ('active' if @scope == 'finished')} + = link_to admin_builds_path(scope: :finished) do + Finished + %span.badge.js-running-count= @all_builds.finished.count(:id) + + %li{class: ('active' if @scope == 'all')} + = link_to admin_builds_path(scope: :all) do + All + %span.badge.js-totalbuilds-count= @all_builds.count(:id) + +.gray-content-block + #{(@scope || 'running').capitalize} builds + +%ul.content-list + - if @builds.blank? + %li + .nothing-here-block No builds to show + - else + .table-holder + %table.table.builds + %thead + %tr + %th Status + %th Build ID + %th Project + %th Commit + %th Ref + %th Runner + %th Name + %th Duration + %th Finished at + %th + + - @builds.each do |build| + = render "admin/builds/build", build: build + + = paginate @builds, theme: 'gitlab' + diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml new file mode 100644 index 00000000000..6745e58deca --- /dev/null +++ b/app/views/admin/runners/_runner.html.haml @@ -0,0 +1,48 @@ +%tr{id: dom_id(runner)} + %td + - if runner.shared? + %span.label.label-success shared + - else + %span.label.label-info specific + - unless runner.active? + %span.label.label-danger paused + + %td + = link_to admin_runner_path(runner) do + = runner.short_sha + %td + .runner-description + = runner.description + %span (#{link_to 'edit', '#', class: 'edit-runner-link'}) + .runner-description-form.hide + = form_for [:admin, runner], remote: true, html: { class: 'form-inline' } do |f| + .form-group + = f.text_field :description, class: 'form-control' + = f.submit 'Save', class: 'btn' + %span (#{link_to 'cancel', '#', class: 'cancel'}) + %td + - if runner.shared? + \- + - else + = runner.projects.count(:all) + %td + #{runner.builds.count(:all)} + %td + - runner.tag_list.each do |tag| + %span.label.label-primary + = tag + %td + - if runner.contacted_at + #{time_ago_in_words(runner.contacted_at)} ago + - else + Never + %td + .pull-right + = link_to 'Edit', admin_runner_path(runner), class: 'btn btn-sm' +   + - if runner.active? + = link_to 'Pause', [:pause, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' + - else + = link_to 'Resume', [:resume, :admin, runner], method: :get, class: 'btn btn-success btn-sm' + = link_to 'Remove', [:admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml new file mode 100644 index 00000000000..26b180b4052 --- /dev/null +++ b/app/views/admin/runners/index.html.haml @@ -0,0 +1,53 @@ +%p.lead + %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. + %code #{GitlabCi::REGISTRATION_TOKEN} + +.bs-callout + %p + A 'runner' is a process which runs a build. + You can setup as many runners as you need. + %br + Runners can be placed on separate users, servers, and even on your local machine. + %br + + %div + %span Each runner can be in one of the following states: + %ul + %li + %span.label.label-success shared + \- run builds from all unassigned projects + %li + %span.label.label-info specific + \- run builds from assigned projects + %li + %span.label.label-danger paused + \- runner will not receive any new builds + +.append-bottom-20.clearfix + .pull-left + = form_tag 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', spellcheck: false + = submit_tag 'Search', class: 'btn' + + .pull-right.light + Runners with last contact less than a minute ago: #{@active_runners_cnt} + +%br + +.table-holder + %table.table + %thead + %tr + %th Type + %th Runner token + %th Description + %th Projects + %th Builds + %th Tags + %th Last contact + %th + + - @runners.each do |runner| + = render "admin/runners/runner", runner: runner += paginate @runners diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml new file mode 100644 index 00000000000..aeb484a7971 --- /dev/null +++ b/app/views/admin/runners/show.html.haml @@ -0,0 +1,125 @@ += content_for :title do + %h3.project-title + Runner ##{@runner.id} + .pull-right + - if @runner.shared? + %span.runner-state.runner-state-shared + Shared + - else + %span.runner-state.runner-state-specific + Specific + + + +- if @runner.shared? + .bs-callout.bs-callout-success + %h4 This runner will process builds from ALL UNASSIGNED projects + %p + If you want runners to build only specific projects, enable them in the table below. + Keep in mind that this is a one way transition. +- else + .bs-callout.bs-callout-info + %h4 This runner will process builds only from ASSIGNED projects + %p You can't make this a shared runner. +%hr += form_for @runner, url: admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| + .form-group + = label_tag :token, class: 'control-label' do + Token + .col-sm-10 + = f.text_field :token, class: 'form-control', readonly: true + .form-group + = label_tag :description, class: 'control-label' do + Description + .col-sm-10 + = f.text_field :description, class: 'form-control' + .form-group + = label_tag :tag_list, class: 'control-label' do + Tags + .col-sm-10 + = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' + .help-block You can setup builds to only use runners with specific tags + .form-actions + = f.submit 'Save', class: 'btn btn-save' + +.row + .col-md-6 + %h4 Restrict projects for this runner + - if @runner.projects.any? + %table.table + %thead + %tr + %th Assigned projects + %th + - @runner.runner_projects.each do |runner_project| + - project = runner_project.project + - if project + %tr.alert-info + %td + %strong + = project.name_with_namespace + %td + .pull-right + = link_to 'Disable', [:admin, project.namespace, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + + %table.table + %thead + %tr + %th Project + %th + + %tr + %td + = form_tag 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', spellcheck: false + = submit_tag 'Search', class: 'btn' + + %td + - @projects.each do |project| + %tr + %td + = project.name_with_namespace + %td + .pull-right + = form_for [:admin, project.namespace, project, project.ci_runner_projects.new] do |f| + = f.hidden_field :runner_id, value: @runner.id + = f.submit 'Enable', class: 'btn btn-xs' + = paginate @projects + + .col-md-6 + %h4 Recent builds served by this runner + %table.table.builds.runner-builds + %thead + %tr + %th Build + %th Status + %th Project + %th Commit + %th Finished at + + - @builds.each do |build| + - project = build.project + %tr.build + %td.id + - if project + = link_to namespace_project_build_path(project.namespace, project, build) do + %strong ##{build.id} + - else + %strong ##{build.id} + + %td.status + = ci_status_with_icon(build.status) + + %td.status + - if project + = project.name_with_namespace + + %td.build-link + - if project + = link_to ci_status_path(build.commit) do + %strong #{build.commit.short_sha} + + %td.timestamp + - if build.finished_at + %span #{time_ago_in_words build.finished_at} ago diff --git a/app/views/admin/runners/update.js.haml b/app/views/admin/runners/update.js.haml new file mode 100644 index 00000000000..2b7d3067e20 --- /dev/null +++ b/app/views/admin/runners/update.js.haml @@ -0,0 +1,2 @@ +:plain + $("#runner_#{@runner.id}").replaceWith("#{escape_javascript(render(@runner))}") diff --git a/app/views/ci/admin/application_settings/_form.html.haml b/app/views/ci/admin/application_settings/_form.html.haml deleted file mode 100644 index 634c9daa477..00000000000 --- a/app/views/ci/admin/application_settings/_form.html.haml +++ /dev/null @@ -1,24 +0,0 @@ -= form_for @application_setting, url: ci_admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f| - - if @application_setting.errors.any? - #error_explanation - .alert.alert-danger - - @application_setting.errors.full_messages.each do |msg| - %p= msg - - %fieldset - %legend Default Project Settings - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :all_broken_builds do - = f.check_box :all_broken_builds - Send emails only on broken builds - .form-group - .col-sm-offset-2.col-sm-10 - .checkbox - = f.label :add_pusher do - = f.check_box :add_pusher - Add pusher to recipients list - - .form-actions - = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/ci/admin/application_settings/show.html.haml b/app/views/ci/admin/application_settings/show.html.haml deleted file mode 100644 index 7ef0aa89ed6..00000000000 --- a/app/views/ci/admin/application_settings/show.html.haml +++ /dev/null @@ -1,3 +0,0 @@ -%h3.page-title Settings -%hr -= render 'form' diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml deleted file mode 100644 index 2df58713214..00000000000 --- a/app/views/ci/admin/builds/_build.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -- gl_project = build.project.gl_project -- if build.commit && build.project - %tr.build - %td.build-link - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do - %strong #{build.id} - - %td.status - = ci_status_with_icon(build.status) - - %td.commit-link - = link_to ci_status_path(build.commit) do - %strong #{build.commit.short_sha} - - %td.runner - - if build.runner - = link_to build.runner.id, ci_admin_runner_path(build.runner) - - %td.build-project - = truncate build.project.name, length: 30 - - %td.build-message - %span= truncate(build.commit.git_commit_message, length: 30) - - %td.build-branch - %span= truncate(build.ref, length: 25) - - %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 diff --git a/app/views/ci/admin/builds/index.html.haml b/app/views/ci/admin/builds/index.html.haml deleted file mode 100644 index d23119162cc..00000000000 --- a/app/views/ci/admin/builds/index.html.haml +++ /dev/null @@ -1,28 +0,0 @@ -%ul.nav.nav-tabs.append-bottom-20 - %li{class: ("active" if @scope.nil?)} - = link_to 'All builds', ci_admin_builds_path - - %li{class: ("active" if @scope == "pending")} - = link_to "Pending", ci_admin_builds_path(scope: :pending) - - %li{class: ("active" if @scope == "running")} - = link_to "Running", ci_admin_builds_path(scope: :running) - - -%table.builds - %thead - %tr - %th Build - %th Status - %th Commit - %th Runner - %th Project - %th Message - %th Branch - %th Duration - %th Finished at - - - @builds.each do |build| - = render "ci/admin/builds/build", build: build - -= paginate @builds diff --git a/app/views/ci/admin/events/index.html.haml b/app/views/ci/admin/events/index.html.haml deleted file mode 100644 index 5a5b4dc7c35..00000000000 --- a/app/views/ci/admin/events/index.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -.table-holder - %table.table - %thead - %tr - %th User ID - %th Description - %th When - - @events.each do |event| - %tr - %td - = event.user_id - %td - = event.description - %td.light - = time_ago_in_words event.updated_at - ago - -= paginate @events diff --git a/app/views/ci/admin/projects/_project.html.haml b/app/views/ci/admin/projects/_project.html.haml deleted file mode 100644 index a342d6e1cf0..00000000000 --- a/app/views/ci/admin/projects/_project.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -- last_commit = project.commits.last -%tr - %td - = project.id - %td - = link_to [:ci, project] do - %strong= project.name - %td - - if last_commit - = ci_status_with_icon(last_commit.status) - - if project.last_commit_date - · - = time_ago_in_words project.last_commit_date - ago - - else - No builds yet - %td - - if project.public - %i.fa.fa-globe - Public - - else - %i.fa.fa-lock - Private - %td - = project.commits.count - %td - = link_to [:ci, :admin, project], method: :delete, class: 'btn btn-danger btn-sm' do - %i.fa.fa-remove - Remove diff --git a/app/views/ci/admin/projects/index.html.haml b/app/views/ci/admin/projects/index.html.haml deleted file mode 100644 index 0da8547924b..00000000000 --- a/app/views/ci/admin/projects/index.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -.table-holder - %table.table - %thead - %tr - %th ID - %th Name - %th Last build - %th Access - %th Builds - %th - - - @projects.each do |project| - = render "ci/admin/projects/project", project: project - -= paginate @projects - diff --git a/app/views/ci/admin/runner_projects/index.html.haml b/app/views/ci/admin/runner_projects/index.html.haml deleted file mode 100644 index 6b4e3b2cb38..00000000000 --- a/app/views/ci/admin/runner_projects/index.html.haml +++ /dev/null @@ -1,57 +0,0 @@ -%p.lead - To register a new runner visit #{link_to 'this page ', ci_runners_path} - -.row - .col-md-8 - %h5 Activated: - %table.table - %tr - %th Runner ID - %th Runner Description - %th Last build - %th Builds Stats - %th Registered - %th - - - @runner_projects.each do |runner_project| - - runner = runner_project.runner - - builds = runner.builds.where(project_id: @project.id) - %tr - %td - %span.badge.badge-info= runner.id - %td - = runner.display_name - %td - - last_build = builds.last - - if last_build - = link_to last_build.short_sha, [last_build.project, last_build] - - else - unknown - %td - %span.badge.badge-success - #{builds.success.count} - %span / - %span.badge.badge-important - #{builds.failed.count} - %td - #{time_ago_in_words(runner_project.created_at)} ago - %td - = link_to 'Disable', [:ci, @project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm right' - .col-md-4 - %h5 Available - %table.table - %tr - %th ID - %th Token - %th - - - (Ci::Runner.all - @project.runners).each do |runner| - %tr - %td - = runner.id - %td - = runner.token - %td - = form_for [:ci, @project, @runner_project] do |f| - = f.hidden_field :runner_id, value: runner.id - = f.submit 'Add', class: 'btn btn-sm' diff --git a/app/views/ci/admin/runners/_runner.html.haml b/app/views/ci/admin/runners/_runner.html.haml deleted file mode 100644 index 701782d26bb..00000000000 --- a/app/views/ci/admin/runners/_runner.html.haml +++ /dev/null @@ -1,48 +0,0 @@ -%tr{id: dom_id(runner)} - %td - - if runner.shared? - %span.label.label-success shared - - else - %span.label.label-info specific - - unless runner.active? - %span.label.label-danger paused - - %td - = link_to ci_admin_runner_path(runner) do - = runner.short_sha - %td - .runner-description - = runner.description - %span (#{link_to 'edit', '#', class: 'edit-runner-link'}) - .runner-description-form.hide - = form_for [:ci, :admin, runner], remote: true, html: { class: 'form-inline' } do |f| - .form-group - = f.text_field :description, class: 'form-control' - = f.submit 'Save', class: 'btn' - %span (#{link_to 'cancel', '#', class: 'cancel'}) - %td - - if runner.shared? - \- - - else - = runner.projects.count(:all) - %td - #{runner.builds.count(:all)} - %td - - runner.tag_list.each do |tag| - %span.label.label-primary - = tag - %td - - if runner.contacted_at - #{time_ago_in_words(runner.contacted_at)} ago - - else - Never - %td - .pull-right - = link_to 'Edit', ci_admin_runner_path(runner), class: 'btn btn-sm' -   - - if runner.active? - = link_to 'Pause', [:pause, :ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :get, class: 'btn btn-danger btn-sm' - - else - = link_to 'Resume', [:resume, :ci, :admin, runner], method: :get, class: 'btn btn-success btn-sm' - = link_to 'Remove', [:ci, :admin, runner], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml deleted file mode 100644 index bacaccfbffa..00000000000 --- a/app/views/ci/admin/runners/index.html.haml +++ /dev/null @@ -1,53 +0,0 @@ -%p.lead - %span To register a new runner you should enter the following registration token. With this token the runner will request a unique runner token and use that for future communication. - %code #{GitlabCi::REGISTRATION_TOKEN} - -.bs-callout - %p - A 'runner' is a process which runs a build. - You can setup as many runners as you need. - %br - Runners can be placed on separate users, servers, and even on your local machine. - %br - - %div - %span Each runner can be in one of the following states: - %ul - %li - %span.label.label-success shared - \- run builds from all unassigned projects - %li - %span.label.label-info specific - \- run builds from assigned projects - %li - %span.label.label-danger paused - \- runner will not receive any new builds - -.append-bottom-20.clearfix - .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', spellcheck: false - = submit_tag 'Search', class: 'btn' - - .pull-right.light - Runners with last contact less than a minute ago: #{@active_runners_cnt} - -%br - -.table-holder - %table.table - %thead - %tr - %th Type - %th Runner token - %th Description - %th Projects - %th Builds - %th Tags - %th Last contact - %th - - - @runners.each do |runner| - = render "ci/admin/runners/runner", runner: runner -= paginate @runners diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml deleted file mode 100644 index fd3d33d657b..00000000000 --- a/app/views/ci/admin/runners/show.html.haml +++ /dev/null @@ -1,130 +0,0 @@ -= content_for :title do - %h3.project-title - Runner ##{@runner.id} - .pull-right - - if @runner.shared? - %span.runner-state.runner-state-shared - Shared - - else - %span.runner-state.runner-state-specific - Specific - - - -- if @runner.shared? - .bs-callout.bs-callout-success - %h4 This runner will process builds from ALL UNASSIGNED projects - %p - If you want runners to build only specific projects, enable them in the table below. - Keep in mind that this is a one way transition. -- else - .bs-callout.bs-callout-info - %h4 This runner will process builds only from ASSIGNED projects - %p You can't make this a shared runner. -%hr -= form_for @runner, url: ci_admin_runner_path(@runner), html: { class: 'form-horizontal' } do |f| - .form-group - = label_tag :token, class: 'control-label' do - Token - .col-sm-10 - = f.text_field :token, class: 'form-control', readonly: true - .form-group - = label_tag :description, class: 'control-label' do - Description - .col-sm-10 - = f.text_field :description, class: 'form-control' - .form-group - = label_tag :tag_list, class: 'control-label' do - Tags - .col-sm-10 - = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control' - .help-block You can setup builds to only use runners with specific tags - .form-actions - = f.submit 'Save', class: 'btn btn-save' - -.row - .col-md-6 - %h4 Restrict projects for this runner - - if @runner.projects.any? - %table.table - %thead - %tr - %th Assigned projects - %th - - @runner.runner_projects.each do |runner_project| - - project = runner_project.project - - if project.gl_project - %tr.alert-info - %td - %strong - = project.name - %td - .pull-right - = link_to 'Disable', [:ci, :admin, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' - - %table.table - %thead - %tr - %th Project - %th - .pull-right - = link_to 'Assign to all', assign_all_ci_admin_runner_path(@runner), - class: 'btn btn-sm assign-all-runner', - title: 'Assign runner to all projects', - method: :put - - %tr - %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', spellcheck: false - = submit_tag 'Search', class: 'btn' - - %td - - @projects.each do |project| - %tr - %td - = project.name - %td - .pull-right - = form_for [:ci, :admin, project, project.runner_projects.new] do |f| - = f.hidden_field :runner_id, value: @runner.id - = f.submit 'Enable', class: 'btn btn-xs' - = paginate @projects - - .col-md-6 - %h4 Recent builds served by this runner - %table.builds.runner-builds - %thead - %tr - %th Build ID - %th Status - %th Project - %th Commit - %th Finished at - - - @builds.each do |build| - - gl_project = build.gl_project - %tr.build - %td.id - - if gl_project - = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do - = build.id - - else - = build.id - - %td.status - = ci_status_with_icon(build.status) - - %td.status - - if gl_project - = gl_project.name_with_namespace - - %td.build-link - - if gl_project - = link_to ci_status_path(build.commit) do - %strong #{build.commit.short_sha} - - %td.timestamp - - if build.finished_at - %span #{time_ago_in_words build.finished_at} ago diff --git a/app/views/ci/admin/runners/update.js.haml b/app/views/ci/admin/runners/update.js.haml deleted file mode 100644 index 2b7d3067e20..00000000000 --- a/app/views/ci/admin/runners/update.js.haml +++ /dev/null @@ -1,2 +0,0 @@ -:plain - $("#runner_#{@runner.id}").replaceWith("#{escape_javascript(render(@runner))}") diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml index b24a3b826cf..11163813f3e 100644 --- a/app/views/ci/commits/_commit.html.haml +++ b/app/views/ci/commits/_commit.html.haml @@ -27,7 +27,6 @@ - if commit.finished_at %span #{time_ago_in_words commit.finished_at} ago - - if commit.project.coverage_enabled? + - if commit.coverage %td.coverage - - if commit.coverage - #{commit.coverage}% + #{commit.coverage}% diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml index db2d7f2f4b6..ec768858669 100644 --- a/app/views/ci/shared/_guide.html.haml +++ b/app/views/ci/shared/_guide.html.haml @@ -4,7 +4,7 @@ %ol %li Add at least one runner to the project. - Go to #{link_to 'Runners page', runners_path(@project.gl_project), target: :blank} for instructions. + Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions. %li Put the .gitlab-ci.yml in the root of your repository. Examples can be found in #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} diff --git a/app/views/ci/user_sessions/new.html.haml b/app/views/ci/user_sessions/new.html.haml deleted file mode 100644 index b8d9a1d7089..00000000000 --- a/app/views/ci/user_sessions/new.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -.login-block - %h2 Login using GitLab account - %p.light - Make sure you have an account on the GitLab server - = link_to GitlabCi.config.gitlab_server.url, GitlabCi.config.gitlab_server.url, no_turbolink - %hr - = link_to "Login with GitLab", auth_ci_user_sessions_path(state: params[:state]), no_turbolink.merge( class: 'btn btn-login btn-success' ) diff --git a/app/views/layouts/ci/_nav_admin.html.haml b/app/views/layouts/ci/_nav_admin.html.haml deleted file mode 100644 index dcda04a4638..00000000000 --- a/app/views/layouts/ci/_nav_admin.html.haml +++ /dev/null @@ -1,35 +0,0 @@ -%ul.nav.nav-sidebar - = nav_link do - = link_to admin_root_path, title: 'Back to admin', data: {placement: 'right'}, class: 'back-link' do - = icon('caret-square-o-left fw') - %span - Back to admin - - %li.separate-item - = nav_link path: 'projects#index' do - = link_to ci_admin_projects_path do - = icon('list-alt fw') - %span - Projects - = nav_link path: 'events#index' do - = link_to ci_admin_events_path do - = icon('book fw') - %span - Events - = nav_link path: ['runners#index', 'runners#show'] do - = link_to ci_admin_runners_path do - = icon('cog fw') - %span - Runners - %span.count= Ci::Runner.count(:all) - = nav_link path: 'builds#index' do - = link_to ci_admin_builds_path do - = icon('link fw') - %span - Builds - %span.count= Ci::Build.count(:all) - = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do - = link_to ci_admin_application_settings_path do - = icon('cogs fw') - %span - Settings diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml deleted file mode 100644 index f094edbfa87..00000000000 --- a/app/views/layouts/ci/_nav_project.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -%ul.nav.nav-sidebar - = nav_link do - = link_to project_path(@project.gl_project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do - = icon('caret-square-o-left fw') - %span - Back to project - %li.separate-item - = nav_link path: 'events#index' do - = link_to ci_project_events_path(@project) do - = icon('book fw') - %span - Events diff --git a/app/views/layouts/ci/admin.html.haml b/app/views/layouts/ci/admin.html.haml deleted file mode 100644 index c8cb185d28c..00000000000 --- a/app/views/layouts/ci/admin.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = "Admin area" - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page', sidebar: 'nav_admin' diff --git a/app/views/layouts/ci/application.html.haml b/app/views/layouts/ci/application.html.haml deleted file mode 100644 index 38023468d0b..00000000000 --- a/app/views/layouts/ci/application.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -!!! 5 -%html{ lang: "en"} - = render 'layouts/head' - %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page} - - header_title = "Continuous Integration" - - if current_user - = render "layouts/header/default", title: header_title - - else - = render "layouts/header/public", title: header_title - - = render 'layouts/ci/page' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index d04a3d1f227..c60ac5eefac 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -24,11 +24,18 @@ = icon('key fw') %span Deploy Keys - = nav_link do - = link_to ci_admin_projects_path, title: 'Continuous Integration' do - = icon('building fw') + = nav_link path: ['runners#index', 'runners#show'] do + = link_to admin_runners_path do + = icon('cog fw') + %span + Runners + %span.count= Ci::Runner.count(:all) + = nav_link path: 'builds#index' do + = link_to admin_builds_path do + = icon('link fw') %span - Continuous Integration + Builds + %span.count= Ci::Build.count(:all) = nav_link(controller: :logs) do = link_to admin_logs_path, title: 'Logs' do = icon('file-text fw') diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 4b32c631d5c..970da78a5c9 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -50,8 +50,3 @@ = icon('retweet fw') %span Triggers - = nav_link path: 'ci_settings#edit' do - = link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do - = icon('building fw') - %span - CI Settings diff --git a/app/views/notify/build_fail_email.html.haml b/app/views/notify/build_fail_email.html.haml index 3b251dc011a..f4e9749e5c7 100644 --- a/app/views/notify/build_fail_email.html.haml +++ b/app/views/notify/build_fail_email.html.haml @@ -7,7 +7,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -20,4 +20,4 @@ Message: #{@build.commit.git_commit_message} %p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} diff --git a/app/views/notify/build_fail_email.text.erb b/app/views/notify/build_fail_email.text.erb index 17a3b9b1d33..675acea60a1 100644 --- a/app/views/notify/build_fail_email.text.erb +++ b/app/views/notify/build_fail_email.text.erb @@ -8,4 +8,4 @@ Stage: <%= @build.stage %> Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> +Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> diff --git a/app/views/notify/build_success_email.html.haml b/app/views/notify/build_success_email.html.haml index c23f4b7e45a..8b004d34cca 100644 --- a/app/views/notify/build_success_email.html.haml +++ b/app/views/notify/build_success_email.html.haml @@ -8,7 +8,7 @@ = @project.name %p - Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)} + Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)} %p Author: #{@build.commit.git_author_name} %p @@ -21,4 +21,4 @@ Message: #{@build.commit.git_commit_message} %p - Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)} + Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)} diff --git a/app/views/notify/build_success_email.text.erb b/app/views/notify/build_success_email.text.erb index bc8b978c3d7..747da44acae 100644 --- a/app/views/notify/build_success_email.text.erb +++ b/app/views/notify/build_success_email.text.erb @@ -8,4 +8,4 @@ Stage: <%= @build.stage %> Job: <%= @build.name %> Message: <%= @build.commit.git_commit_message %> -Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %> +Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %> diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index fbf2c293db8..1a26908ab11 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -3,7 +3,7 @@ .project-issuable-filter .controls - - if @ci_project && can?(current_user, :manage_builds, @project) + - if can?(current_user, :manage_builds, @project) .pull-left.hidden-xs - if @all_builds.running_or_pending.any? = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post @@ -40,7 +40,7 @@ %thead %tr %th Status - %th Build ID + %th Runner %th Commit %th Ref %th Stage diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml index d5e81f84b56..20a5b6a66e7 100644 --- a/app/views/projects/builds/show.html.haml +++ b/app/views/projects/builds/show.html.haml @@ -56,7 +56,7 @@ %br Go to - = link_to namespace_project_runners_path(@build.gl_project.namespace, @build.gl_project) do + = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do Runners page .row.prepend-top-default @@ -113,7 +113,7 @@ %p %span.attr-name Runner: - if @build.runner && current_user && current_user.admin - = link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id) + = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id) - elsif @build.runner \##{@build.runner.id} diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml deleted file mode 100644 index ee6b8885e2d..00000000000 --- a/app/views/projects/ci_settings/_form.html.haml +++ /dev/null @@ -1,120 +0,0 @@ -%h3.page-title - CI settings -%hr -.bs-callout.help-callout - %p - If you want to test your .gitlab-ci.yml, you can use special tool - #{link_to "Lint", ci_lint_path} - %p - Edit your - #{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@ci_project)} - -- 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 - 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 - %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" - .alert.alert-error - %ul - - @ci_project.errors.full_messages.each do |msg| - %li= msg - - %fieldset - %legend Build settings - .form-group - = label_tag nil, class: 'control-label' do - Get code - .col-sm-10 - %p Get recent application code using the following command: - .radio - = label_tag do - = f.radio_button :allow_git_fetch, 'false' - %strong git clone - .light Slower but makes sure you have a clean dir before every build - .radio - = label_tag do - = f.radio_button :allow_git_fetch, 'true' - %strong git fetch - .light Faster - .form-group - = f.label :timeout_in_minutes, 'Timeout', class: 'control-label' - .col-sm-10 - = f.number_field :timeout_in_minutes, class: 'form-control', min: '0' - .light per build in minutes - - - %fieldset - %legend Build Schedule - .form-group - = f.label :always_build, 'Schedule build', class: 'control-label' - .col-sm-10 - .checkbox - = f.label :always_build do - = f.check_box :always_build - %span.light Repeat last build after X hours if no builds - .form-group - = f.label :polling_interval, "Build interval", class: 'control-label' - .col-sm-10 - = f.number_field :polling_interval, placeholder: '5', min: '0', class: 'form-control' - .light In hours - - %fieldset - %legend Project settings - .form-group - = f.label :default_ref, "Make tabs for the following branches", class: 'control-label' - .col-sm-10 - = f.text_field :default_ref, class: 'form-control', placeholder: 'master, stable' - .light You will be able to filter builds by the following branches - .form-group - = f.label :public, 'Public mode', class: 'control-label' - .col-sm-10 - .checkbox - = f.label :public do - = f.check_box :public - %span.light Anyone can see project and builds - .form-group - = f.label :coverage_regex, "Test coverage parsing", class: 'control-label' - .col-sm-10 - .input-group - %span.input-group-addon / - = f.text_field :coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' - %span.input-group-addon / - .light We will use this regular expression to find test coverage output in build trace. Leave blank if you want to disable this feature - .bs-callout.bs-callout-info - %p Below are examples of regex for existing tools: - %ul - %li - Simplecov (Ruby) - - %code \(\d+.\d+\%\) covered - %li - pytest-cov (Python) - - %code \d+\%\s*$ - %li - phpunit --coverage-text --colors=never (PHP) - - %code ^\s*Lines:\s*\d+.\d+\% - - %fieldset - %legend Advanced settings - .form-group - = f.label :token, "CI token", class: 'control-label' - .col-sm-10 - = f.text_field :token, class: 'form-control', placeholder: 'xEeFCaDAB89' - - .form-actions - = f.submit 'Save changes', class: 'btn btn-save' - - unless @ci_project.new_record? - = link_to 'Remove Project', ci_project_path(@ci_project), method: :delete, data: { confirm: 'Project will be removed. Are you sure?' }, class: 'btn btn-danger pull-right' diff --git a/app/views/projects/ci_settings/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml deleted file mode 100644 index 1374e6680f9..00000000000 --- a/app/views/projects/ci_settings/_no_runners.html.haml +++ /dev/null @@ -1,8 +0,0 @@ -.alert.alert-danger - %p - There are NO runners to build this project. - %br - You can add Specific runner for this project on Runners page - - - if current_user.admin - or add Shared runner for whole application in admin area. diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml deleted file mode 100644 index acc912d4596..00000000000 --- a/app/views/projects/ci_settings/edit.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -- page_title "CI Settings" - -- if no_runners_for_project?(@ci_project) - = render 'no_runners' - -= render 'form' diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml index e4d81182c1a..952f33184fc 100644 --- a/app/views/projects/commit/_builds.html.haml +++ b/app/views/projects/commit/_builds.html.haml @@ -1,17 +1,17 @@ .gray-content-block.middle-block .pull-right - - if @ci_project && can?(current_user, :manage_builds, @ci_commit.gl_project) + - if @ci_project && can?(current_user, :manage_builds, @ci_commit.project) - if @ci_commit.builds.latest.failed.any?(&:retryable?) - = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post + = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post - if @ci_commit.builds.running_or_pending.any? - = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post + = link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post .oneline = pluralize @statuses.count(:id), "build" - if defined?(link_to_commit) && link_to_commit for commit - = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.gl_project.namespace, @ci_commit.gl_project, @ci_commit.sha), class: "monospace" + = link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: "monospace" - if @ci_commit.duration > 0 in = time_interval_in_words @ci_commit.duration @@ -23,7 +23,7 @@ - @ci_commit.yaml_errors.split(",").each do |error| %li= error -- if @ci_commit.gl_project.builds_enabled? && !@ci_commit.ci_yaml_file +- if @ci_commit.project.builds_enabled? && !@ci_commit.ci_yaml_file .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit @@ -38,12 +38,12 @@ %th Name %th Duration %th Finished at - - if @ci_project && @ci_project.coverage_enabled? + - if @ci_commit.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, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true, allow_retry: true } + locals: { coverage: @ci_commit.project.coverage_enabled?, stage: true, allow_retry: true } - if @ci_commit.retried.any? .gray-content-block.second-block @@ -60,8 +60,8 @@ %th Name %th Duration %th Finished at - - if @ci_project && @ci_project.coverage_enabled? + - if @ci_commit.project.coverage_enabled? %th Coverage %th = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, - locals: { coverage: @ci_project.try(:coverage_enabled?), stage: true } + locals: { coverage: @ci_commit.project.coverage_enabled?, stage: true } diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml index a527bb2f84a..45a00e4d259 100644 --- a/app/views/projects/commit_statuses/_commit_status.html.haml +++ b/app/views/projects/commit_statuses/_commit_status.html.haml @@ -69,7 +69,7 @@ - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url = link_to commit_status.download_url, title: 'Download artifacts' do %i.fa.fa-download - - if current_user && can?(current_user, :manage_builds, commit_status.gl_project) + - if current_user && can?(current_user, :manage_builds, commit_status.project) - if commit_status.active? - if commit_status.cancel_url = link_to commit_status.cancel_url, method: :post, title: 'Cancel' do diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index d865d299135..a7ab9b44e79 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -112,6 +112,62 @@ %hr = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-avatar" + %fieldset.features + %legend + Continuous Integration + .form-group + .col-sm-offset-2.col-sm-10 + %p Get recent application code using the following command: + .radio + = f.label :build_allow_git_fetch do + = f.radio_button :build_allow_git_fetch, 'false' + %strong git clone + %br + %span.descr Slower but makes sure you have a clean dir before every build + .radio + = f.label :build_allow_git_fetch do + = f.radio_button :build_allow_git_fetch, 'true' + %strong git fetch + %br + %span.descr Faster + .form-group + = f.label :build_timeout_in_minutes, 'Timeout', class: 'control-label' + .col-sm-10 + = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' + %p.help-block per build in minutes + .form-group + = f.label :build_coverage_regex, "Test coverage parsing", class: 'control-label' + .col-sm-10 + .input-group + %span.input-group-addon / + = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + %p.help-block + We will use this regular expression to find test coverage output in build trace. + Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%\s*$ + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% + + + %fieldset.features + %legend + Advanced settings + .form-group + = f.label :token, "CI token", class: 'control-label' + .col-sm-10 + = f.text_field :token, class: "form-control", placeholder: 'xEeFCaDAB89' + %p.help-block The secure token used to checkout project. + .form-actions = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml index cf4285a2671..4105d683117 100644 --- a/app/views/projects/graphs/ci/_overall.haml +++ b/app/views/projects/graphs/ci/_overall.haml @@ -1,20 +1,19 @@ -- ci_project = @project.gitlab_ci_project %h4 Overall stats %ul %li Total: - %strong= pluralize ci_project.builds.count(:all), 'build' + %strong= pluralize @project.ci_builds.count(:all), 'build' %li Successful: - %strong= pluralize ci_project.builds.success.count(:all), 'build' + %strong= pluralize @project.ci_builds.success.count(:all), 'build' %li Failed: - %strong= pluralize ci_project.builds.failed.count(:all), 'build' + %strong= pluralize @project.ci_builds.failed.count(:all), 'build' %li Success ratio: %strong - #{success_ratio(ci_project.builds.success, ci_project.builds.failed)}% + #{success_ratio(@project.ci_builds.success, @project.ci_builds.failed)}% %li Commits covered: %strong - = ci_project.commits.count(:all) + = @project.ci_commits.count(:all) diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index e6b8a2e6fe7..1a61cb07b5f 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -15,10 +15,10 @@ - if runner.belongs_to_one_project? = link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - else - - runner_project = @ci_project.runner_projects.find_by(runner_id: runner) - = link_to 'Disable for this project', [:ci, @ci_project, runner_project], data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' + - runner_project = @project.ci_runner_projects.find_by(runner_id: runner) + = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - elsif runner.specific? - = form_for [:ci, @ci_project, @ci_project.runner_projects.new] do |f| + = form_for [@project.namespace, @project, @project.ci_runner_projects.new] do |f| = f.hidden_field :runner_id, value: runner.id = f.submit 'Enable for this project', class: 'btn btn-sm' .pull-right diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 316ea747b14..7e4ffc884a1 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -3,11 +3,11 @@ .bs-callout.bs-callout-warning GitLab Runners do not offer secure isolation between projects that they do builds for. You are TRUSTING all GitLab users who can push code to project A, B or C to run shell scripts on the machine hosting runner X. %hr - - if @ci_project.shared_runners_enabled - = link_to toggle_shared_runners_ci_project_path(@ci_project), class: 'btn btn-warning', method: :post do + - if @project.shared_runners_enabled? + = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do Disable shared runners - else - = link_to toggle_shared_runners_ci_project_path(@ci_project), class: 'btn btn-success', method: :post do + = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-success', method: :post do Enable shared runners   for this project diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index c13625c7e49..67767f3e34e 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -12,7 +12,7 @@ %code #{ci_root_url(only_path: false)} %li Use the following registration token during setup: - %code #{@ci_project.token} + %code #{@project.token} %li Start runner! diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index b3ad79a200e..147cda51d5a 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -36,7 +36,7 @@ :plain curl -X POST \ -F token=TOKEN \ - #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')} + #{ci_build_trigger_url(@project.id, 'REF_NAME')} %h3 Use .gitlab-ci.yml @@ -51,7 +51,7 @@ trigger: type: deploy script: - - "curl -X POST -F token=TOKEN #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')}" + - "curl -X POST -F token=TOKEN #{ci_build_trigger_url(@project.id, 'REF_NAME')}" %h3 Pass build variables @@ -66,4 +66,4 @@ curl -X POST \ -F token=TOKEN \ -F "variables[RUN_NIGHTLY_BUILD]=true" \ - #{ci_build_trigger_url(@ci_project.id, 'REF_NAME')} + #{ci_build_trigger_url(@project.id, 'REF_NAME')} diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index e052da1ac43..e7bc866d6bc 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -10,16 +10,16 @@ %hr -= nested_form_for @ci_project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| += nested_form_for @project, url: url_for(controller: 'projects/variables', action: 'update'), html: { class: 'form-horizontal' } do |f| - if @project.errors.any? #error_explanation - %p.lead= "#{pluralize(@ci_project.errors.count, "error")} prohibited this project from being saved:" + %p.lead= "#{pluralize(@project.errors.count, "error")} prohibited this project from being saved:" .alert.alert-error %ul - - @ci_project.errors.full_messages.each do |msg| + - @project.errors.full_messages.each do |msg| %li= msg - = f.fields_for :variables do |variable_form| + = f.fields_for :ci_variables do |variable_form| .form-group = variable_form.label :key, 'Key', class: 'control-label' .col-sm-10 @@ -34,7 +34,7 @@ %hr %p .clearfix - = f.link_to_add "Add a variable", :variables, class: 'btn btn-success pull-right' + = f.link_to_add "Add a variable", :ci_variables, class: 'btn btn-success pull-right' .form-actions = f.submit 'Save changes', class: 'btn btn-save', return_to: request.original_url diff --git a/config/routes.rb b/config/routes.rb index a104e686ac6..50836d63fa5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,43 +24,10 @@ Rails.application.routes.draw do resource :lint, only: [:show, :create] resources :projects do - collection do - post :add - get :disabled - end - member do get :status, to: 'projects#badge' get :integration - post :toggle_shared_runners - end - - resources :runner_projects, only: [:create, :destroy] - end - - resource :user_sessions do - get :auth - get :callback - end - - namespace :admin do - resources :runners, only: [:index, :show, :update, :destroy] do - member do - put :assign_all - get :resume - get :pause - end - end - - resources :events, only: [:index] - - resources :projects do - resources :runner_projects end - - resources :builds, only: :index - - resource :application_settings, only: [:show, :update] end root to: 'projects#index' @@ -271,6 +238,8 @@ Rails.application.routes.draw do member do put :transfer end + + resources :runner_projects end end @@ -280,6 +249,19 @@ Rails.application.routes.draw do resources :labels + resources :runners, only: [:index, :show, :update, :destroy] do + member do + get :resume + get :pause + end + end + + resources :builds, only: :index do + collection do + post :cancel_all + end + end + root to: 'dashboard#index' end @@ -595,7 +577,6 @@ Rails.application.routes.draw do resources :protected_branches, only: [:index, :create, :update, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resource :variables, only: [:show, :update] resources :triggers, only: [:index, :create, :destroy] - resource :ci_settings, only: [:edit, :update, :destroy] resources :builds, only: [:index, :show] do collection do @@ -674,7 +655,13 @@ Rails.application.routes.draw do get :resume get :pause end + + collection do + post :toggle_shared_runners + end end + + resources :runner_projects, only: [:create, :destroy] end end end diff --git a/db/migrate/20151203162135_add_ci_to_project.rb b/db/migrate/20151203162135_add_ci_to_project.rb new file mode 100644 index 00000000000..e95942666c3 --- /dev/null +++ b/db/migrate/20151203162135_add_ci_to_project.rb @@ -0,0 +1,10 @@ +class AddCiToProject < ActiveRecord::Migration + def up + add_column :projects, :builds_enabled, :boolean, default: true, null: false + add_column :projects, :shared_runners_enabled, :boolean, default: true, null: false + add_column :projects, :token, :string + add_column :projects, :build_coverage_regex, :string + add_column :projects, :build_allow_git_fetch, :boolean, default: true, null: false + add_column :projects, :build_timeout, :integer, default: 3600, null: false + end +end diff --git a/db/migrate/20151204110124_add_project_id_to_ci.rb b/db/migrate/20151204110124_add_project_id_to_ci.rb new file mode 100644 index 00000000000..5d1cf543576 --- /dev/null +++ b/db/migrate/20151204110124_add_project_id_to_ci.rb @@ -0,0 +1,8 @@ +class AddProjectIdToCi < ActiveRecord::Migration + def up + add_column :ci_builds, :gl_project_id, :integer + add_column :ci_runner_projects, :gl_project_id, :integer + add_column :ci_triggers, :gl_project_id, :integer + add_column :ci_variables, :gl_project_id, :integer + end +end diff --git a/db/migrate/20151204110613_migrate_ci_to_project.rb b/db/migrate/20151204110613_migrate_ci_to_project.rb new file mode 100644 index 00000000000..1777b6170b4 --- /dev/null +++ b/db/migrate/20151204110613_migrate_ci_to_project.rb @@ -0,0 +1,36 @@ +class MigrateCiToProject < ActiveRecord::Migration + def up + migrate_project_id_for_table('ci_runner_projects') + migrate_project_id_for_table('ci_triggers') + migrate_project_id_for_table('ci_variables') + migrate_project_id_for_builds + + migrate_project_column('shared_runners_enabled') + migrate_project_column('token') + migrate_project_column('coverage_regex', 'build_coverage_regex') + migrate_project_column('allow_git_fetch', 'build_allow_git_fetch') + migrate_project_column('timeout', 'build_timeout') + migrate_ci_service + end + + def migrate_project_id_for_table(table) + subquery = "SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = #{table}.project_id" + execute("UPDATE #{table} SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_id_for_builds + subquery = 'SELECT gl_project_id FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id' + execute("UPDATE ci_builds SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_column(column, new_column = nil) + new_column ||= column + subquery = "SELECT #{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" + execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") + end + + def migrate_ci_service + subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService'" + execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE builds_enabled IS NULL AND (#{subquery}) IS NOT NULL") + end +end diff --git a/db/migrate/20151204110832_add_index_to_ci_tables.rb b/db/migrate/20151204110832_add_index_to_ci_tables.rb new file mode 100644 index 00000000000..b95931334c6 --- /dev/null +++ b/db/migrate/20151204110832_add_index_to_ci_tables.rb @@ -0,0 +1,11 @@ +class AddIndexToCiTables < ActiveRecord::Migration + def up + add_index :ci_builds, :gl_project_id + add_index :ci_runner_projects, :gl_project_id + add_index :ci_triggers, :gl_project_id + add_index :ci_variables, :gl_project_id + add_index :projects, :token + add_index :projects, :builds_enabled + add_index :projects, [:builds_enabled, :shared_runners_enabled] + end +end diff --git a/db/migrate/20151204123933_drop_null_for_ci_tables.rb b/db/migrate/20151204123933_drop_null_for_ci_tables.rb new file mode 100644 index 00000000000..0b007430b0c --- /dev/null +++ b/db/migrate/20151204123933_drop_null_for_ci_tables.rb @@ -0,0 +1,9 @@ +class DropNullForCiTables < ActiveRecord::Migration + def up + remove_index :ci_variables, :project_id + remove_index :ci_runner_projects, :project_id + change_column_null :ci_triggers, :project_id, true + change_column_null :ci_variables, :project_id, true + change_column_null :ci_runner_projects, :project_id, true + end +end diff --git a/db/schema.rb b/db/schema.rb index 6ccd4404219..fd9004faa29 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: 20151210125927) do +ActiveRecord::Schema.define(version: 20151204123933) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -110,6 +110,7 @@ ActiveRecord::Schema.define(version: 20151210125927) do t.string "target_url" t.string "description" t.text "artifacts_file" + t.integer "gl_project_id" 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 @@ -117,6 +118,7 @@ ActiveRecord::Schema.define(version: 20151210125927) do 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", ["gl_project_id"], name: "index_ci_builds_on_gl_project_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 add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree @@ -201,13 +203,14 @@ ActiveRecord::Schema.define(version: 20151210125927) do add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree create_table "ci_runner_projects", force: :cascade do |t| - t.integer "runner_id", null: false - t.integer "project_id", null: false + t.integer "runner_id", null: false + t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end - add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree + add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| @@ -277,24 +280,27 @@ ActiveRecord::Schema.define(version: 20151210125927) do create_table "ci_triggers", force: :cascade do |t| t.string "token" - t.integer "project_id", null: false + t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" t.datetime "updated_at" + t.integer "gl_project_id" end add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree + add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree create_table "ci_variables", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id" t.string "key" t.text "value" t.text "encrypted_value" t.string "encrypted_value_salt" t.string "encrypted_value_iv" + t.integer "gl_project_id" end - add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree + add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "ci_web_hooks", force: :cascade do |t| t.string "url", null: false @@ -649,14 +655,23 @@ ActiveRecord::Schema.define(version: 20151210125927) do t.string "import_source" t.integer "commit_count", default: 0 t.text "import_error" + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false + t.string "token" + t.string "build_coverage_regex" + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false end + add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree + add_index "projects", ["builds_enabled"], name: "index_projects_on_builds_enabled", using: :btree add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree + add_index "projects", ["token"], name: "index_projects_on_token", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: :cascade do |t| diff --git a/doc/api/projects.md b/doc/api/projects.md index 43a50a9a810..1a524400627 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -32,7 +32,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ```json [ @@ -137,7 +136,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ### List ALL projects @@ -153,7 +151,6 @@ Parameters: - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at` - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc` - `search` (optional) - Return list of authorized projects according to a search criteria -- `ci_enabled_first` - Return projects ordered by ci_enabled flag. Projects with enabled GitLab CI go first ### Get single project diff --git a/features/project/service.feature b/features/project/service.feature index 5014b52b9f6..ff3e7a0b38e 100644 --- a/features/project/service.feature +++ b/features/project/service.feature @@ -7,12 +7,6 @@ Feature: Project Services When I visit project "Shop" services page Then I should see list of available services - Scenario: Activate gitlab-ci service - When I visit project "Shop" services page - And I click gitlab-ci service link - And I fill gitlab-ci settings - Then I should see service settings saved - Scenario: Activate hipchat service When I visit project "Shop" services page And I click hipchat service link diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 0d6a9a8fc66..101eb408ba1 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -104,7 +104,7 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps step 'commit has ci status' do @project.enable_ci - ci_commit = create :ci_commit, gl_project: @project, sha: sample_commit.id + ci_commit = create :ci_commit, project: @project, sha: sample_commit.id create :ci_build, commit: ci_commit end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 0107d9d8486..c4c6225242e 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -361,7 +361,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps step '"Bug NS-05" has CI status' do project = merge_request.source_project project.enable_ci - ci_commit = create :ci_commit, gl_project: project, sha: merge_request.last_commit.id + ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id create :ci_build, commit: ci_commit end diff --git a/features/steps/project/services.rb b/features/steps/project/services.rb index 1c700df0c63..ed3957ca873 100644 --- a/features/steps/project/services.rb +++ b/features/steps/project/services.rb @@ -11,7 +11,6 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(page).to have_content 'Project services' expect(page).to have_content 'Campfire' expect(page).to have_content 'HipChat' - expect(page).to have_content 'GitLab CI' expect(page).to have_content 'Assembla' expect(page).to have_content 'Pushover' expect(page).to have_content 'Atlassian Bamboo' @@ -20,15 +19,6 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps expect(page).to have_content 'Irker (IRC gateway)' end - step 'I click gitlab-ci service link' do - click_link 'GitLab CI' - end - - step 'I fill gitlab-ci settings' do - check 'Active' - click_button 'Save' - end - step 'I should see service settings saved' do expect(find_field('Active').value).to eq '1' end diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index 7021fac5fe4..da643bf3ba9 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -204,7 +204,7 @@ module SharedProject step 'project "Shop" has CI build' do project = Project.find_by(name: "Shop") - create :ci_commit, gl_project: project, sha: project.commit.sha + create :ci_commit, project: project, sha: project.commit.sha end step 'I should see last commit with CI status' do diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 2c0596c9dfb..1162271f5fc 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) + status ||= GenericCommitStatus.new(project: @project, commit: ci_commit, user: current_user) status.update(attrs) case params[:state].to_s diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 03e3056a87e..381babe291b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -64,6 +64,7 @@ module API expose :name, :name_with_namespace expose :path, :path_with_namespace expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at + expose :shared_runners_enabled expose :creator_id expose :namespace expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 92540ccf2b1..a4df810e755 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -266,12 +266,7 @@ module API projects = projects.search(params[:search]) end - if params[:ci_enabled_first].present? - projects.includes(:gitlab_ci_service). - reorder("services.active DESC, projects.#{project_order_by} #{project_sort}") - else - projects.reorder(project_order_by => project_sort) - end + projects.reorder(project_order_by => project_sort) end def project_order_by diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6928fe0eb9d..bdf4b77596e 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -82,6 +82,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # namespace_id (optional) - defaults to user namespace # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - 0 by default @@ -98,6 +99,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :namespace_id, :public, :visibility_level, @@ -126,6 +128,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) # import_url (optional) @@ -142,6 +145,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :public, :visibility_level, :import_url] @@ -183,6 +187,7 @@ module API # builds_enabled (optional) # wiki_enabled (optional) # snippets_enabled (optional) + # shared_runners_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - visibility level of a project # Example Request @@ -197,6 +202,7 @@ module API :builds_enabled, :wiki_enabled, :snippets_enabled, + :shared_runners_enabled, :public, :visibility_level] attrs = map_public_to_visibility_level(attrs) diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb index 07e68216d7f..5c347e432b4 100644 --- a/lib/ci/api/api.rb +++ b/lib/ci/api/api.rb @@ -30,9 +30,7 @@ module Ci helpers Gitlab::CurrentSettings mount Builds - mount Commits mount Runners - mount Projects mount Triggers end end diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb deleted file mode 100644 index a60769d8305..00000000000 --- a/lib/ci/api/commits.rb +++ /dev/null @@ -1,66 +0,0 @@ -module Ci - module API - class Commits < Grape::API - resource :commits do - # Get list of commits per project - # - # Parameters: - # project_id (required) - The ID of a project - # project_token (requires) - Project token - # page (optional) - # per_page (optional) - items per request (default is 20) - # - get do - required_attributes! [:project_id, :project_token] - project = Ci::Project.find(params[:project_id]) - authenticate_project_token!(project) - - commits = project.commits.page(params[:page]).per(params[:per_page] || 20) - present commits, with: Entities::CommitWithBuilds - end - - # Create a commit - # - # Parameters: - # project_id (required) - The ID of a project - # project_token (requires) - Project token - # data (required) - GitLab push data - # - # Sample GitLab push data: - # { - # "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", - # "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - # "ref": "refs/heads/master", - # "commits": [ - # { - # "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - # "message": "Update Catalan translation to e38cb41.", - # "timestamp": "2011-12-12T14:27:31+02:00", - # "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - # "author": { - # "name": "Jordi Mallach", - # "email": "jordi@softcatala.org", - # } - # }, .... more commits - # ] - # } - # - # Example Request: - # POST /commits - post do - required_attributes! [:project_id, :data, :project_token] - project = Ci::Project.find(params[:project_id]) - authenticate_project_token!(project) - commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data]) - - if commit.persisted? - present commit, with: Entities::CommitWithBuilds - else - errors = commit.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - end - end - end -end diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb deleted file mode 100644 index 23c79f3879d..00000000000 --- a/lib/ci/api/projects.rb +++ /dev/null @@ -1,157 +0,0 @@ -module Ci - module API - # Projects API - class Projects < Grape::API - before { authenticate! } - - resource :projects do - # Retrieve all Gitlab CI projects that the user has access to - # - # Example Request: - # GET /projects - get do - gitlab_projects = current_user.authorized_projects - gitlab_projects = filter_projects(gitlab_projects) - gitlab_projects = paginate gitlab_projects - - ids = gitlab_projects.map { |project| project.id } - - projects = Ci::Project.where("gitlab_id IN (?)", ids).load - present projects, with: Entities::Project - end - - # Retrieve all Gitlab CI projects that the user owns - # - # Example Request: - # GET /projects/owned - get "owned" do - gitlab_projects = current_user.owned_projects - gitlab_projects = filter_projects(gitlab_projects) - gitlab_projects = paginate gitlab_projects - - ids = gitlab_projects.map { |project| project.id } - - projects = Ci::Project.where("gitlab_id IN (?)", ids).load - present projects, with: Entities::Project - end - - # Retrieve info for a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id - get ":id" do - project = Ci::Project.find(params[:id]) - unauthorized! unless can?(current_user, :read_project, project.gl_project) - - present project, with: Entities::Project - end - - # Create Gitlab CI project using Gitlab project info - # - # Parameters: - # gitlab_id (required) - The gitlab id of the project - # default_ref - The branch to run against (defaults to `master`) - # Example Request: - # POST /projects - post do - required_attributes! [:gitlab_id] - - filtered_params = { - gitlab_id: params[:gitlab_id], - # we accept gitlab_url for backward compatibility for a while (added to 7.11) - default_ref: params[:default_ref] || 'master' - } - - project = Ci::Project.new(filtered_params) - project.build_missing_services - - if project.save - present project, with: Entities::Project - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Update a Gitlab CI project - # - # Parameters: - # id (required) - The ID of a project - # default_ref - The branch to run against (defaults to `master`) - # Example Request: - # PUT /projects/:id - put ":id" do - project = Ci::Project.find(params[:id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - attrs = attributes_for_keys [:default_ref] - - if project.update_attributes(attrs) - present project, with: Entities::Project - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Link a Gitlab CI project to a runner - # - # Parameters: - # id (required) - The ID of a CI project - # runner_id (required) - The ID of a runner - # Example Request: - # POST /projects/:id/runners/:runner_id - post ":id/runners/:runner_id" do - project = Ci::Project.find(params[:id]) - runner = Ci::Runner.find(params[:runner_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - options = { - project_id: project.id, - runner_id: runner.id - } - - runner_project = Ci::RunnerProject.new(options) - - if runner_project.save - present runner_project, with: Entities::RunnerProject - else - errors = project.errors.full_messages.join(", ") - render_api_error!(errors, 400) - end - end - - # Remove a Gitlab CI project from a runner - # - # Parameters: - # id (required) - The ID of a CI project - # runner_id (required) - The ID of a runner - # Example Request: - # DELETE /projects/:id/runners/:runner_id - delete ":id/runners/:runner_id" do - project = Ci::Project.find(params[:id]) - runner = Ci::Runner.find(params[:runner_id]) - - unauthorized! unless can?(current_user, :admin_project, project.gl_project) - - options = { - project_id: project.id, - runner_id: runner.id - } - - runner_project = Ci::RunnerProject.find_by(options) - - if runner_project.present? - runner_project.destroy - else - not_found! - end - end - end - end - end -end diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 1466fe4356e..dd77bd65863 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -3,17 +3,6 @@ module Ci # Runners API class Runners < Grape::API resource :runners do - # Get list of all available runners - # - # Example Request: - # GET /runners - get do - authenticate! - runners = Ci::Runner.all - - present runners, with: Entities::Runner - end - # Delete runner # Parameters: # token (required) - The unique token of runner @@ -47,9 +36,9 @@ module Ci tag_list: params[:tag_list], is_shared: true ) - elsif project = Ci::Project.find_by(token: params[:token]) + elsif project = Project.find_by(token: params[:token]) # Create a specific runner for project. - project.runners.create( + project.ci_runners.create( description: params[:description], tag_list: params[:tag_list] ) diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb index 40907d6db54..6d2cdd8c682 100644 --- a/lib/ci/api/triggers.rb +++ b/lib/ci/api/triggers.rb @@ -14,7 +14,7 @@ module Ci post ":id/refs/:ref/trigger" do required_attributes! [:token] - project = Ci::Project.find(params[:id]) + project = Project.find_by_ci_id(params[:id]) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger unauthorized! unless trigger.project == project diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb index 5ff7407c6fe..912ccff5f98 100644 --- a/lib/ci/charts.rb +++ b/lib/ci/charts.rb @@ -16,10 +16,10 @@ module Ci def push(from, to, format) @labels << from.strftime(format) - @total << project.builds. + @total << project.ci_builds. where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). count(:all) - @success << project.builds. + @success << project.ci_builds. where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). success.count(:all) end @@ -60,7 +60,7 @@ module Ci class BuildTime < Chart def collect - commits = project.commits.last(30) + commits = project.ci_commits.last(30) commits.each do |commit| @labels << commit.short_sha diff --git a/lib/ci/current_settings.rb b/lib/ci/current_settings.rb deleted file mode 100644 index fd78b024970..00000000000 --- a/lib/ci/current_settings.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Ci - module CurrentSettings - def current_application_settings - key = :ci_current_application_settings - - RequestStore.store[key] ||= begin - if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('ci_application_settings') - Ci::ApplicationSetting.current || Ci::ApplicationSetting.create_from_defaults - else - fake_application_settings - end - end - end - - def fake_application_settings - OpenStruct.new( - all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'], - add_pusher: Ci::Settings.gitlab_ci['add_pusher'], - ) - end - end -end diff --git a/lib/ci/scheduler.rb b/lib/ci/scheduler.rb deleted file mode 100644 index ee0958f4be1..00000000000 --- a/lib/ci/scheduler.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Ci - class Scheduler - def perform - projects = Ci::Project.where(always_build: true).all - projects.each do |project| - last_commit = project.commits.last - next unless last_commit && last_commit.last_build - - interval = project.polling_interval - if (last_commit.last_build.created_at + interval.hours) < Time.now - last_commit.retry - end - end - end - end -end diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 0d156047ff0..5a032b572ae 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -77,7 +77,9 @@ module Grack if project && matched_login.present? && git_cmd == 'git-upload-pack' underscored_service = matched_login['s'].underscore - if Service.available_services_names.include?(underscored_service) + if underscored_service == 'gitlab_ci' + return project && project.builds_enabled? && project.valid_token?(password) + elsif Service.available_services_names.include?(underscored_service) service_method = "#{underscored_service}_service" service = project.send(service_method) diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb index fa2cd551cee..86bfa0a4378 100644 --- a/lib/gitlab/build_data_builder.rb +++ b/lib/gitlab/build_data_builder.rb @@ -2,7 +2,7 @@ module Gitlab class BuildDataBuilder class << self def build(build) - project = build.gl_project + project = build.project commit = build.commit user = build.user diff --git a/lib/tasks/ci/schedule_builds.rake b/lib/tasks/ci/schedule_builds.rake deleted file mode 100644 index 49435504c67..00000000000 --- a/lib/tasks/ci/schedule_builds.rake +++ /dev/null @@ -1,6 +0,0 @@ -namespace :ci do - desc "GitLab CI | Clean running builds" - task schedule_builds: :environment do - Ci::Scheduler.new.perform - end -end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 2fcd70182b9..f76e826f138 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -42,6 +42,10 @@ FactoryGirl.define do commit factory: :ci_commit + after(:build) do |build, evaluator| + build.project = build.commit.project + end + factory :ci_not_started_build do started_at nil finished_at nil diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb index 70e3fa319c6..b42cafa518a 100644 --- a/spec/factories/ci/commits.rb +++ b/spec/factories/ci/commits.rb @@ -21,7 +21,7 @@ FactoryGirl.define do factory :ci_empty_commit, class: Ci::Commit do sha '97de212e80737a608d939f648d959671fb0a0142' - gl_project factory: :empty_project + project factory: :empty_project factory :ci_commit_without_jobs do after(:build) do |commit| diff --git a/spec/factories/ci/events.rb b/spec/factories/ci/events.rb deleted file mode 100644 index 9638618a400..00000000000 --- a/spec/factories/ci/events.rb +++ /dev/null @@ -1,24 +0,0 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# project_id :integer -# user_id :integer -# is_admin :integer -# description :text -# created_at :datetime -# updated_at :datetime -# - -FactoryGirl.define do - factory :ci_event, class: Ci::Event do - sequence :description do |n| - "updated project settings#{n}" - end - - factory :ci_admin_event do - is_admin true - end - end -end diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb deleted file mode 100644 index 11cb8c9eeaa..00000000000 --- a/spec/factories/ci/projects.rb +++ /dev/null @@ -1,50 +0,0 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) not null -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -# Read about factories at https://github.com/thoughtbot/factory_girl - -FactoryGirl.define do - factory :ci_project_without_token, class: Ci::Project do - default_ref 'master' - - shared_runners_enabled false - - factory :ci_project do - token 'iPWx6WM4lhHNedGfBpPJNP' - end - - initialize_with do - # TODO: - # this is required, because builds_enabled is initialized when Project is created - # and this create gitlab_ci_project if builds is set to true - # here we take created gitlab_ci_project and update it's attributes - ci_project = create(:empty_project).ensure_gitlab_ci_project - ci_project.update_attributes(attributes) - ci_project - end - end -end diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb index 3aa14ca434d..008d1c5d961 100644 --- a/spec/factories/ci/runner_projects.rb +++ b/spec/factories/ci/runner_projects.rb @@ -14,6 +14,6 @@ FactoryGirl.define do factory :ci_runner_project, class: Ci::RunnerProject do runner_id 1 - project_id 1 + gl_project_id 1 end end diff --git a/spec/features/atom/builds_spec.rb b/spec/features/atom/builds_spec.rb new file mode 100644 index 00000000000..72764b1629d --- /dev/null +++ b/spec/features/atom/builds_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe "Admin Builds" do + let(:commit) { FactoryGirl.create :ci_commit } + let(:build) { FactoryGirl.create :ci_build, commit: commit } + + before do + login_as :admin + end + + describe "GET /admin/builds" do + before do + build + visit admin_builds_path + end + + it { expect(page).to have_content "Running" } + it { expect(page).to have_content build.short_sha } + end + + describe "Tabs" do + it "shows all builds" do + FactoryGirl.create :ci_build, commit: commit, status: "pending" + FactoryGirl.create :ci_build, commit: commit, status: "running" + FactoryGirl.create :ci_build, commit: commit, status: "success" + FactoryGirl.create :ci_build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "All" + end + + expect(page.all(".build-link").size).to eq(4) + end + + it "shows finished builds" do + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "Finished" + end + + expect(page.find(".build-link")).not_to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build1.id) + expect(page.find(".build-link")).to have_content(build2.id) + end + + it "shows running builds" do + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "Running" + end + + expect(page.find(".build-link")).to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build2.id) + expect(page.find(".build-link")).not_to have_content(build3.id) + end + end +end diff --git a/spec/features/atom/runners_spec.rb b/spec/features/atom/runners_spec.rb new file mode 100644 index 00000000000..b1f2d401042 --- /dev/null +++ b/spec/features/atom/runners_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe "Admin Runners" do + before do + login_as :admin + end + + describe "Runners page" do + before do + runner = FactoryGirl.create(:ci_runner) + commit = FactoryGirl.create(:ci_commit) + FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) + visit admin_runners_path + end + + it { page.has_text? "Manage Runners" } + it { page.has_text? "To register a new runner" } + it { page.has_text? "Runners with last contact less than a minute ago: 1" } + + describe 'search' do + before do + FactoryGirl.create :ci_runner, description: 'runner-foo' + FactoryGirl.create :ci_runner, description: 'runner-bar' + + search_form = find('#runners-search') + search_form.fill_in 'search', with: 'runner-foo' + search_form.click_button 'Search' + end + + it { expect(page).to have_content("runner-foo") } + it { expect(page).not_to have_content("runner-bar") } + end + end + + describe "Runner show page" do + let(:runner) { FactoryGirl.create :ci_runner } + + before do + @project1 = FactoryGirl.create(:empty_project) + @project2 = FactoryGirl.create(:empty_project) + visit admin_runner_path(runner) + end + + describe 'runner info' do + it { expect(find_field('runner_token').value).to eq runner.token } + end + + describe 'projects' do + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).to have_content(@project2.name_with_namespace) } + end + + describe 'search' do + before do + search_form = find('#runner-projects-search') + search_form.fill_in 'search', with: @project1.name + search_form.click_button 'Search' + end + + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).not_to have_content(@project2.name_with_namespace) } + end + end +end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 1f99a808f87..61d81cb0c8e 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -7,15 +7,15 @@ describe "Builds" 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] + @project = @commit.project + @project.team << [@user, :master] end describe "GET /:project/builds" do context "Running scope" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + visit namespace_project_builds_path(@project.namespace, @project) end it { expect(page).to have_content 'Running' } @@ -28,7 +28,7 @@ describe "Builds" do context "Finished scope" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :finished) + visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) end it { expect(page).to have_content 'No builds to show' } @@ -37,8 +37,8 @@ describe "Builds" do context "All builds" do before do - @gl_project.ci_builds.running_or_pending.each(&:success) - visit namespace_project_builds_path(@gl_project.namespace, @gl_project, scope: :all) + @project.ci_builds.running_or_pending.each(&:success) + visit namespace_project_builds_path(@project.namespace, @project, scope: :all) end it { expect(page).to have_content 'All' } @@ -52,7 +52,7 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel_all" do before do @build.run! - visit namespace_project_builds_path(@gl_project.namespace, @gl_project) + visit namespace_project_builds_path(@project.namespace, @project) click_link "Cancel running" end @@ -62,7 +62,7 @@ describe "Builds" do describe "GET /:project/builds/:id" do before do - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) end it { expect(page).to have_content @commit.sha[0..7] } @@ -72,7 +72,7 @@ describe "Builds" do context "Download artifacts" do before do @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) end it { expect(page).to have_content 'Download artifacts' } @@ -82,7 +82,7 @@ describe "Builds" do describe "POST /:project/builds/:id/cancel" do before do @build.run! - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link "Cancel" end @@ -93,7 +93,7 @@ describe "Builds" do describe "POST /:project/builds/:id/retry" do before do @build.run! - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link "Cancel" click_link 'Retry' end @@ -105,7 +105,7 @@ describe "Builds" do describe "GET /:project/builds/:id/download" do before do @build.update_attributes(artifacts_file: artifacts_file) - visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build) + visit namespace_project_build_path(@project.namespace, @project, @build) click_link 'Download artifacts' end diff --git a/spec/features/ci/admin/builds_spec.rb b/spec/features/ci/admin/builds_spec.rb deleted file mode 100644 index 623d466c67b..00000000000 --- a/spec/features/ci/admin/builds_spec.rb +++ /dev/null @@ -1,70 +0,0 @@ -require 'spec_helper' - -describe "Admin Builds" do - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - - before do - skip_ci_admin_auth - login_as :user - end - - describe "GET /admin/builds" do - before do - build - visit ci_admin_builds_path - end - - it { expect(page).to have_content "All builds" } - it { expect(page).to have_content build.short_sha } - end - - describe "Tabs" do - it "shows all builds" do - FactoryGirl.create :ci_build, commit: commit, status: "pending" - FactoryGirl.create :ci_build, commit: commit, status: "running" - FactoryGirl.create :ci_build, commit: commit, status: "success" - FactoryGirl.create :ci_build, commit: commit, status: "failed" - - visit ci_admin_builds_path - - expect(page.all(".build-link").size).to eq(4) - end - - it "shows pending builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - - visit ci_admin_builds_path - - within ".nav.nav-tabs" do - click_on "Pending" - end - - expect(page.find(".build-link")).to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build1.id) - expect(page.find(".build-link")).not_to have_content(build2.id) - expect(page.find(".build-link")).not_to have_content(build3.id) - end - - it "shows running builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - - visit ci_admin_builds_path - - within ".nav.nav-tabs" do - click_on "Running" - end - - expect(page.find(".build-link")).to have_content(build1.id) - expect(page.find(".build-link")).not_to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build2.id) - expect(page.find(".build-link")).not_to have_content(build3.id) - end - end -end diff --git a/spec/features/ci/admin/events_spec.rb b/spec/features/ci/admin/events_spec.rb deleted file mode 100644 index a7e75cc4f6b..00000000000 --- a/spec/features/ci/admin/events_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe "Admin Events" do - let(:event) { FactoryGirl.create :ci_admin_event } - - before do - skip_ci_admin_auth - login_as :user - end - - describe "GET /admin/events" do - before do - event - visit ci_admin_events_path - end - - it { expect(page).to have_content "Events" } - it { expect(page).to have_content event.description } - end -end diff --git a/spec/features/ci/admin/projects_spec.rb b/spec/features/ci/admin/projects_spec.rb deleted file mode 100644 index b88f55a6807..00000000000 --- a/spec/features/ci/admin/projects_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe "Admin Projects" do - let(:project) { FactoryGirl.create :ci_project } - - before do - skip_ci_admin_auth - login_as :user - end - - describe "GET /admin/projects" do - before do - project - visit ci_admin_projects_path - end - - it { expect(page).to have_content "Projects" } - end -end diff --git a/spec/features/ci/admin/runners_spec.rb b/spec/features/ci/admin/runners_spec.rb deleted file mode 100644 index b83744f53a8..00000000000 --- a/spec/features/ci/admin/runners_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'spec_helper' - -describe "Admin Runners" do - before do - login_as :admin - end - - describe "Runners page" do - before do - runner = FactoryGirl.create(:ci_runner) - commit = FactoryGirl.create(:ci_commit) - FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) - visit ci_admin_runners_path - end - - it { page.has_text? "Manage Runners" } - it { page.has_text? "To register a new runner" } - it { page.has_text? "Runners with last contact less than a minute ago: 1" } - - describe 'search' do - before do - FactoryGirl.create :ci_runner, description: 'runner-foo' - FactoryGirl.create :ci_runner, description: 'runner-bar' - - search_form = find('#runners-search') - search_form.fill_in 'search', with: 'runner-foo' - search_form.click_button 'Search' - end - - it { expect(page).to have_content("runner-foo") } - it { expect(page).not_to have_content("runner-bar") } - end - end - - describe "Runner show page" do - let(:runner) { FactoryGirl.create :ci_runner } - - before do - @project1 = FactoryGirl.create(:ci_project) - @project2 = FactoryGirl.create(:ci_project) - visit ci_admin_runner_path(runner) - end - - describe 'runner info' do - it { expect(find_field('runner_token').value).to eq runner.token } - end - - describe 'projects' do - it { expect(page).to have_content(@project1.name_with_namespace) } - it { expect(page).to have_content(@project2.name_with_namespace) } - end - - describe 'search' do - before do - search_form = find('#runner-projects-search') - search_form.fill_in 'search', with: @project1.gl_project.name - search_form.click_button 'Search' - end - - it { expect(page).to have_content(@project1.name_with_namespace) } - it { expect(page).not_to have_content(@project2.name_with_namespace) } - end - end -end diff --git a/spec/features/ci/lint_spec.rb b/spec/features/ci/lint_spec.rb deleted file mode 100644 index 5d8f56e2cfb..00000000000 --- a/spec/features/ci/lint_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'spec_helper' - -describe "Lint" do - before do - login_as :user - end - - it "Yaml parsing", js: true do - content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) - visit ci_lint_path - fill_in "content", with: content - click_on "Validate" - within "table" do - expect(page).to have_content("Job - rspec") - expect(page).to have_content("Job - spinach") - expect(page).to have_content("Deploy Job - staging") - expect(page).to have_content("Deploy Job - production") - end - end - - it "Yaml parsing with error", js: true do - visit ci_lint_path - fill_in "content", with: "" - click_on "Validate" - expect(page).to have_content("Status: syntax is incorrect") - expect(page).to have_content("Error: Please provide content of .gitlab-ci.yml") - end -end diff --git a/spec/features/ci_settings_spec.rb b/spec/features/ci_settings_spec.rb deleted file mode 100644 index 7e25e883018..00000000000 --- a/spec/features/ci_settings_spec.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'spec_helper' - -describe "CI settings" do - let(:user) { create(:user) } - before { login_as(user) } - - before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit edit_namespace_project_ci_settings_path(@gl_project.namespace, @gl_project) - end - - it { expect(page).to have_content 'Build Schedule' } - - it "updates configuration" do - fill_in 'Timeout', with: '70' - click_button 'Save changes' - expect(page).to have_content 'was successfully updated' - expect(find_field('Timeout').value).to eq '70' - end -end diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 90739cd6a28..cc0d4c150fe 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -9,8 +9,7 @@ describe "Commits" do before do login_as :user project.team << [@user, :master] - @ci_project = project.ensure_gitlab_ci_project - @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha + @commit = FactoryGirl.create :ci_commit, project: project, sha: project.commit.sha @build = FactoryGirl.create :ci_build, commit: @commit @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit end diff --git a/spec/features/lint_spec.rb b/spec/features/lint_spec.rb new file mode 100644 index 00000000000..5d8f56e2cfb --- /dev/null +++ b/spec/features/lint_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe "Lint" do + before do + login_as :user + end + + it "Yaml parsing", js: true do + content = File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) + visit ci_lint_path + fill_in "content", with: content + click_on "Validate" + within "table" do + expect(page).to have_content("Job - rspec") + expect(page).to have_content("Job - spinach") + expect(page).to have_content("Deploy Job - staging") + expect(page).to have_content("Deploy Job - production") + end + end + + it "Yaml parsing with error", js: true do + visit ci_lint_path + fill_in "content", with: "" + click_on "Validate" + expect(page).to have_content("Status: syntax is incorrect") + expect(page).to have_content("Error: Please provide content of .gitlab-ci.yml") + end +end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index b0259026630..a4562f2fae8 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -8,24 +8,24 @@ describe "Runners" do describe "specific runners" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project, shared_runners_enabled: false + @project.team << [user, :master] - @project2 = FactoryGirl.create :ci_project - @project2.gl_project.team << [user, :master] + @project2 = FactoryGirl.create :empty_project + @project2.team << [user, :master] - @project3 = FactoryGirl.create :ci_project - @project3.gl_project.team << [user, :developer] + @project3 = FactoryGirl.create :empty_project + @project3.team << [user, :developer] @shared_runner = FactoryGirl.create :ci_shared_runner @specific_runner = FactoryGirl.create :ci_specific_runner @specific_runner2 = FactoryGirl.create :ci_specific_runner @specific_runner3 = FactoryGirl.create :ci_specific_runner - @project.runners << @specific_runner - @project2.runners << @specific_runner2 - @project3.runners << @specific_runner3 + @project.ci_runners << @specific_runner + @project2.ci_runners << @specific_runner2 + @project3.ci_runners << @specific_runner3 - visit runners_path(@project.gl_project) + visit runners_path(@project) end before do @@ -48,8 +48,8 @@ describe "Runners" do end it "disables specific runner for project" do - @project2.runners << @specific_runner - visit runners_path(@project.gl_project) + @project2.ci_runners << @specific_runner + visit runners_path(@project) within ".activated-specific-runners" do click_on "Disable for this project" @@ -69,9 +69,9 @@ describe "Runners" do describe "shared runners" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] - visit runners_path(@project.gl_project) + @project = FactoryGirl.create :empty_project, shared_runners_enabled: false + @project.team << [user, :master] + visit runners_path(@project) end it "enables shared runners" do @@ -82,14 +82,14 @@ describe "Runners" do describe "show page" do before do - @project = FactoryGirl.create :ci_project - @project.gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] @specific_runner = FactoryGirl.create :ci_specific_runner - @project.runners << @specific_runner + @project.ci_runners << @specific_runner end it "shows runner information" do - visit runners_path(@project.gl_project) + visit runners_path(@project) click_on @specific_runner.short_sha expect(page).to have_content(@specific_runner.platform) end diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index 69492d58878..b0705a45aee 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -5,25 +5,24 @@ describe 'Triggers' do before { login_as(user) } before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] - visit namespace_project_triggers_path(@gl_project.namespace, @gl_project) + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] + visit namespace_project_triggers_path(@project.namespace, @project) end context 'create a trigger' do before do click_on 'Add Trigger' - expect(@project.triggers.count).to eq(1) + expect(@project.ci_triggers.count).to eq(1) end it 'contains trigger token' do - expect(page).to have_content(@project.triggers.first.token) + expect(page).to have_content(@project.ci_triggers.first.token) end it 'revokes the trigger' do click_on 'Revoke' - expect(@project.triggers.count).to eq(0) + expect(@project.ci_triggers.count).to eq(0) end end end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index adb602f3edd..a6e68eeefbc 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -6,20 +6,19 @@ describe "Variables" do describe "specific runners" do before do - @project = FactoryGirl.create :ci_project - @gl_project = @project.gl_project - @gl_project.team << [user, :master] + @project = FactoryGirl.create :empty_project + @project.team << [user, :master] end it "creates variable", js: true do - visit namespace_project_variables_path(@gl_project.namespace, @gl_project) + visit namespace_project_variables_path(@project.namespace, @project) click_on "Add a variable" fill_in "Key", with: "SECRET_KEY" fill_in "Value", with: "SECRET_VALUE" click_on "Save changes" expect(page).to have_content("Variables were successfully updated.") - expect(@project.variables.count).to eq(1) + expect(@project.ci_variables.count).to eq(1) end end end diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index 73458735ad9..2dd27464de7 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -191,15 +191,10 @@ describe Grack::Auth, lib: true do context "when a gitlab ci token is provided" do let(:token) { "123" } - let(:gitlab_ci_project) { FactoryGirl.create :ci_project, token: token } + let(:project) { FactoryGirl.create :empty_project, token: token } before do - project.gitlab_ci_project = gitlab_ci_project - project.save - - gitlab_ci_service = project.build_gitlab_ci_service - gitlab_ci_service.active = true - gitlab_ci_service.save + project.update_attributes(token: token, builds_enabled: true) env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) end diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb index af2de207eba..839b30f1ff4 100644 --- a/spec/lib/gitlab/build_data_builder_spec.rb +++ b/spec/lib/gitlab/build_data_builder_spec.rb @@ -14,7 +14,7 @@ describe 'Gitlab::BuildDataBuilder' do it { expect(data[:tag]).to eq(build.tag) } it { expect(data[:build_id]).to eq(build.id) } it { expect(data[:build_status]).to eq(build.status) } - it { expect(data[:project_id]).to eq(build.gl_project.id) } - it { expect(data[:project_name]).to eq(build.gl_project.name_with_namespace) } + it { expect(data[:project_id]).to eq(build.project.id) } + it { expect(data[:project_name]).to eq(build.project.name_with_namespace) } end end diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b67b84959d9..5f64453a35f 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -73,26 +73,4 @@ describe ApplicationSetting, models: true do expect(setting.restricted_signup_domains).to eq(['example.com', '*.example.com']) end end - - context 'shared runners' do - let(:gl_project) { create(:empty_project) } - - before do - allow_any_instance_of(Project).to receive(:current_application_settings).and_return(setting) - end - - subject { gl_project.ensure_gitlab_ci_project.shared_runners_enabled } - - context 'enabled' do - before { setting.update_attributes(shared_runners_enabled: true) } - - it { is_expected.to be_truthy } - end - - context 'disabled' do - before { setting.update_attributes(shared_runners_enabled: false) } - - it { is_expected.to be_falsey } - end - end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 70c831b7cbe..70a63c05b22 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -26,9 +26,8 @@ require 'spec_helper' describe Ci::Build, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } let(:build) { FactoryGirl.create :ci_build, commit: commit } it { is_expected.to validate_presence_of :ref } @@ -120,11 +119,12 @@ describe Ci::Build, models: true do end end - describe :timeout do - subject { build.timeout } - - it { is_expected.to eq(commit.project.timeout) } - end + # TODO: build timeout + # describe :timeout do + # subject { build.timeout } + # + # it { is_expected.to eq(commit.project.timeout) } + # end describe :options do let(:options) do @@ -140,11 +140,12 @@ describe Ci::Build, models: true do it { is_expected.to eq(options) } end - describe :allow_git_fetch do - subject { build.allow_git_fetch } - - it { is_expected.to eq(project.allow_git_fetch) } - end + # TODO: allow_git_fetch + # describe :allow_git_fetch do + # subject { build.allow_git_fetch } + # + # it { is_expected.to eq(project.allow_git_fetch) } + # end describe :project do subject { build.project } @@ -164,12 +165,6 @@ describe Ci::Build, models: true do it { is_expected.to eq(project.name) } end - describe :repo_url do - subject { build.repo_url } - - it { is_expected.to eq(project.repo_url_with_auth) } - end - describe :extract_coverage do context 'valid content & regex' do subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') } @@ -237,7 +232,7 @@ describe Ci::Build, models: true do end before do - build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') + build.project.ci_variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') end it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) } @@ -266,44 +261,10 @@ describe Ci::Build, models: true do end end - describe :project_recipients do - let(:pusher_email) { 'pusher@gitlab.test' } - let(:user) { User.new(notification_email: pusher_email) } - subject { build.project_recipients } - - before do - build.update_attributes(user: user) - end - - it 'should return pusher_email as only recipient when no additional recipients are given' do - project.update_attributes(email_add_pusher: true, - email_recipients: '') - is_expected.to eq([pusher_email]) - end - - it 'should return pusher_email and additional recipients' do - project.update_attributes(email_add_pusher: true, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2', pusher_email]) - end - - it 'should return recipients' do - project.update_attributes(email_add_pusher: false, - email_recipients: 'rec1 rec2') - is_expected.to eq(['rec1', 'rec2']) - end - - it 'should return unique recipients only' do - project.update_attributes(email_add_pusher: true, - email_recipients: "rec1 rec1 #{pusher_email}") - is_expected.to eq(['rec1', pusher_email]) - end - end - describe :can_be_served? do let(:runner) { FactoryGirl.create :ci_specific_runner } - before { build.project.runners << runner } + before { build.project.ci_runners << runner } context 'runner without tags' do it 'can handle builds without tags' do @@ -346,7 +307,7 @@ describe Ci::Build, models: true do let(:runner) { FactoryGirl.create :ci_specific_runner } before do - build.project.runners << runner + build.project.ci_runners << runner runner.update_attributes(contacted_at: 1.second.ago) end @@ -383,7 +344,7 @@ describe Ci::Build, models: true do let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago } before do - build.project.runners << runner + build.project.ci_runners << runner runner.save end @@ -415,4 +376,18 @@ describe Ci::Build, models: true do is_expected.to_not be_nil end end + + describe :repo_url do + let(:build) { FactoryGirl.create :ci_build } + let(:project) { build.project } + + subject { build.repo_url } + + it { is_expected.to be_a(String) } + it { is_expected.to end_with(".git") } + it { is_expected.to start_with(project.web_url[0..6]) } + it { is_expected.to include(project.token) } + it { is_expected.to include('gitlab-ci-token') } + it { is_expected.to include(project.web_url[7..-1]) } + end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 89813cdf7fc..ac61c8fb525 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -13,17 +13,16 @@ # tag :boolean default(FALSE) # yaml_errors :text # committed_at :datetime -# gl_project_id :integer +# project_id :integer # require 'spec_helper' describe Ci::Commit, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project } + let(:commit) { FactoryGirl.create :ci_commit, project: project } - it { is_expected.to belong_to(:gl_project) } + it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:statuses) } it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:builds) } @@ -37,16 +36,16 @@ describe Ci::Commit, models: true do let(:project) { FactoryGirl.create :empty_project } it 'returns ordered list of commits' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project expect(project.ci_commits.ordered).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do - commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project - commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: project - commit4 = FactoryGirl.create :ci_commit, committed_at: nil, gl_project: project + commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1]) end end @@ -162,7 +161,7 @@ describe Ci::Commit, models: true do end describe :create_builds do - let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let!(:commit) { FactoryGirl.create :ci_commit, project: project } def create_builds(trigger_request = nil) commit.create_builds('master', false, nil, trigger_request) @@ -390,9 +389,8 @@ describe Ci::Commit, models: true do end describe "coverage" do - let(:project) { FactoryGirl.create :ci_project, coverage_regex: "/.*/" } - let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project } - let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" } + let(:commit) { FactoryGirl.create :ci_commit, project: project } it "calculates average when there are two builds with coverage" do FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb deleted file mode 100644 index e358aa02741..00000000000 --- a/spec/models/ci/project_spec.rb +++ /dev/null @@ -1,244 +0,0 @@ -# == Schema Information -# -# Table name: ci_projects -# -# id :integer not null, primary key -# name :string(255) -# timeout :integer default(3600), not null -# created_at :datetime -# updated_at :datetime -# token :string(255) -# default_ref :string(255) -# path :string(255) -# always_build :boolean default(FALSE), not null -# polling_interval :integer -# public :boolean default(FALSE), not null -# ssh_url_to_repo :string(255) -# gitlab_id :integer -# allow_git_fetch :boolean default(TRUE), not null -# email_recipients :string(255) default(""), not null -# email_add_pusher :boolean default(TRUE), not null -# email_only_broken_builds :boolean default(TRUE), not null -# skip_refs :string(255) -# coverage_regex :string(255) -# shared_runners_enabled :boolean default(FALSE) -# generated_yaml_config :text -# - -require 'spec_helper' - -describe Ci::Project, models: true do - let(:project) { FactoryGirl.create :ci_project } - let(:gl_project) { project.gl_project } - subject { project } - - it { is_expected.to have_many(:runner_projects) } - it { is_expected.to have_many(:runners) } - it { is_expected.to have_many(:events) } - it { is_expected.to have_many(:variables) } - it { is_expected.to have_many(:triggers) } - - it { is_expected.to validate_presence_of :timeout } - it { is_expected.to validate_presence_of :gitlab_id } - - describe 'before_validation' do - it 'should set an random token if none provided' do - project = FactoryGirl.create :ci_project_without_token - expect(project.token).not_to eq("") - end - - it 'should not set an random toke if one provided' do - project = FactoryGirl.create :ci_project - expect(project.token).to eq("iPWx6WM4lhHNedGfBpPJNP") - end - end - - describe :name_with_namespace do - subject { project.name_with_namespace } - - it { is_expected.to eq(project.name) } - it { is_expected.to eq(gl_project.name_with_namespace) } - end - - describe :path_with_namespace do - subject { project.path_with_namespace } - - it { is_expected.to eq(project.path) } - it { is_expected.to eq(gl_project.path_with_namespace) } - end - - describe :path_with_namespace do - subject { project.web_url } - - it { is_expected.to eq(gl_project.web_url) } - end - - describe :web_url do - subject { project.web_url } - - it { is_expected.to eq(project.gitlab_url) } - it { is_expected.to eq(gl_project.web_url) } - end - - describe :http_url_to_repo do - subject { project.http_url_to_repo } - - it { is_expected.to eq(gl_project.http_url_to_repo) } - end - - describe :ssh_url_to_repo do - subject { project.ssh_url_to_repo } - - it { is_expected.to eq(gl_project.ssh_url_to_repo) } - end - - describe :commits do - subject { project.commits } - - before do - FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project - end - - it { is_expected.to eq(gl_project.ci_commits) } - end - - describe :builds do - subject { project.builds } - - before do - commit = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project - FactoryGirl.create :ci_build, commit: commit - end - - it { is_expected.to eq(gl_project.ci_builds) } - end - - describe "ordered_by_last_commit_date" do - it "returns ordered projects" do - newest_project = FactoryGirl.create :empty_project - newest_ci_project = newest_project.ensure_gitlab_ci_project - oldest_project = FactoryGirl.create :empty_project - oldest_ci_project = oldest_project.ensure_gitlab_ci_project - project_without_commits = FactoryGirl.create :empty_project - ci_project_without_commits = project_without_commits.ensure_gitlab_ci_project - - FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: newest_project - FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: oldest_project - - expect(Ci::Project.ordered_by_last_commit_date).to eq([newest_ci_project, oldest_ci_project, ci_project_without_commits]) - end - end - - context :valid_project do - let(:commit) { FactoryGirl.create(:ci_commit) } - - context :project_with_commit_and_builds do - let(:project) { commit.project } - - before do - FactoryGirl.create(:ci_build, commit: commit) - end - - it { expect(project.status).to eq('pending') } - it { expect(project.last_commit).to be_kind_of(Ci::Commit) } - it { expect(project.human_status).to eq('pending') } - end - end - - describe '#email_notification?' do - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - expect(project.email_notification?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: 'test tesft' - expect(project.email_notification?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: false, email_recipients: '' - expect(project.email_notification?).to eq(false) - end - end - - describe '#broken_or_success?' do - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(true) - allow(project).to receive(:success?).and_return(true) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(true) - allow(project).to receive(:success?).and_return(false) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(false) - allow(project).to receive(:success?).and_return(true) - expect(project.broken_or_success?).to eq(true) - end - - it do - project = FactoryGirl.create :ci_project, email_add_pusher: true - allow(project).to receive(:broken?).and_return(false) - allow(project).to receive(:success?).and_return(false) - expect(project.broken_or_success?).to eq(false) - end - end - - describe :repo_url_with_auth do - let(:project) { FactoryGirl.create :ci_project } - subject { project.repo_url_with_auth } - - it { is_expected.to be_a(String) } - it { is_expected.to end_with(".git") } - it { is_expected.to start_with(project.gitlab_url[0..6]) } - it { is_expected.to include(project.token) } - it { is_expected.to include('gitlab-ci-token') } - it { is_expected.to include(project.gitlab_url[7..-1]) } - end - - describe :any_runners do - it "there are no runners available" do - project = FactoryGirl.create(:ci_project) - expect(project.any_runners?).to be_falsey - end - - it "there is a specific runner" do - project = FactoryGirl.create(:ci_project) - project.runners << FactoryGirl.create(:ci_specific_runner) - expect(project.any_runners?).to be_truthy - end - - it "there is a shared runner" do - project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) - FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners?).to be_truthy - end - - it "there is a shared runner, but they are prohibited to use" do - project = FactoryGirl.create(:ci_project) - FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners?).to be_falsey - end - - it "checks the presence of specific runner" do - project = FactoryGirl.create(:ci_project) - specific_runner = FactoryGirl.create(:ci_specific_runner) - project.runners << specific_runner - expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy - end - - it "checks the presence of shared runner" do - project = FactoryGirl.create(:ci_project, shared_runners_enabled: true) - shared_runner = FactoryGirl.create(:ci_shared_runner) - expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy - end - end -end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 6ebb5e86863..33388f97826 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -38,7 +38,7 @@ describe Ci::Runner, models: true do end describe :assign_to do - let!(:project) { FactoryGirl.create :ci_project } + let!(:project) { FactoryGirl.create :empty_project } let!(:shared_runner) { FactoryGirl.create(:ci_shared_runner) } before { shared_runner.assign_to(project) } @@ -116,18 +116,18 @@ describe Ci::Runner, models: true do describe "belongs_to_one_project?" do it "returns false if there are two projects runner assigned to" do runner = FactoryGirl.create(:ci_specific_runner) - project = FactoryGirl.create(:ci_project) - project1 = FactoryGirl.create(:ci_project) - project.runners << runner - project1.runners << runner + project = FactoryGirl.create(:empty_project) + project1 = FactoryGirl.create(:empty_project) + project.ci_runners << runner + project1.ci_runners << runner expect(runner.belongs_to_one_project?).to be_falsey end it "returns true" do runner = FactoryGirl.create(:ci_specific_runner) - project = FactoryGirl.create(:ci_project) - project.runners << runner + project = FactoryGirl.create(:empty_project) + project.ci_runners << runner expect(runner.belongs_to_one_project?).to be_truthy end diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 61eb3c08296..cb2f51e2011 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -13,7 +13,7 @@ require 'spec_helper' describe Ci::Trigger, models: true do - let(:project) { FactoryGirl.create :ci_project } + let(:project) { FactoryGirl.create :empty_project } describe 'before_validation' do it 'should set an random token if none provided' do diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 5e311ead28b..6131191f0fa 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -44,7 +44,7 @@ describe CommitStatus, models: true do 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 delegate_method(:project).to(:commit) } it { is_expected.to respond_to :success? } it { is_expected.to respond_to :failed? } diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb deleted file mode 100644 index 835bf364050..00000000000 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -# == 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 GitlabCiService, models: true do - describe 'associations' do - it { is_expected.to belong_to(:project) } - it { is_expected.to have_one(:service_hook) } - end - - describe 'commits methods' do - before do - @ci_project = create(:ci_project) - @service = GitlabCiService.new - allow(@service).to receive_messages( - service_hook: true, - project_url: 'http://ci.gitlab.org/projects/2', - token: 'verySecret', - project: @ci_project.gl_project - ) - end - - describe :build_page do - it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/builds")} - end - - describe "execute" do - let(:user) { create(:user, username: 'username') } - let(:project) { create(:project, name: 'project') } - let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) } - - it "calls CreateCommitService" do - expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data) - - @service.execute(push_sample_data) - end - end - end -end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6ddb0e2b8f7..ee106b11fbb 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -54,6 +54,13 @@ describe Project, models: true do it { is_expected.to have_one(:slack_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } + it { is_expected.to have_many(:ci_commits) } + it { is_expected.to have_many(:ci_statuses) } + it { is_expected.to have_many(:ci_builds) } + it { is_expected.to have_many(:ci_runner_projects) } + it { is_expected.to have_many(:ci_runners) } + it { is_expected.to have_many(:ci_variables) } + it { is_expected.to have_many(:ci_triggers) } end describe 'modules' do @@ -88,6 +95,18 @@ describe Project, models: true do expect(project2.errors[:limit_reached].first).to match(/Your project limit is 0/) end end + + describe 'project token' do + it 'should set an random token if none provided' do + project = FactoryGirl.create :empty_project, token: '' + expect(project.token).not_to eq('') + end + + it 'should not set an random toke if one provided' do + project = FactoryGirl.create :empty_project, token: 'my-token' + expect(project.token).to eq('my-token') + end + end describe 'Respond to' do it { is_expected.to respond_to(:url_to_repo) } @@ -395,12 +414,7 @@ describe Project, models: true do describe :ci_commit do let(:project) { create :project } - let(:commit) { create :ci_commit, gl_project: project } - - before do - project.ensure_gitlab_ci_project - project.create_gitlab_ci_service(active: true) - end + let(:commit) { create :ci_commit, project: project } it { expect(project.ci_commit(commit.sha)).to eq(commit) } end @@ -412,9 +426,7 @@ describe Project, models: true do subject { project.builds_enabled } - it { is_expected.to eq(project.gitlab_ci_service.active) } it { expect(project.builds_enabled?).to be_truthy } - it { expect(project.gitlab_ci_project).to be_a(Ci::Project) } end describe '.trending' do @@ -475,4 +487,65 @@ describe Project, models: true do it { is_expected.to eq([]) } end end + + context 'shared runners by default' do + let(:project) { create(:empty_project) } + + subject { project.shared_runners_enabled } + + context 'are enabled' do + before { stub_application_setting(shared_runners_enabled: true) } + + it { is_expected.to be_truthy } + end + + context 'are disabled' do + before { stub_application_setting(shared_runners_enabled: false) } + + it { is_expected.to be_falsey } + end + end + + describe :any_runners do + let(:project) { create(:empty_project, shared_runners_enabled: shared_runners_enabled) } + let(:specific_runner) { create(:ci_specific_runner) } + let(:shared_runner) { create(:ci_shared_runner) } + + context 'for shared runners disabled' do + let(:shared_runners_enabled) { false } + + it 'there are no runners available' do + expect(project.any_runners?).to be_falsey + end + + it 'there is a specific runner' do + project.ci_runners << specific_runner + expect(project.any_runners?).to be_truthy + end + + it 'there is a shared runner, but they are prohibited to use' do + shared_runner + expect(project.any_runners?).to be_falsey + end + + it 'checks the presence of specific runner' do + project.ci_runners << specific_runner + expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy + end + end + + context 'for shared runners enabled' do + let(:shared_runners_enabled) { true } + + it 'there is a shared runner' do + shared_runner + expect(project.any_runners?).to be_truthy + end + + it 'checks the presence of shared runner' do + shared_runner + expect(project.any_runners? { |runner| runner == shared_runner }).to be_truthy + end + end + end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 24b765f4979..e784b7d1f2d 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -86,18 +86,6 @@ describe API::API, api: true do expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project3.id) end - - it 'returns projects in the correct order when ci_enabled_first parameter is passed' do - [project, project2, project3].each do |project| - project.builds_enabled = false - project.build_missing_services - end - project2.builds_enabled = true - get api('/projects', user), { ci_enabled_first: 'true' } - expect(response.status).to eq(200) - expect(json_response).to be_an Array - expect(json_response.first['id']).to eq(project2.id) - end end end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index c2be045099d..0aa9981ea8d 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -4,8 +4,7 @@ describe Ci::API::API do include ApiHelpers let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) } - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { project.gl_project } + let(:project) { FactoryGirl.create(:empty_project) } before do stub_ci_commit_to_return_yaml_file @@ -13,16 +12,15 @@ describe Ci::API::API do describe "Builds API for runners" do let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") } - let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") } - let(:shared_gl_project) { shared_project.gl_project } + let(:shared_project) { FactoryGirl.create(:empty_project, name: "SharedProject") } before do - FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id + FactoryGirl.create :ci_runner_project, project: project, runner: runner end describe "POST /builds/register" do it "should start a build" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) build = commit.builds.first @@ -40,7 +38,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for specific runner" do - commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project) + commit = FactoryGirl.create(:ci_commit, project: shared_project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: runner.token @@ -49,7 +47,7 @@ describe Ci::API::API do end it "should return 404 error if no builds for shared runner" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) FactoryGirl.create(:ci_build, commit: commit, status: 'pending') post ci_api("/builds/register"), token: shared_runner.token @@ -58,7 +56,7 @@ describe Ci::API::API do end it "returns options" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -68,9 +66,9 @@ describe Ci::API::API do end it "returns variables" do - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) - project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") + project.ci_variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -85,11 +83,11 @@ describe Ci::API::API do it "returns variables for triggers" do trigger = FactoryGirl.create(:ci_trigger, project: project) - commit = FactoryGirl.create(:ci_commit, gl_project: gl_project) + commit = FactoryGirl.create(:ci_commit, project: project) trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds('master', false, nil, trigger_request) - project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") + project.ci_variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -106,7 +104,7 @@ describe Ci::API::API do end describe "PUT /builds/:id" do - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project)} + let(:commit) { FactoryGirl.create(:ci_commit, project: project)} let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } it "should update a running build" do @@ -126,7 +124,7 @@ describe Ci::API::API do context "Artifacts" do let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } + let(:commit) { FactoryGirl.create(:ci_commit, project: project) } let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) } let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") } let(:post_url) { ci_api("/builds/#{build.id}/artifacts") } diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb deleted file mode 100644 index aa51ba95bca..00000000000 --- a/spec/requests/ci/api/commits_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'spec_helper' - -describe Ci::API::API, 'Commits' do - include ApiHelpers - - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { project.gl_project } - let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) } - - let(:options) do - { - project_token: project.token, - project_id: project.id - } - end - - describe "GET /commits" do - before { commit } - - it "should return commits per project" do - get ci_api("/commits"), options - - expect(response.status).to eq(200) - expect(json_response.count).to eq(1) - expect(json_response.first["project_id"]).to eq(project.id) - expect(json_response.first["sha"]).to eq(commit.sha) - end - end - - describe "POST /commits" do - let(:data) do - { - "before" => "95790bf891e76fee5e1747ab589903a6a1f80f22", - "after" => "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", - "ref" => "refs/heads/master", - "commits" => [ - { - "id" => "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "message" => "Update Catalan translation to e38cb41.", - "timestamp" => "2011-12-12T14:27:31+02:00", - "url" => "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", - "author" => { - "name" => "Jordi Mallach", - "email" => "jordi@softcatala.org", - } - } - ] - } - end - - it "should create a build" do - post ci_api("/commits"), options.merge(data: data) - - expect(response.status).to eq(201) - expect(json_response['sha']).to eq("da1560886d4f094c3e6c9ef40349f7d38b5d27d7") - end - - it "should return 400 error if no data passed" do - post ci_api("/commits"), options - - expect(response.status).to eq(400) - expect(json_response['message']).to eq("400 (Bad request) \"data\" not given") - end - end -end diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb deleted file mode 100644 index b1fbdac5970..00000000000 --- a/spec/requests/ci/api/projects_spec.rb +++ /dev/null @@ -1,159 +0,0 @@ -require 'spec_helper' - -describe Ci::API::API do - include ApiHelpers - - let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } - let(:user) { create(:user) } - let(:private_token) { user.private_token } - - let(:options) do - { - private_token: private_token, - url: gitlab_url - } - end - - before do - stub_gitlab_calls - end - - context "requests for scoped projects" do - # NOTE: These ids are tied to the actual projects on demo.gitlab.com - describe "GET /projects" do - let!(:project1) { FactoryGirl.create(:ci_project) } - let!(:project2) { FactoryGirl.create(:ci_project) } - - before do - project1.gl_project.team << [user, :developer] - project2.gl_project.team << [user, :developer] - end - - it "should return all projects on the CI instance" do - get ci_api("/projects"), options - expect(response.status).to eq(200) - expect(json_response.count).to eq(2) - expect(json_response.first["id"]).to eq(project1.id) - expect(json_response.last["id"]).to eq(project2.id) - end - end - - describe "GET /projects/owned" do - let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)} - let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)} - let!(:project1) { gl_project1.ensure_gitlab_ci_project } - let!(:project2) { gl_project2.ensure_gitlab_ci_project } - - before do - project1.gl_project.team << [user, :developer] - project2.gl_project.team << [user, :developer] - end - - it "should return all projects on the CI instance" do - get ci_api("/projects/owned"), options - - expect(response.status).to eq(200) - expect(json_response.count).to eq(2) - end - end - end - - describe "GET /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - - before do - project.gl_project.team << [user, :developer] - end - - context "with an existing project" do - it "should retrieve the project info" do - get ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect(json_response['id']).to eq(project.id) - end - end - - context "with a non-existing project" do - it "should return 404 error if project not found" do - get ci_api("/projects/non_existent_id"), options - expect(response.status).to eq(404) - end - end - end - - describe "PUT /projects/:id" do - let!(:project) { FactoryGirl.create(:ci_project) } - let!(:project_info) { { default_ref: "develop" } } - - before do - options.merge!(project_info) - end - - it "should update a specific project's information" do - project.gl_project.team << [user, :master] - put ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(200) - expect(json_response["default_ref"]).to eq(project_info[:default_ref]) - end - - it "fails to update a non-existing project" do - put ci_api("/projects/non-existant-id"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - put ci_api("/projects/#{project.id}"), options - expect(response.status).to eq(401) - end - end - - describe "POST /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } - - it "should add the project to the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(201) - - project.reload - expect(project.runners.first.id).to eq(runner.id) - end - - it "should fail if it tries to link a non-existing project or runner" do - post ci_api("/projects/#{project.id}/runners/non-existing"), options - expect(response.status).to eq(404) - - post ci_api("/projects/non-existing/runners/#{runner.id}"), options - expect(response.status).to eq(404) - end - - it "non-manager is not authorized" do - allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false) - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end - end - - describe "DELETE /projects/:id/runners/:id" do - let(:project) { FactoryGirl.create(:ci_project) } - let(:runner) { FactoryGirl.create(:ci_runner) } - - it "should remove the project from the runner" do - project.gl_project.team << [user, :master] - post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - - expect(project.runners).to be_present - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(200) - - project.reload - expect(project.runners).to be_empty - end - - it "non-manager is not authorized" do - delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options - expect(response.status).to eq(401) - end - end -end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 11dc089e1f5..ef2f759fdaa 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -8,29 +8,6 @@ describe Ci::API::API do stub_gitlab_calls end - describe "GET /runners" do - let(:gitlab_url) { GitlabCi.config.gitlab_ci.url } - let(:private_token) { create(:user).private_token } - let(:options) do - { - private_token: private_token, - url: gitlab_url - } - end - - before do - 5.times { FactoryGirl.create(:ci_runner) } - end - - it "should retrieve a list of all runners" do - get ci_api("/runners", nil), options - expect(response.status).to eq(200) - expect(json_response.count).to eq(5) - expect(json_response.last).to have_key("id") - expect(json_response.last).to have_key("token") - end - end - describe "POST /runners/register" do describe "should create a runner if token provided" do before { post ci_api("/runners/register"), token: GitlabCi::REGISTRATION_TOKEN } @@ -53,11 +30,11 @@ describe Ci::API::API do end describe "should create a runner if project token provided" do - let(:project) { FactoryGirl.create(:ci_project) } + let(:project) { FactoryGirl.create(:empty_project) } before { post ci_api("/runners/register"), token: project.token } it { expect(response.status).to eq(201) } - it { expect(project.runners.size).to eq(1) } + it { expect(project.ci_runners.size).to eq(1) } end it "should return 403 error if token is invalid" do diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index a2b436d5811..cd33a96e1a6 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,15 +5,16 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } - let!(:gl_project) { FactoryGirl.create(:project) } - let!(:project) { gl_project.ensure_gitlab_ci_project } - let!(:project2) { FactoryGirl.create(:ci_project) } + let!(:project) { FactoryGirl.create(:project) } + let!(:project2) { FactoryGirl.create(:empty_project) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do { token: trigger_token } end + let(:project_ci_id) { create_ci_id(project) } + let(:project2_ci_id) { create_ci_id(project2) } before do stub_ci_commit_to_return_yaml_file @@ -21,7 +22,7 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do - post ci_api("/projects/#{project.id}/refs/master/trigger") + post ci_api("/projects/#{project_ci_id}/refs/master/trigger") expect(response.status).to eq(400) end @@ -31,23 +32,23 @@ describe Ci::API::API do end it 'should return unauthorized if token is for different project' do - post ci_api("/projects/#{project2.id}/refs/master/trigger"), options + post ci_api("/projects/#{project2_ci_id}/refs/master/trigger"), options expect(response.status).to eq(401) end end context 'Have a commit' do - let(:commit) { project.commits.last } + let(:commit) { project.ci_commits.last } it 'should create builds' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options + post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do - post ci_api("/projects/#{project.id}/refs/other-branch/trigger"), options + post ci_api("/projects/#{project_ci_id}/refs/other-branch/trigger"), options expect(response.status).to eq(400) expect(json_response['message']).to eq('No builds created') end @@ -58,19 +59,19 @@ describe Ci::API::API do end it 'should validate variables to be a hash' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: 'value') + post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: 'value') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) + post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do - post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables) + post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: variables) expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.first.trigger_request.variables).to eq(variables) @@ -78,4 +79,8 @@ describe Ci::API::API do end end end + + def create_ci_id(project) + ActiveRecord::Base.connection.insert("INSERT INTO ci_projects (gitlab_id) VALUES(#{project.id})") + end end diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb index c2fafca2ad2..7e2dcc503e5 100644 --- a/spec/services/ci/create_commit_service_spec.rb +++ b/spec/services/ci/create_commit_service_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' module Ci describe CreateCommitService, services: true do let(:service) { CreateCommitService.new } - let(:project) { FactoryGirl.create(:ci_project) } + let(:project) { FactoryGirl.create(:empty_project) } let(:user) { nil } before do @@ -24,7 +24,7 @@ module Ci it { expect(commit).to be_kind_of(Commit) } it { expect(commit).to be_valid } it { expect(commit).to be_persisted } - it { expect(commit).to eq(project.commits.last) } + it { expect(commit).to eq(project.ci_commits.last) } it { expect(commit.builds.first).to be_kind_of(Build) } end diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb index c80cb58163a..dbdc5370bd8 100644 --- a/spec/services/ci/create_trigger_request_service_spec.rb +++ b/spec/services/ci/create_trigger_request_service_spec.rb @@ -2,8 +2,7 @@ require 'spec_helper' describe Ci::CreateTriggerRequestService, services: true do let(:service) { Ci::CreateTriggerRequestService.new } - let(:gl_project) { create(:project) } - let(:project) { gl_project.ensure_gitlab_ci_project } + let(:project) { create(:project) } let(:trigger) { create(:ci_trigger, project: project) } before do @@ -29,7 +28,7 @@ describe Ci::CreateTriggerRequestService, services: true do before do stub_ci_commit_yaml_file('{}') - FactoryGirl.create :ci_commit, gl_project: gl_project + FactoryGirl.create :ci_commit, project: project end it { expect(subject).to be_nil } diff --git a/spec/services/ci/event_service_spec.rb b/spec/services/ci/event_service_spec.rb deleted file mode 100644 index 32516c75cf3..00000000000 --- a/spec/services/ci/event_service_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'spec_helper' - -describe Ci::EventService, services: true do - let(:project) { FactoryGirl.create :ci_project } - let(:user) { double(username: "root", id: 1) } - - before do - Event.destroy_all - end - - describe :remove_project do - it "creates event" do - Ci::EventService.new.remove_project(user, project) - - expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been removed by root") - end - end - - describe :create_project do - it "creates event" do - Ci::EventService.new.create_project(user, project) - - expect(Ci::Event.admin.last.description).to eq("Project \"#{project.name_with_namespace}\" has been created by root") - end - end - - describe :change_project_settings do - it "creates event" do - Ci::EventService.new.change_project_settings(user, project) - - expect(Ci::Event.last.description).to eq("User \"root\" updated projects settings") - end - end -end diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index b43cabb4ee4..870861ad20a 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -3,16 +3,16 @@ require 'spec_helper' module Ci describe ImageForBuildService, services: true do let(:service) { ImageForBuildService.new } - let(:project) { FactoryGirl.create(:ci_project) } - let(:gl_project) { FactoryGirl.create(:project, gitlab_ci_project: project) } - let(:commit_sha) { gl_project.commit('master').sha } - let(:commit) { gl_project.ensure_ci_commit(commit_sha) } + let(:project) { FactoryGirl.create(:empty_project) } + let(:commit_sha) { '01234567890123456789' } + let(:commit) { project.ensure_ci_commit(commit_sha) } let(:build) { FactoryGirl.create(:ci_build, commit: commit) } describe :execute do before { build } context 'branch name' do + before { allow(project).to receive(:commit).and_return(OpenStruct.new(sha: commit_sha)) } before { build.run! } let(:image) { service.execute(project, ref: 'master') } diff --git a/spec/services/ci/register_build_service_spec.rb b/spec/services/ci/register_build_service_spec.rb index 379e07982fb..e81f9e757ac 100644 --- a/spec/services/ci/register_build_service_spec.rb +++ b/spec/services/ci/register_build_service_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' module Ci describe RegisterBuildService, services: true do let!(:service) { RegisterBuildService.new } - let!(:gl_project) { FactoryGirl.create :empty_project } - let!(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project } + let!(:project) { FactoryGirl.create :empty_project, shared_runners_enabled: false } + let!(:commit) { FactoryGirl.create :ci_commit, project: project } let!(:pending_build) { FactoryGirl.create :ci_build, commit: commit } let!(:shared_runner) { FactoryGirl.create(:ci_runner, is_shared: true) } let!(:specific_runner) { FactoryGirl.create(:ci_runner, is_shared: false) } before do - specific_runner.assign_to(gl_project.ensure_gitlab_ci_project) + specific_runner.assign_to(project) end describe :execute do @@ -47,7 +47,7 @@ module Ci context 'allow shared runners' do before do - gl_project.gitlab_ci_project.update(shared_runners_enabled: true) + project.update(shared_runners_enabled: true) end context 'shared runner' do @@ -71,7 +71,7 @@ module Ci context 'disallow shared runners' do before do - gl_project.gitlab_ci_project.update(shared_runners_enabled: false) + project.update(shared_runners_enabled: false) end context 'shared runner' do -- cgit v1.2.1 From 8cdd54cc0696b76daa2baf463d02d944b50bac6a Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 17:29:44 +0100 Subject: Add runners token --- app/controllers/ci/application_controller.rb | 6 ------ app/controllers/ci/projects_controller.rb | 3 +-- app/models/ci/build.rb | 2 +- app/models/project.rb | 16 +++++++++------- db/migrate/20151203162135_add_ci_to_project.rb | 3 ++- db/migrate/20151204110613_migrate_ci_to_project.rb | 7 ++++--- db/migrate/20151204110832_add_index_to_ci_tables.rb | 3 ++- lib/ci/api/helpers.rb | 4 ---- lib/ci/api/runners.rb | 2 +- lib/ci/api/triggers.rb | 2 +- lib/gitlab/backend/grack_auth.rb | 2 +- spec/models/project_spec.rb | 2 +- 12 files changed, 23 insertions(+), 29 deletions(-) diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb index bc7f48b3c87..c420b59c3a2 100644 --- a/app/controllers/ci/application_controller.rb +++ b/app/controllers/ci/application_controller.rb @@ -6,12 +6,6 @@ module Ci private - def authenticate_token! - unless project.valid_token?(params[:token]) - return head(403) - end - end - def authorize_access_project! unless can?(current_user, :read_project, project) return page_404 diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 7e62320bf21..3004c2d27f0 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -22,8 +22,7 @@ module Ci protected def project - # TODO: what to do here? - @project ||= Project.find_by_ci_id(params[:id]) + @project ||= Project.find_by(ci_id: params[:id].to_i) end def no_cache diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 43ed8eb518b..fac1d1c4c2c 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -225,7 +225,7 @@ module Ci end def valid_token? token - project.valid_token? token + project.valid_runners_token? token end def target_url diff --git a/app/models/project.rb b/app/models/project.rb index e3eee36c253..a11bc9c4bd5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -169,9 +169,9 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } - before_validation :set_random_token - def set_random_token - self.token = SecureRandom.hex(15) if self.token.blank? + before_validation :set_runners_token_token + def set_runners_token_token + self.runners_token = SecureRandom.hex(15) if self.runners_token.blank? end mount_uploader :avatar, AvatarUploader @@ -270,9 +270,7 @@ class Project < ActiveRecord::Base end def find_by_ci_id(id) - ci_projects = Arel::Table.new(:ci_projects) - gitlab_id = ci_projects.where(ci_projects[:id].eq(id)).project(ci_projects[:gitlab_id]) - find_by("id=(#{gitlab_id.to_sql})") + find_by(ci_id: id.to_i) end def visibility_levels @@ -831,7 +829,11 @@ class Project < ActiveRecord::Base shared_runners_enabled? && Ci::Runner.shared.active.any?(&block) end - def valid_token? token + def valid_runners_token? token + self.token && self.token == token + end + + def valid_build_token? token self.token && self.token == token end diff --git a/db/migrate/20151203162135_add_ci_to_project.rb b/db/migrate/20151203162135_add_ci_to_project.rb index e95942666c3..8a65abab636 100644 --- a/db/migrate/20151203162135_add_ci_to_project.rb +++ b/db/migrate/20151203162135_add_ci_to_project.rb @@ -1,8 +1,9 @@ class AddCiToProject < ActiveRecord::Migration def up + add_column :projects, :ci_id, :integer add_column :projects, :builds_enabled, :boolean, default: true, null: false add_column :projects, :shared_runners_enabled, :boolean, default: true, null: false - add_column :projects, :token, :string + add_column :projects, :runners_token, :string add_column :projects, :build_coverage_regex, :string add_column :projects, :build_allow_git_fetch, :boolean, default: true, null: false add_column :projects, :build_timeout, :integer, default: 3600, null: false diff --git a/db/migrate/20151204110613_migrate_ci_to_project.rb b/db/migrate/20151204110613_migrate_ci_to_project.rb index 1777b6170b4..d17b2a425f8 100644 --- a/db/migrate/20151204110613_migrate_ci_to_project.rb +++ b/db/migrate/20151204110613_migrate_ci_to_project.rb @@ -5,8 +5,9 @@ class MigrateCiToProject < ActiveRecord::Migration migrate_project_id_for_table('ci_variables') migrate_project_id_for_builds - migrate_project_column('shared_runners_enabled') - migrate_project_column('token') + migrate_project_column('id', 'ci_id') + migrate_project_column('shared_runners_enabled', 'shared_runners_enabled') + migrate_project_column('token', 'runners_token') migrate_project_column('coverage_regex', 'build_coverage_regex') migrate_project_column('allow_git_fetch', 'build_allow_git_fetch') migrate_project_column('timeout', 'build_timeout') @@ -25,7 +26,7 @@ class MigrateCiToProject < ActiveRecord::Migration def migrate_project_column(column, new_column = nil) new_column ||= column - subquery = "SELECT #{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" + subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") end diff --git a/db/migrate/20151204110832_add_index_to_ci_tables.rb b/db/migrate/20151204110832_add_index_to_ci_tables.rb index b95931334c6..9fedb5d612c 100644 --- a/db/migrate/20151204110832_add_index_to_ci_tables.rb +++ b/db/migrate/20151204110832_add_index_to_ci_tables.rb @@ -4,8 +4,9 @@ class AddIndexToCiTables < ActiveRecord::Migration add_index :ci_runner_projects, :gl_project_id add_index :ci_triggers, :gl_project_id add_index :ci_variables, :gl_project_id - add_index :projects, :token + add_index :projects, :runners_token add_index :projects, :builds_enabled add_index :projects, [:builds_enabled, :shared_runners_enabled] + add_index :projects, [:ci_id] end end diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb index 02502333756..9891b5e38ea 100644 --- a/lib/ci/api/helpers.rb +++ b/lib/ci/api/helpers.rb @@ -13,10 +13,6 @@ module Ci forbidden! unless current_runner end - def authenticate_project_token!(project) - forbidden! unless project.valid_token?(params[:project_token]) - end - def authenticate_build_token!(build) token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s forbidden! unless token && build.valid_token?(token) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index dd77bd65863..1e738a73157 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -36,7 +36,7 @@ module Ci tag_list: params[:tag_list], is_shared: true ) - elsif project = Project.find_by(token: params[:token]) + elsif project = Project.find_by(runners_token: params[:token]) # Create a specific runner for project. project.ci_runners.create( description: params[:description], diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb index 6d2cdd8c682..63b42113513 100644 --- a/lib/ci/api/triggers.rb +++ b/lib/ci/api/triggers.rb @@ -14,7 +14,7 @@ module Ci post ":id/refs/:ref/trigger" do required_attributes! [:token] - project = Project.find_by_ci_id(params[:id]) + project = Project.find_by(ci_id: params[:id].to_i) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger unauthorized! unless trigger.project == project diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index 5a032b572ae..d854c1c8683 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -78,7 +78,7 @@ module Grack underscored_service = matched_login['s'].underscore if underscored_service == 'gitlab_ci' - return project && project.builds_enabled? && project.valid_token?(password) + return project && project.builds_enabled? && project.valid_build_token?(password) elsif Service.available_services_names.include?(underscored_service) service_method = "#{underscored_service}_service" service = project.send(service_method) diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index ee106b11fbb..9c9266455cf 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -55,7 +55,7 @@ describe Project, models: true do it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } it { is_expected.to have_many(:ci_commits) } - it { is_expected.to have_many(:ci_statuses) } + it { is_expected.to have_many(:commit_statuses) } it { is_expected.to have_many(:ci_builds) } it { is_expected.to have_many(:ci_runner_projects) } it { is_expected.to have_many(:ci_runners) } -- cgit v1.2.1 From 64bfd9d71a4017e0b5336a2c1565926f4b8beedd Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 17:44:06 +0100 Subject: Remove ci_ prefix from all ci related things --- .../admin/runner_projects_controller.rb | 4 +- app/controllers/projects/builds_controller.rb | 6 +- .../projects/runner_projects_controller.rb | 2 +- app/controllers/projects/runners_controller.rb | 6 +- app/controllers/projects/triggers_controller.rb | 8 +- app/controllers/projects/variables_controller.rb | 2 +- app/helpers/ci_status_helper.rb | 2 +- app/models/ci/build.rb | 2 +- app/models/ci/runner.rb | 2 +- app/models/project.rb | 16 +- app/models/user.rb | 2 +- app/services/ci/create_commit_service.rb | 28 ---- app/services/create_commit_builds_service.rb | 29 ++++ app/services/git_push_service.rb | 1 + app/services/git_tag_push_service.rb | 1 + app/views/admin/runners/show.html.haml | 2 +- app/views/layouts/nav/_project.html.haml | 2 +- app/views/projects/graphs/ci/_overall.haml | 8 +- app/views/projects/runners/_runner.html.haml | 4 +- app/views/projects/variables/show.html.haml | 4 +- lib/ci/api/runners.rb | 2 +- lib/ci/charts.rb | 4 +- spec/features/builds_spec.rb | 2 +- spec/features/runners_spec.rb | 10 +- spec/features/triggers_spec.rb | 6 +- spec/features/variables_spec.rb | 2 +- spec/models/build_spec.rb | 8 +- spec/models/ci/runner_spec.rb | 6 +- spec/models/project_spec.rb | 16 +- spec/requests/ci/api/builds_spec.rb | 4 +- spec/requests/ci/api/runners_spec.rb | 2 +- spec/services/ci/create_commit_service_spec.rb | 172 --------------------- spec/services/create_commit_builds_service_spec.rb | 171 ++++++++++++++++++++ 33 files changed, 269 insertions(+), 267 deletions(-) delete mode 100644 app/services/ci/create_commit_service.rb create mode 100644 app/services/create_commit_builds_service.rb delete mode 100644 spec/services/ci/create_commit_service_spec.rb create mode 100644 spec/services/create_commit_builds_service_spec.rb diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 20d621742f9..d25619d94e0 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -2,8 +2,8 @@ class Admin::RunnerProjectsController < Admin::ApplicationController before_action :project, only: [:create] def index - @runner_projects = project.ci_runner_projects.all - @runner_project = project.ci_runner_projects.new + @runner_projects = project.runner_projects.all + @runner_project = project.runner_projects.new end def create diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index e7e2ab43130..26ba12520c7 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -8,7 +8,7 @@ class Projects::BuildsController < Projects::ApplicationController def index @scope = params[:scope] - @all_builds = project.ci_builds + @all_builds = project.builds @builds = @all_builds.order('created_at DESC') @builds = case @scope @@ -23,7 +23,7 @@ class Projects::BuildsController < Projects::ApplicationController end def cancel_all - @project.ci_builds.running_or_pending.each(&:cancel) + @project.builds.running_or_pending.each(&:cancel) redirect_to namespace_project_builds_path(project.namespace, project) end @@ -76,7 +76,7 @@ class Projects::BuildsController < Projects::ApplicationController private def build - @build ||= project.ci_builds.unscoped.find_by!(id: params[:id]) + @build ||= project.builds.unscoped.find_by!(id: params[:id]) end def artifacts_file diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index 69863387354..e2785caa2fb 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -18,7 +18,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController end def destroy - runner_project = project.ci_runner_projects.find(params[:id]) + runner_project = project.runner_projects.find(params[:id]) runner_project.destroy redirect_to runners_path(project) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 863c5d131ab..4993b2648a5 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -5,9 +5,9 @@ class Projects::RunnersController < Projects::ApplicationController layout 'project_settings' def index - @runners = project.ci_runners.ordered + @runners = project.runners.ordered @specific_runners = current_user.ci_authorized_runners. - where.not(id: project.ci_runners). + where.not(id: project.runners). ordered.page(params[:page]).per(20) @shared_runners = Ci::Runner.shared.active @shared_runners_count = @shared_runners.count(:all) @@ -60,7 +60,7 @@ class Projects::RunnersController < Projects::ApplicationController protected def set_runner - @runner ||= project.ci_runners.find(params[:id]) + @runner ||= project.runners.find(params[:id]) end def runner_params diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 421e648a2dd..30adfad1daa 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -4,18 +4,18 @@ class Projects::TriggersController < Projects::ApplicationController layout 'project_settings' def index - @triggers = project.ci_triggers + @triggers = project.triggers @trigger = Ci::Trigger.new end def create - @trigger = project.ci_triggers.new + @trigger = project.triggers.new @trigger.save if @trigger.valid? redirect_to namespace_project_triggers_path(@project.namespace, @project) else - @triggers = project.ci_triggers.select(&:persisted?) + @triggers = project.triggers.select(&:persisted?) render :index end end @@ -29,6 +29,6 @@ class Projects::TriggersController < Projects::ApplicationController private def trigger - @trigger ||= project.ci_triggers.find(params[:id]) + @trigger ||= project.triggers.find(params[:id]) end end diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 1dab978f462..10efafea9db 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -17,6 +17,6 @@ class Projects::VariablesController < Projects::ApplicationController private def project_params - params.require(:project).permit({ ci_variables_attributes: [:id, :key, :value, :_destroy] }) + params.require(:project).permit({ variables_attributes: [:id, :key, :value, :_destroy] }) end end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index 7feeaa17306..02ae8177df8 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -65,7 +65,7 @@ module CiStatusHelper end def no_runners_for_project?(project) - project.ci_runners.blank? && + project.runners.blank? && Ci::Runner.shared.blank? end end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index fac1d1c4c2c..401e08115dc 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -287,7 +287,7 @@ module Ci end def project_variables - project.ci_variables.map do |variable| + project.variables.map do |variable| { key: variable.key, value: variable.value, public: false } end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index aa445db7ebf..38b20cd7faa 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -52,7 +52,7 @@ module Ci def assign_to(project, current_user = nil) self.is_shared = false if shared? self.save - project.ci_runner_projects.create!(runner_id: self.id) + project.runner_projects.create!(runner_id: self.id) end def display_name diff --git a/app/models/project.rb b/app/models/project.rb index a11bc9c4bd5..ba04470c64e 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -128,15 +128,15 @@ class Project < ActiveRecord::Base has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" + has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id - has_many :ci_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id - has_many :ci_builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the ci_statuses - has_many :ci_runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id - has_many :ci_runners, through: :ci_runner_projects, source: :runner, class_name: 'Ci::Runner' - has_many :ci_variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id - has_many :ci_triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id + has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses + has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id + has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' + has_many :variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id - accepts_nested_attributes_for :ci_variables, allow_destroy: true + accepts_nested_attributes_for :variables, allow_destroy: true delegate :name, to: :owner, allow_nil: true, prefix: true delegate :members, to: :team, prefix: true @@ -822,7 +822,7 @@ class Project < ActiveRecord::Base end def any_runners?(&block) - if ci_runners.active.any?(&block) + if runners.active.any?(&block) return true end diff --git a/app/models/user.rb b/app/models/user.rb index da06b6f3ade..87d46a49430 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -134,7 +134,7 @@ class User < ActiveRecord::Base has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest" has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy has_one :abuse_report, dependent: :destroy - has_many :ci_builds, dependent: :nullify, class_name: 'Ci::Build' + has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb deleted file mode 100644 index 6401ce3619e..00000000000 --- a/app/services/ci/create_commit_service.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Ci - class CreateCommitService - def execute(project, user, params) - sha = params[:checkout_sha] || params[:after] - origin_ref = params[:ref] - - unless origin_ref && sha.present? - return false - end - - ref = origin_ref.gsub(/\Arefs\/(tags|heads)\//, '') - - # Skip branch removal - if sha == Ci::Git::BLANK_SHA - return false - end - - tag = origin_ref.start_with?('refs/tags/') - commit = project.ensure_ci_commit(sha) - unless commit.skip_ci? - commit.update_committed! - commit.create_builds(ref, tag, user) - end - - commit - end - end -end diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb new file mode 100644 index 00000000000..886bd88d204 --- /dev/null +++ b/app/services/create_commit_builds_service.rb @@ -0,0 +1,29 @@ +class CreateCommitBuildsService + def execute(project, user, params) + return false unless project.builds_enabled? + + sha = params[:checkout_sha] || params[:after] + origin_ref = params[:ref] + + unless origin_ref && sha.present? + return false + end + + ref = origin_ref.gsub(/\Arefs\/(tags|heads)\//, '') + + # Skip branch removal + if sha == Ci::Git::BLANK_SHA + return false + end + + tag = origin_ref.start_with?('refs/tags/') + commit = project.ensure_ci_commit(sha) + unless commit.skip_ci? + commit.update_committed! + commit.create_builds(ref, tag, user) + end + + commit + end +end + diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index f11690aa3f4..d7ea30bc315 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -61,6 +61,7 @@ class GitPushService EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) + CreateCommitBuildsService.new.execute(project, @user, @push_data) ProjectCacheWorker.perform_async(project.id) end diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 1cc42b0b0ad..4144c7111d0 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -10,6 +10,7 @@ class GitTagPushService EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) + CreateCommitBuildsService.new.execute(project, @user, @push_data) ProjectCacheWorker.perform_async(project.id) true diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index aeb484a7971..cd0cc2d1776 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -82,7 +82,7 @@ = project.name_with_namespace %td .pull-right - = form_for [:admin, project.namespace, project, project.ci_runner_projects.new] do |f| + = form_for [:admin, project.namespace, project, project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: @runner.id = f.submit 'Enable', class: 'btn btn-xs' = paginate @projects diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 2fcba7bd672..c0d62028639 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -44,7 +44,7 @@ = icon('cubes fw') %span Builds - %span.count.builds_counter= @project.ci_builds.running_or_pending.count(:all) + %span.count.builds_counter= @project.builds.running_or_pending.count(:all) - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml index 4105d683117..4b12e5f2da1 100644 --- a/app/views/projects/graphs/ci/_overall.haml +++ b/app/views/projects/graphs/ci/_overall.haml @@ -2,17 +2,17 @@ %ul %li Total: - %strong= pluralize @project.ci_builds.count(:all), 'build' + %strong= pluralize @project.builds.count(:all), 'build' %li Successful: - %strong= pluralize @project.ci_builds.success.count(:all), 'build' + %strong= pluralize @project.builds.success.count(:all), 'build' %li Failed: - %strong= pluralize @project.ci_builds.failed.count(:all), 'build' + %strong= pluralize @project.builds.failed.count(:all), 'build' %li Success ratio: %strong - #{success_ratio(@project.ci_builds.success, @project.ci_builds.failed)}% + #{success_ratio(@project.builds.success, @project.builds.failed)}% %li Commits covered: %strong diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 1a61cb07b5f..4d95afc28bb 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -15,10 +15,10 @@ - if runner.belongs_to_one_project? = link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - else - - runner_project = @project.ci_runner_projects.find_by(runner_id: runner) + - runner_project = @project.runner_projects.find_by(runner_id: runner) = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - elsif runner.specific? - = form_for [@project.namespace, @project, @project.ci_runner_projects.new] do |f| + = form_for [@project.namespace, @project, @project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: runner.id = f.submit 'Enable for this project', class: 'btn btn-sm' .pull-right diff --git a/app/views/projects/variables/show.html.haml b/app/views/projects/variables/show.html.haml index e7bc866d6bc..e80dffc1ced 100644 --- a/app/views/projects/variables/show.html.haml +++ b/app/views/projects/variables/show.html.haml @@ -19,7 +19,7 @@ - @project.errors.full_messages.each do |msg| %li= msg - = f.fields_for :ci_variables do |variable_form| + = f.fields_for :variables do |variable_form| .form-group = variable_form.label :key, 'Key', class: 'control-label' .col-sm-10 @@ -34,7 +34,7 @@ %hr %p .clearfix - = f.link_to_add "Add a variable", :ci_variables, class: 'btn btn-success pull-right' + = f.link_to_add "Add a variable", :variables, class: 'btn btn-success pull-right' .form-actions = f.submit 'Save changes', class: 'btn btn-save', return_to: request.original_url diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 1e738a73157..8704917b9e4 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -38,7 +38,7 @@ module Ci ) elsif project = Project.find_by(runners_token: params[:token]) # Create a specific runner for project. - project.ci_runners.create( + project.runners.create( description: params[:description], tag_list: params[:tag_list] ) diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb index 912ccff5f98..d53bdcbd0f2 100644 --- a/lib/ci/charts.rb +++ b/lib/ci/charts.rb @@ -16,10 +16,10 @@ module Ci def push(from, to, format) @labels << from.strftime(format) - @total << project.ci_builds. + @total << project.builds. where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). count(:all) - @success << project.ci_builds. + @success << project.builds. where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", to, from). success.count(:all) end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 61d81cb0c8e..f0031a0a247 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -37,7 +37,7 @@ describe "Builds" do context "All builds" do before do - @project.ci_builds.running_or_pending.each(&:success) + @project.builds.running_or_pending.each(&:success) visit namespace_project_builds_path(@project.namespace, @project, scope: :all) end diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index a4562f2fae8..d97831aae14 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -21,9 +21,9 @@ describe "Runners" do @specific_runner = FactoryGirl.create :ci_specific_runner @specific_runner2 = FactoryGirl.create :ci_specific_runner @specific_runner3 = FactoryGirl.create :ci_specific_runner - @project.ci_runners << @specific_runner - @project2.ci_runners << @specific_runner2 - @project3.ci_runners << @specific_runner3 + @project.runners << @specific_runner + @project2.runners << @specific_runner2 + @project3.runners << @specific_runner3 visit runners_path(@project) end @@ -48,7 +48,7 @@ describe "Runners" do end it "disables specific runner for project" do - @project2.ci_runners << @specific_runner + @project2.runners << @specific_runner visit runners_path(@project) within ".activated-specific-runners" do @@ -85,7 +85,7 @@ describe "Runners" do @project = FactoryGirl.create :empty_project @project.team << [user, :master] @specific_runner = FactoryGirl.create :ci_specific_runner - @project.ci_runners << @specific_runner + @project.runners << @specific_runner end it "shows runner information" do diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb index b0705a45aee..3cbc8253ad6 100644 --- a/spec/features/triggers_spec.rb +++ b/spec/features/triggers_spec.rb @@ -13,16 +13,16 @@ describe 'Triggers' do context 'create a trigger' do before do click_on 'Add Trigger' - expect(@project.ci_triggers.count).to eq(1) + expect(@project.triggers.count).to eq(1) end it 'contains trigger token' do - expect(page).to have_content(@project.ci_triggers.first.token) + expect(page).to have_content(@project.triggers.first.token) end it 'revokes the trigger' do click_on 'Revoke' - expect(@project.ci_triggers.count).to eq(0) + expect(@project.triggers.count).to eq(0) end end end diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb index a6e68eeefbc..afea1840cd7 100644 --- a/spec/features/variables_spec.rb +++ b/spec/features/variables_spec.rb @@ -18,7 +18,7 @@ describe "Variables" do click_on "Save changes" expect(page).to have_content("Variables were successfully updated.") - expect(@project.ci_variables.count).to eq(1) + expect(@project.variables.count).to eq(1) end end end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 70a63c05b22..91af694fc03 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -232,7 +232,7 @@ describe Ci::Build, models: true do end before do - build.project.ci_variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') + build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value') end it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) } @@ -264,7 +264,7 @@ describe Ci::Build, models: true do describe :can_be_served? do let(:runner) { FactoryGirl.create :ci_specific_runner } - before { build.project.ci_runners << runner } + before { build.project.runners << runner } context 'runner without tags' do it 'can handle builds without tags' do @@ -307,7 +307,7 @@ describe Ci::Build, models: true do let(:runner) { FactoryGirl.create :ci_specific_runner } before do - build.project.ci_runners << runner + build.project.runners << runner runner.update_attributes(contacted_at: 1.second.ago) end @@ -344,7 +344,7 @@ describe Ci::Build, models: true do let(:runner) { FactoryGirl.create :ci_specific_runner, contacted_at: 1.second.ago } before do - build.project.ci_runners << runner + build.project.runners << runner runner.save end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 33388f97826..232760dfeba 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -118,8 +118,8 @@ describe Ci::Runner, models: true do runner = FactoryGirl.create(:ci_specific_runner) project = FactoryGirl.create(:empty_project) project1 = FactoryGirl.create(:empty_project) - project.ci_runners << runner - project1.ci_runners << runner + project.runners << runner + project1.runners << runner expect(runner.belongs_to_one_project?).to be_falsey end @@ -127,7 +127,7 @@ describe Ci::Runner, models: true do it "returns true" do runner = FactoryGirl.create(:ci_specific_runner) project = FactoryGirl.create(:empty_project) - project.ci_runners << runner + project.runners << runner expect(runner.belongs_to_one_project?).to be_truthy end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9c9266455cf..b962f66ba26 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -54,13 +54,13 @@ describe Project, models: true do it { is_expected.to have_one(:slack_service).dependent(:destroy) } it { is_expected.to have_one(:pushover_service).dependent(:destroy) } it { is_expected.to have_one(:asana_service).dependent(:destroy) } - it { is_expected.to have_many(:ci_commits) } it { is_expected.to have_many(:commit_statuses) } - it { is_expected.to have_many(:ci_builds) } - it { is_expected.to have_many(:ci_runner_projects) } - it { is_expected.to have_many(:ci_runners) } - it { is_expected.to have_many(:ci_variables) } - it { is_expected.to have_many(:ci_triggers) } + it { is_expected.to have_many(:ci_commits) } + it { is_expected.to have_many(:builds) } + it { is_expected.to have_many(:runner_projects) } + it { is_expected.to have_many(:runners) } + it { is_expected.to have_many(:variables) } + it { is_expected.to have_many(:triggers) } end describe 'modules' do @@ -519,7 +519,7 @@ describe Project, models: true do end it 'there is a specific runner' do - project.ci_runners << specific_runner + project.runners << specific_runner expect(project.any_runners?).to be_truthy end @@ -529,7 +529,7 @@ describe Project, models: true do end it 'checks the presence of specific runner' do - project.ci_runners << specific_runner + project.runners << specific_runner expect(project.any_runners? { |runner| runner == specific_runner }).to be_truthy end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 0aa9981ea8d..550d74d158a 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -68,7 +68,7 @@ describe Ci::API::API do it "returns variables" do commit = FactoryGirl.create(:ci_commit, project: project) commit.create_builds('master', false, nil) - project.ci_variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") + project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -87,7 +87,7 @@ describe Ci::API::API do trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) commit.create_builds('master', false, nil, trigger_request) - project.ci_variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") + project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index ef2f759fdaa..944d910d055 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -34,7 +34,7 @@ describe Ci::API::API do before { post ci_api("/runners/register"), token: project.token } it { expect(response.status).to eq(201) } - it { expect(project.ci_runners.size).to eq(1) } + it { expect(project.runners.size).to eq(1) } end it "should return 403 error if token is invalid" do diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb deleted file mode 100644 index 7e2dcc503e5..00000000000 --- a/spec/services/ci/create_commit_service_spec.rb +++ /dev/null @@ -1,172 +0,0 @@ -require 'spec_helper' - -module Ci - describe CreateCommitService, services: true do - let(:service) { CreateCommitService.new } - let(:project) { FactoryGirl.create(:empty_project) } - let(:user) { nil } - - before do - stub_ci_commit_to_return_yaml_file - end - - describe :execute do - context 'valid params' do - let(:commit) do - service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - end - - it { expect(commit).to be_kind_of(Commit) } - it { expect(commit).to be_valid } - it { expect(commit).to be_persisted } - it { expect(commit).to eq(project.ci_commits.last) } - it { expect(commit.builds.first).to be_kind_of(Build) } - end - - context "skip tag if there is no build for it" do - it "creates commit if there is appropriate job" do - result = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - expect(result).to be_persisted - end - - it "creates commit if there is no appropriate job but deploy job has right ref setting" do - config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) - stub_ci_commit_yaml_file(config) - - result = service.execute(project, user, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: "Message" } ] - ) - expect(result).to be_persisted - end - end - - it 'skips commits without .gitlab-ci.yml' do - stub_ci_commit_yaml_file(nil) - result = service.execute(project, user, - ref: 'refs/heads/0_1', - before: '00000000', - after: '31das312', - commits: [ { message: 'Message' } ] - ) - expect(result).to be_persisted - expect(result.builds.any?).to be_falsey - expect(result.status).to eq('skipped') - expect(result.yaml_errors).to be_nil - end - - it 'skips commits if yaml is invalid' do - message = 'message' - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } - stub_ci_commit_yaml_file('invalid: file: file') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq('failed') - expect(commit.yaml_errors).to_not be_nil - end - - describe :ci_skip? do - let(:message) { "some message[ci skip]" } - - before do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } - end - - it "skips builds creation if there is [ci skip] tag in commit message" do - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - end - - it "does not skips builds creation if there is no [ci skip] tag in commit message" do - allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" } - - commits = [{ message: "some message" }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - - expect(commit.builds.first.name).to eq("staging") - end - - it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file: fiile') - commits = [{ message: message }] - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.any?).to be false - expect(commit.status).to eq("skipped") - expect(commit.yaml_errors).to be_nil - end - end - - it "skips build creation if there are already builds" do - allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } - - commits = [{ message: "message" }] - commit = service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.count(:all)).to eq(2) - - commit = service.execute(project, user, - ref: 'refs/heads/master', - before: '00000000', - after: '31das312', - commits: commits - ) - expect(commit.builds.count(:all)).to eq(2) - end - - it "creates commit with failed status if yaml is invalid" do - stub_ci_commit_yaml_file('invalid: file') - - commits = [{ message: "some message" }] - - commit = service.execute(project, user, - ref: 'refs/tags/0_1', - before: '00000000', - after: '31das312', - commits: commits - ) - - expect(commit.status).to eq("failed") - expect(commit.builds.any?).to be false - end - end - end -end diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb new file mode 100644 index 00000000000..70e28f6c528 --- /dev/null +++ b/spec/services/create_commit_builds_service_spec.rb @@ -0,0 +1,171 @@ +require 'spec_helper' + +describe CreateCommitBuildsService, services: true do + let(:service) { CreateCommitBuildsService.new } + let(:project) { FactoryGirl.create(:empty_project) } + let(:user) { nil } + + before do + stub_ci_commit_to_return_yaml_file + end + + describe :execute do + context 'valid params' do + let(:commit) do + service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + end + + it { expect(commit).to be_kind_of(Commit) } + it { expect(commit).to be_valid } + it { expect(commit).to be_persisted } + it { expect(commit).to eq(project.ci_commits.last) } + it { expect(commit.builds.first).to be_kind_of(Build) } + end + + context "skip tag if there is no build for it" do + it "creates commit if there is appropriate job" do + result = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + expect(result).to be_persisted + end + + it "creates commit if there is no appropriate job but deploy job has right ref setting" do + config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } }) + stub_ci_commit_yaml_file(config) + + result = service.execute(project, user, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: "Message" }] + ) + expect(result).to be_persisted + end + end + + it 'skips commits without .gitlab-ci.yml' do + stub_ci_commit_yaml_file(nil) + result = service.execute(project, user, + ref: 'refs/heads/0_1', + before: '00000000', + after: '31das312', + commits: [{ message: 'Message' }] + ) + expect(result).to be_persisted + expect(result.builds.any?).to be_falsey + expect(result.status).to eq('skipped') + expect(result.yaml_errors).to be_nil + end + + it 'skips commits if yaml is invalid' do + message = 'message' + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + stub_ci_commit_yaml_file('invalid: file: file') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq('failed') + expect(commit.yaml_errors).to_not be_nil + end + + describe :ci_skip? do + let(:message) { "some message[ci skip]" } + + before do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message } + end + + it "skips builds creation if there is [ci skip] tag in commit message" do + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + end + + it "does not skips builds creation if there is no [ci skip] tag in commit message" do + allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" } + + commits = [{ message: "some message" }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(commit.builds.first.name).to eq("staging") + end + + it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do + stub_ci_commit_yaml_file('invalid: file: fiile') + commits = [{ message: message }] + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.any?).to be false + expect(commit.status).to eq("skipped") + expect(commit.yaml_errors).to be_nil + end + end + + it "skips build creation if there are already builds" do + allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml } + + commits = [{ message: "message" }] + commit = service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.count(:all)).to eq(2) + + commit = service.execute(project, user, + ref: 'refs/heads/master', + before: '00000000', + after: '31das312', + commits: commits + ) + expect(commit.builds.count(:all)).to eq(2) + end + + it "creates commit with failed status if yaml is invalid" do + stub_ci_commit_yaml_file('invalid: file') + + commits = [{ message: "some message" }] + + commit = service.execute(project, user, + ref: 'refs/tags/0_1', + before: '00000000', + after: '31das312', + commits: commits + ) + + expect(commit.status).to eq("failed") + expect(commit.builds.any?).to be false + end + end +end + -- cgit v1.2.1 From 3d9ce37a48615032c786913acdde1eedba351361 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 18:04:40 +0100 Subject: Reimplement Trigger API --- app/helpers/triggers_helper.rb | 4 +- app/views/projects/triggers/index.html.haml | 8 +-- lib/api/api.rb | 1 + lib/api/entities.rb | 4 ++ lib/api/triggers.rb | 48 +++++++++++++++++ spec/requests/api/triggers_spec.rb | 80 +++++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 lib/api/triggers.rb create mode 100644 spec/requests/api/triggers_spec.rb diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index 2a3a7e80fca..8cad994d10f 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -1,5 +1,5 @@ module TriggersHelper - def ci_build_trigger_url(project_id, ref_name) - "#{Settings.gitlab_ci.url}/ci/api/v1/projects/#{project_id}/refs/#{ref_name}/trigger" + def builds_trigger_url(project_id) + "#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds" end end diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index 147cda51d5a..fb3794764c5 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -36,7 +36,8 @@ :plain curl -X POST \ -F token=TOKEN \ - #{ci_build_trigger_url(@project.id, 'REF_NAME')} + -F ref=REF_NAME \ + #{builds_trigger_url(@project.id)} %h3 Use .gitlab-ci.yml @@ -51,7 +52,7 @@ trigger: type: deploy script: - - "curl -X POST -F token=TOKEN #{ci_build_trigger_url(@project.id, 'REF_NAME')}" + - "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}" %h3 Pass build variables @@ -65,5 +66,6 @@ :plain curl -X POST \ -F token=TOKEN \ + -F "ref=REF_NAME" \ -F "variables[RUN_NIGHTLY_BUILD]=true" \ - #{ci_build_trigger_url(@project.id, 'REF_NAME')} + #{builds_trigger_url(@project.id, 'TOKEN')} diff --git a/lib/api/api.rb b/lib/api/api.rb index fe1bf8a4816..7834262d612 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -53,5 +53,6 @@ module API mount Settings mount Keys mount Tags + mount Triggers end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 381babe291b..b1cd80bdf65 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -361,5 +361,9 @@ module API end end end + + class TriggerRequest < Grape::Entity + expose :id, :variables + end end end diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb new file mode 100644 index 00000000000..b713c00e82a --- /dev/null +++ b/lib/api/triggers.rb @@ -0,0 +1,48 @@ +module API + # Triggers API + class Triggers < Grape::API + resource :projects do + # Trigger a GitLab project build + # + # Parameters: + # id (required) - The ID of a CI project + # ref (required) - The name of project's branch or tag + # token (required) - The uniq token of trigger + # variables (optional) - The list of variables to be injected into build + # Example Request: + # POST /projects/:id/trigger/builds + post ":id/trigger/builds" do + required_attributes! [:ref, :token] + + project = Project.find_with_namespace(id) || Project.find_by(id: params[:id]) + trigger = Ci::Trigger.find_by_token(params[:token].to_s) + not_found! unless project && trigger + unauthorized! unless trigger.project == project + + # validate variables + variables = params[:variables] + if variables + unless variables.is_a?(Hash) + render_api_error!('variables needs to be a hash', 400) + end + + unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) } + render_api_error!('variables needs to be a map of key-valued strings', 400) + end + + # convert variables from Mash to Hash + variables = variables.to_h + end + + # create request and trigger builds + trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables) + if trigger_request + present trigger_request, with: Entities::TriggerRequest + else + errors = 'No builds created' + render_api_error!(errors, 400) + end + end + end + end +end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb new file mode 100644 index 00000000000..899458e619e --- /dev/null +++ b/spec/requests/api/triggers_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe API::API do + include ApiHelpers + + describe 'POST /projects/:project_id/trigger' do + let!(:trigger_token) { 'secure token' } + let!(:project) { FactoryGirl.create(:project) } + let!(:project2) { FactoryGirl.create(:empty_project) } + let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } + let(:options) do + { + token: trigger_token + } + end + + before do + stub_ci_commit_to_return_yaml_file + end + + context 'Handles errors' do + it 'should return bad request if token is missing' do + post api("/projects/#{project.id}/trigger/builds"), ref: 'master' + expect(response.status).to eq(400) + end + + it 'should return not found if project is not found' do + post api('/projects/0/refs/master/trigger/builds'), options.merge(ref: 'master') + expect(response.status).to eq(404) + end + + it 'should return unauthorized if token is for different project' do + post api("/projects/#{project2.id}/trigger/builds"), options.merge(ref: 'master') + expect(response.status).to eq(401) + end + end + + context 'Have a commit' do + let(:commit) { project.ci_commits.last } + + it 'should create builds' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'master') + expect(response.status).to eq(201) + commit.builds.reload + expect(commit.builds.size).to eq(2) + end + + it 'should return bad request with no builds created if there\'s no commit for that ref' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('No builds created') + end + + context 'Validates variables' do + let(:variables) do + { 'TRIGGER_KEY' => 'TRIGGER_VALUE' } + end + + it 'should validate variables to be a hash' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', refs: 'master') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a hash') + end + + it 'should validate variables needs to be a map of key-valued strings' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, refs: 'master') + expect(response.status).to eq(400) + expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') + end + + it 'create trigger request with variables' do + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, refs: 'master') + expect(response.status).to eq(201) + commit.builds.reload + expect(commit.builds.first.trigger_request.variables).to eq(variables) + end + end + end + end +end -- cgit v1.2.1 From 91cdb08d5fa83a86f5f18088bf148e832732bed9 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 18:08:59 +0100 Subject: Rename columns and rename migrations --- db/migrate/20151203162135_add_ci_to_project.rb | 11 ------- db/migrate/20151204110124_add_project_id_to_ci.rb | 8 ----- db/migrate/20151204110613_migrate_ci_to_project.rb | 37 ---------------------- .../20151204110832_add_index_to_ci_tables.rb | 12 ------- .../20151204123933_drop_null_for_ci_tables.rb | 9 ------ db/migrate/20151210125928_add_ci_to_project.rb | 11 +++++++ db/migrate/20151210125929_add_project_id_to_ci.rb | 8 +++++ db/migrate/20151210125930_migrate_ci_to_project.rb | 37 ++++++++++++++++++++++ .../20151210125931_add_index_to_ci_tables.rb | 12 +++++++ .../20151210125932_drop_null_for_ci_tables.rb | 9 ++++++ db/schema.rb | 8 +++-- 11 files changed, 82 insertions(+), 80 deletions(-) delete mode 100644 db/migrate/20151203162135_add_ci_to_project.rb delete mode 100644 db/migrate/20151204110124_add_project_id_to_ci.rb delete mode 100644 db/migrate/20151204110613_migrate_ci_to_project.rb delete mode 100644 db/migrate/20151204110832_add_index_to_ci_tables.rb delete mode 100644 db/migrate/20151204123933_drop_null_for_ci_tables.rb create mode 100644 db/migrate/20151210125928_add_ci_to_project.rb create mode 100644 db/migrate/20151210125929_add_project_id_to_ci.rb create mode 100644 db/migrate/20151210125930_migrate_ci_to_project.rb create mode 100644 db/migrate/20151210125931_add_index_to_ci_tables.rb create mode 100644 db/migrate/20151210125932_drop_null_for_ci_tables.rb diff --git a/db/migrate/20151203162135_add_ci_to_project.rb b/db/migrate/20151203162135_add_ci_to_project.rb deleted file mode 100644 index 8a65abab636..00000000000 --- a/db/migrate/20151203162135_add_ci_to_project.rb +++ /dev/null @@ -1,11 +0,0 @@ -class AddCiToProject < ActiveRecord::Migration - def up - add_column :projects, :ci_id, :integer - add_column :projects, :builds_enabled, :boolean, default: true, null: false - add_column :projects, :shared_runners_enabled, :boolean, default: true, null: false - add_column :projects, :runners_token, :string - add_column :projects, :build_coverage_regex, :string - add_column :projects, :build_allow_git_fetch, :boolean, default: true, null: false - add_column :projects, :build_timeout, :integer, default: 3600, null: false - end -end diff --git a/db/migrate/20151204110124_add_project_id_to_ci.rb b/db/migrate/20151204110124_add_project_id_to_ci.rb deleted file mode 100644 index 5d1cf543576..00000000000 --- a/db/migrate/20151204110124_add_project_id_to_ci.rb +++ /dev/null @@ -1,8 +0,0 @@ -class AddProjectIdToCi < ActiveRecord::Migration - def up - add_column :ci_builds, :gl_project_id, :integer - add_column :ci_runner_projects, :gl_project_id, :integer - add_column :ci_triggers, :gl_project_id, :integer - add_column :ci_variables, :gl_project_id, :integer - end -end diff --git a/db/migrate/20151204110613_migrate_ci_to_project.rb b/db/migrate/20151204110613_migrate_ci_to_project.rb deleted file mode 100644 index d17b2a425f8..00000000000 --- a/db/migrate/20151204110613_migrate_ci_to_project.rb +++ /dev/null @@ -1,37 +0,0 @@ -class MigrateCiToProject < ActiveRecord::Migration - def up - migrate_project_id_for_table('ci_runner_projects') - migrate_project_id_for_table('ci_triggers') - migrate_project_id_for_table('ci_variables') - migrate_project_id_for_builds - - migrate_project_column('id', 'ci_id') - migrate_project_column('shared_runners_enabled', 'shared_runners_enabled') - migrate_project_column('token', 'runners_token') - migrate_project_column('coverage_regex', 'build_coverage_regex') - migrate_project_column('allow_git_fetch', 'build_allow_git_fetch') - migrate_project_column('timeout', 'build_timeout') - migrate_ci_service - end - - def migrate_project_id_for_table(table) - subquery = "SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = #{table}.project_id" - execute("UPDATE #{table} SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") - end - - def migrate_project_id_for_builds - subquery = 'SELECT gl_project_id FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id' - execute("UPDATE ci_builds SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") - end - - def migrate_project_column(column, new_column = nil) - new_column ||= column - subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" - execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") - end - - def migrate_ci_service - subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService'" - execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE builds_enabled IS NULL AND (#{subquery}) IS NOT NULL") - end -end diff --git a/db/migrate/20151204110832_add_index_to_ci_tables.rb b/db/migrate/20151204110832_add_index_to_ci_tables.rb deleted file mode 100644 index 9fedb5d612c..00000000000 --- a/db/migrate/20151204110832_add_index_to_ci_tables.rb +++ /dev/null @@ -1,12 +0,0 @@ -class AddIndexToCiTables < ActiveRecord::Migration - def up - add_index :ci_builds, :gl_project_id - add_index :ci_runner_projects, :gl_project_id - add_index :ci_triggers, :gl_project_id - add_index :ci_variables, :gl_project_id - add_index :projects, :runners_token - add_index :projects, :builds_enabled - add_index :projects, [:builds_enabled, :shared_runners_enabled] - add_index :projects, [:ci_id] - end -end diff --git a/db/migrate/20151204123933_drop_null_for_ci_tables.rb b/db/migrate/20151204123933_drop_null_for_ci_tables.rb deleted file mode 100644 index 0b007430b0c..00000000000 --- a/db/migrate/20151204123933_drop_null_for_ci_tables.rb +++ /dev/null @@ -1,9 +0,0 @@ -class DropNullForCiTables < ActiveRecord::Migration - def up - remove_index :ci_variables, :project_id - remove_index :ci_runner_projects, :project_id - change_column_null :ci_triggers, :project_id, true - change_column_null :ci_variables, :project_id, true - change_column_null :ci_runner_projects, :project_id, true - end -end diff --git a/db/migrate/20151210125928_add_ci_to_project.rb b/db/migrate/20151210125928_add_ci_to_project.rb new file mode 100644 index 00000000000..8a65abab636 --- /dev/null +++ b/db/migrate/20151210125928_add_ci_to_project.rb @@ -0,0 +1,11 @@ +class AddCiToProject < ActiveRecord::Migration + def up + add_column :projects, :ci_id, :integer + add_column :projects, :builds_enabled, :boolean, default: true, null: false + add_column :projects, :shared_runners_enabled, :boolean, default: true, null: false + add_column :projects, :runners_token, :string + add_column :projects, :build_coverage_regex, :string + add_column :projects, :build_allow_git_fetch, :boolean, default: true, null: false + add_column :projects, :build_timeout, :integer, default: 3600, null: false + end +end diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb new file mode 100644 index 00000000000..5d1cf543576 --- /dev/null +++ b/db/migrate/20151210125929_add_project_id_to_ci.rb @@ -0,0 +1,8 @@ +class AddProjectIdToCi < ActiveRecord::Migration + def up + add_column :ci_builds, :gl_project_id, :integer + add_column :ci_runner_projects, :gl_project_id, :integer + add_column :ci_triggers, :gl_project_id, :integer + add_column :ci_variables, :gl_project_id, :integer + end +end diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb new file mode 100644 index 00000000000..d17b2a425f8 --- /dev/null +++ b/db/migrate/20151210125930_migrate_ci_to_project.rb @@ -0,0 +1,37 @@ +class MigrateCiToProject < ActiveRecord::Migration + def up + migrate_project_id_for_table('ci_runner_projects') + migrate_project_id_for_table('ci_triggers') + migrate_project_id_for_table('ci_variables') + migrate_project_id_for_builds + + migrate_project_column('id', 'ci_id') + migrate_project_column('shared_runners_enabled', 'shared_runners_enabled') + migrate_project_column('token', 'runners_token') + migrate_project_column('coverage_regex', 'build_coverage_regex') + migrate_project_column('allow_git_fetch', 'build_allow_git_fetch') + migrate_project_column('timeout', 'build_timeout') + migrate_ci_service + end + + def migrate_project_id_for_table(table) + subquery = "SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = #{table}.project_id" + execute("UPDATE #{table} SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_id_for_builds + subquery = 'SELECT gl_project_id FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id' + execute("UPDATE ci_builds SET gl_project_id=(#{subquery}) WHERE gl_project_id IS NULL") + end + + def migrate_project_column(column, new_column = nil) + new_column ||= column + subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" + execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") + end + + def migrate_ci_service + subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService'" + execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE builds_enabled IS NULL AND (#{subquery}) IS NOT NULL") + end +end diff --git a/db/migrate/20151210125931_add_index_to_ci_tables.rb b/db/migrate/20151210125931_add_index_to_ci_tables.rb new file mode 100644 index 00000000000..9fedb5d612c --- /dev/null +++ b/db/migrate/20151210125931_add_index_to_ci_tables.rb @@ -0,0 +1,12 @@ +class AddIndexToCiTables < ActiveRecord::Migration + def up + add_index :ci_builds, :gl_project_id + add_index :ci_runner_projects, :gl_project_id + add_index :ci_triggers, :gl_project_id + add_index :ci_variables, :gl_project_id + add_index :projects, :runners_token + add_index :projects, :builds_enabled + add_index :projects, [:builds_enabled, :shared_runners_enabled] + add_index :projects, [:ci_id] + end +end diff --git a/db/migrate/20151210125932_drop_null_for_ci_tables.rb b/db/migrate/20151210125932_drop_null_for_ci_tables.rb new file mode 100644 index 00000000000..0b007430b0c --- /dev/null +++ b/db/migrate/20151210125932_drop_null_for_ci_tables.rb @@ -0,0 +1,9 @@ +class DropNullForCiTables < ActiveRecord::Migration + def up + remove_index :ci_variables, :project_id + remove_index :ci_runner_projects, :project_id + change_column_null :ci_triggers, :project_id, true + change_column_null :ci_variables, :project_id, true + change_column_null :ci_runner_projects, :project_id, true + end +end diff --git a/db/schema.rb b/db/schema.rb index fd9004faa29..d17930b84b0 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: 20151204123933) do +ActiveRecord::Schema.define(version: 20151210125932) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -655,9 +655,10 @@ ActiveRecord::Schema.define(version: 20151204123933) do t.string "import_source" t.integer "commit_count", default: 0 t.text "import_error" + t.integer "ci_id" t.boolean "builds_enabled", default: true, null: false t.boolean "shared_runners_enabled", default: true, null: false - t.string "token" + t.string "runners_token" t.string "build_coverage_regex" t.boolean "build_allow_git_fetch", default: true, null: false t.integer "build_timeout", default: 3600, null: false @@ -665,13 +666,14 @@ ActiveRecord::Schema.define(version: 20151204123933) do add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree add_index "projects", ["builds_enabled"], name: "index_projects_on_builds_enabled", using: :btree + add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree + add_index "projects", ["runners_token"], name: "index_projects_on_runners_token", using: :btree add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree - add_index "projects", ["token"], name: "index_projects_on_token", using: :btree add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: :cascade do |t| -- cgit v1.2.1 From 3578153d3e838da92587fed88a608d9a5458c37c Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 18:12:19 +0100 Subject: Fix triggers tests --- lib/api/triggers.rb | 2 +- spec/requests/api/triggers_spec.rb | 8 ++++---- spec/requests/ci/api/triggers_spec.rb | 24 +++++++++--------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index b713c00e82a..2781f1cf191 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -14,7 +14,7 @@ module API post ":id/trigger/builds" do required_attributes! [:ref, :token] - project = Project.find_with_namespace(id) || Project.find_by(id: params[:id]) + project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id]) trigger = Ci::Trigger.find_by_token(params[:token].to_s) not_found! unless project && trigger unauthorized! unless trigger.project == project diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 899458e619e..314bd7ddc59 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -25,7 +25,7 @@ describe API::API do end it 'should return not found if project is not found' do - post api('/projects/0/refs/master/trigger/builds'), options.merge(ref: 'master') + post api('/projects/0/trigger/builds'), options.merge(ref: 'master') expect(response.status).to eq(404) end @@ -57,19 +57,19 @@ describe API::API do end it 'should validate variables to be a hash' do - post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', refs: 'master') + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: 'value', ref: 'master') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do - post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, refs: 'master') + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: { key: %w(1 2) }, ref: 'master') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do - post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, refs: 'master') + post api("/projects/#{project.id}/trigger/builds"), options.merge(variables: variables, ref: 'master') expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.first.trigger_request.variables).to eq(variables) diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb index cd33a96e1a6..0ef03f9371b 100644 --- a/spec/requests/ci/api/triggers_spec.rb +++ b/spec/requests/ci/api/triggers_spec.rb @@ -5,16 +5,14 @@ describe Ci::API::API do describe 'POST /projects/:project_id/refs/:ref/trigger' do let!(:trigger_token) { 'secure token' } - let!(:project) { FactoryGirl.create(:project) } - let!(:project2) { FactoryGirl.create(:empty_project) } + let!(:project) { FactoryGirl.create(:project, ci_id: 10) } + let!(:project2) { FactoryGirl.create(:empty_project, ci_id: 11) } let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } let(:options) do { token: trigger_token } end - let(:project_ci_id) { create_ci_id(project) } - let(:project2_ci_id) { create_ci_id(project2) } before do stub_ci_commit_to_return_yaml_file @@ -22,7 +20,7 @@ describe Ci::API::API do context 'Handles errors' do it 'should return bad request if token is missing' do - post ci_api("/projects/#{project_ci_id}/refs/master/trigger") + post ci_api("/projects/#{project.ci_id}/refs/master/trigger") expect(response.status).to eq(400) end @@ -32,7 +30,7 @@ describe Ci::API::API do end it 'should return unauthorized if token is for different project' do - post ci_api("/projects/#{project2_ci_id}/refs/master/trigger"), options + post ci_api("/projects/#{project2.ci_id}/refs/master/trigger"), options expect(response.status).to eq(401) end end @@ -41,14 +39,14 @@ describe Ci::API::API do let(:commit) { project.ci_commits.last } it 'should create builds' do - post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.size).to eq(2) end it 'should return bad request with no builds created if there\'s no commit for that ref' do - post ci_api("/projects/#{project_ci_id}/refs/other-branch/trigger"), options + post ci_api("/projects/#{project.ci_id}/refs/other-branch/trigger"), options expect(response.status).to eq(400) expect(json_response['message']).to eq('No builds created') end @@ -59,19 +57,19 @@ describe Ci::API::API do end it 'should validate variables to be a hash' do - post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: 'value') + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: 'value') expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a hash') end it 'should validate variables needs to be a map of key-valued strings' do - post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: { key: %w(1 2) }) expect(response.status).to eq(400) expect(json_response['message']).to eq('variables needs to be a map of key-valued strings') end it 'create trigger request with variables' do - post ci_api("/projects/#{project_ci_id}/refs/master/trigger"), options.merge(variables: variables) + post ci_api("/projects/#{project.ci_id}/refs/master/trigger"), options.merge(variables: variables) expect(response.status).to eq(201) commit.builds.reload expect(commit.builds.first.trigger_request.variables).to eq(variables) @@ -79,8 +77,4 @@ describe Ci::API::API do end end end - - def create_ci_id(project) - ActiveRecord::Base.connection.insert("INSERT INTO ci_projects (gitlab_id) VALUES(#{project.id})") - end end -- cgit v1.2.1 From 1e2a4895c803f4881ab63c1816141462fbfa6d2b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Thu, 10 Dec 2015 18:47:22 +0100 Subject: Finishing touches --- app/helpers/yaml_helper.rb | 11 ----------- app/models/commit_status.rb | 4 +--- app/services/ci/create_builds_service.rb | 2 +- app/views/ci/shared/_guide.html.haml | 6 ++---- app/views/projects/commit/_builds.html.haml | 1 + 5 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 app/helpers/yaml_helper.rb diff --git a/app/helpers/yaml_helper.rb b/app/helpers/yaml_helper.rb deleted file mode 100644 index 17990e1f475..00000000000 --- a/app/helpers/yaml_helper.rb +++ /dev/null @@ -1,11 +0,0 @@ -module YamlHelper - def yaml_web_editor_link(project) - commits = project.ci_commits - - if commits.any? && commits.last.ci_yaml_file - "#{project.gitlab_url}/edit/master/.gitlab-ci.yml" - else - "#{project.gitlab_url}/new/master" - end - end -end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 579b638706d..77c3f776aab 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -50,7 +50,6 @@ class CommitStatus < ActiveRecord::Base 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 :has_coverage?, -> { where.not(coverage: nil).any? } state_machine :status, initial: :pending do event :run do @@ -88,8 +87,7 @@ class CommitStatus < ActiveRecord::Base state :canceled, value: 'canceled' end - delegate :sha, :short_sha, :project, - to: :commit, prefix: false + delegate :sha, :short_sha, to: :commit, prefix: false # TODO: this should be removed with all references def before_sha diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb index ba7c4632f49..ad901f2da5d 100644 --- a/app/services/ci/create_builds_service.rb +++ b/app/services/ci/create_builds_service.rb @@ -30,7 +30,7 @@ module Ci tag: tag, trigger_request: trigger_request, user: user, - gl_project_id: commit.gl_project_id) + project: commit.project) build = commit.builds.create!(build_attrs) build.execute_hooks diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml index ec768858669..09e7e653521 100644 --- a/app/views/ci/shared/_guide.html.haml +++ b/app/views/ci/shared/_guide.html.haml @@ -6,10 +6,8 @@ Add at least one runner to the project. Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions. %li - Put the .gitlab-ci.yml in the root of your repository. Examples can be found in #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. + Put the .gitlab-ci.yml in the root of your repository. Examples can be found in + #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}. You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} - %li - Visit #{link_to 'GitLab project settings', @project.gitlab_url + "/services/gitlab_ci/edit", target: :blank} - and press the "Test settings" button. %li Return to this page and refresh it, it should show a new build. diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml index 952f33184fc..c87b06d2f01 100644 --- a/app/views/projects/commit/_builds.html.haml +++ b/app/views/projects/commit/_builds.html.haml @@ -22,6 +22,7 @@ %ul - @ci_commit.yaml_errors.split(",").each do |error| %li= error + You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path} - if @ci_commit.project.builds_enabled? && !@ci_commit.ci_yaml_file .bs-callout.bs-callout-warning -- cgit v1.2.1 From 2b7a75ceaf96830e482fc5c59460fe2dd6ff0a28 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 13:25:42 +0100 Subject: Update badge --- app/helpers/ci_badge_helper.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/helpers/ci_badge_helper.rb b/app/helpers/ci_badge_helper.rb index a81edbcb416..27386133e36 100644 --- a/app/helpers/ci_badge_helper.rb +++ b/app/helpers/ci_badge_helper.rb @@ -1,11 +1,13 @@ module CiBadgeHelper def markdown_badge_code(project, ref) url = status_ci_project_url(project, ref: ref, format: 'png') - "[![build status](#{url})](#{ci_project_url(project, ref: ref)})" + link = namespace_project_commits_path(project.namespace, project, ref) + "[![build status](#{url})](#{link})" end def html_badge_code(project, ref) url = status_ci_project_url(project, ref: ref, format: 'png') - "" + link = namespace_project_commits_path(project.namespace, project, ref) + "" end end -- cgit v1.2.1 From 73b04bebad23ce6750d7747c821a93cfeb73a9d2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 13:34:11 +0100 Subject: Fix errors --- app/controllers/projects/merge_requests_controller.rb | 3 --- app/models/commit_status.rb | 2 +- app/services/create_commit_builds_service.rb | 1 - app/views/projects/commit/_builds.html.haml | 2 +- spec/features/merge_requests/merge_when_build_succeeds_spec.rb | 4 ++-- spec/services/create_commit_builds_service_spec.rb | 1 - .../services/merge_requests/merge_when_build_succeeds_service_spec.rb | 2 +- 7 files changed, 5 insertions(+), 10 deletions(-) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 530f3d3dcb8..3ae4c5b099f 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -81,8 +81,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def builds - @ci_project = @merge_request.source_project.gitlab_ci_project - respond_to do |format| format.html { render 'show' } format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } } @@ -106,7 +104,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController @first_commit = @merge_request.first_commit @diffs = @merge_request.compare_diffs - @ci_project = @source_project.gitlab_ci_project @ci_commit = @merge_request.ci_commit @statuses = @ci_commit.statuses if @ci_commit diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 77c3f776aab..21c5c87bc3d 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -77,7 +77,7 @@ class CommitStatus < ActiveRecord::Base end after_transition [:pending, :running] => :success do |build, transition| - MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build) + MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.project, nil).trigger(build) end state :pending, value: 'pending' diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb index 886bd88d204..392fe21eecf 100644 --- a/app/services/create_commit_builds_service.rb +++ b/app/services/create_commit_builds_service.rb @@ -26,4 +26,3 @@ class CreateCommitBuildsService commit end end - diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml index c87b06d2f01..e65a5a9c2de 100644 --- a/app/views/projects/commit/_builds.html.haml +++ b/app/views/projects/commit/_builds.html.haml @@ -1,6 +1,6 @@ .gray-content-block.middle-block .pull-right - - if @ci_project && can?(current_user, :manage_builds, @ci_commit.project) + - if can?(current_user, :manage_builds, @ci_commit.project) - if @ci_commit.builds.latest.failed.any?(&:retryable?) = link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post diff --git a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb index a674124aab7..28a46a0725d 100644 --- a/spec/features/merge_requests/merge_when_build_succeeds_spec.rb +++ b/spec/features/merge_requests/merge_when_build_succeeds_spec.rb @@ -12,7 +12,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do end context "Active build for Merge Request" do - let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } let!(:ci_build) { create(:ci_build, commit: ci_commit) } before do @@ -47,7 +47,7 @@ feature 'Merge When Build Succeeds', feature: true, js: true do merge_user: user, title: "MepMep", merge_when_build_succeeds: true) end - let!(:ci_commit) { create(:ci_commit, gl_project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } + let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch) } let!(:ci_build) { create(:ci_build, commit: ci_commit) } before do diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index 70e28f6c528..ae56f0b3a9a 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -168,4 +168,3 @@ describe CreateCommitBuildsService, services: true do end end end - diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb index 188fda6211f..449cecaa789 100644 --- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb +++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb @@ -11,7 +11,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do end let(:project) { create(:project) } - let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, gl_project: project) } + let(:ci_commit) { create(:ci_commit_with_one_job, ref: mr_merge_if_green_enabled.source_branch, project: project) } let(:service) { MergeRequests::MergeWhenBuildSucceedsService.new(project, user, commit_message: 'Awesome message') } describe "#execute" do -- cgit v1.2.1 From 513d551c8f7078e263d31ef2c30a1f72cbab3fae Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 13:39:43 +0100 Subject: Fix after column rename --- app/controllers/projects_controller.rb | 2 +- app/models/ci/build.rb | 4 ++-- app/views/projects/edit.html.haml | 4 ++-- lib/ci/api/entities.rb | 9 --------- spec/lib/gitlab/backend/grack_auth_spec.rb | 4 ++-- spec/models/build_spec.rb | 2 +- spec/models/project_spec.rb | 8 ++++---- spec/requests/ci/api/builds_spec.rb | 16 ++++++++-------- spec/requests/ci/api/runners_spec.rb | 2 +- 9 files changed, 21 insertions(+), 30 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index e9917109f3e..bf5e25ff895 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -210,7 +210,7 @@ class ProjectsController < ApplicationController def project_params params.require(:project).permit( - :name, :path, :description, :issues_tracker, :tag_list, :token, + :name, :path, :description, :issues_tracker, :tag_list, :runners_token, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 401e08115dc..6d9cdb95295 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -194,7 +194,7 @@ module Ci def trace trace = raw_trace if project && trace.present? - trace.gsub(project.token, 'xxxxxx') + trace.gsub(project.runners_token, 'xxxxxx') else trace end @@ -221,7 +221,7 @@ module Ci end def token - project.token + project.runners_token end def valid_token? token diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index a7ab9b44e79..650629ef1b9 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -163,9 +163,9 @@ %legend Advanced settings .form-group - = f.label :token, "CI token", class: 'control-label' + = f.label :runners_token, "CI token", class: 'control-label' .col-sm-10 - = f.text_field :token, class: "form-control", placeholder: 'xEeFCaDAB89' + = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' %p.help-block The secure token used to checkout project. .form-actions diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb index 750f421872d..e4ac0545ea2 100644 --- a/lib/ci/api/entities.rb +++ b/lib/ci/api/entities.rb @@ -37,15 +37,6 @@ module Ci expose :id, :token end - class Project < Grape::Entity - expose :id, :name, :token, :default_ref, :gitlab_url, :path, - :always_build, :polling_interval, :public, :ssh_url_to_repo, :gitlab_id - - expose :timeout do |model| - model.timeout - end - end - class RunnerProject < Grape::Entity expose :id, :project_id, :runner_id end diff --git a/spec/lib/gitlab/backend/grack_auth_spec.rb b/spec/lib/gitlab/backend/grack_auth_spec.rb index 2dd27464de7..cd26dca0998 100644 --- a/spec/lib/gitlab/backend/grack_auth_spec.rb +++ b/spec/lib/gitlab/backend/grack_auth_spec.rb @@ -191,10 +191,10 @@ describe Grack::Auth, lib: true do context "when a gitlab ci token is provided" do let(:token) { "123" } - let(:project) { FactoryGirl.create :empty_project, token: token } + let(:project) { FactoryGirl.create :empty_project } before do - project.update_attributes(token: token, builds_enabled: true) + project.update_attributes(runners_token: token, builds_enabled: true) env["HTTP_AUTHORIZATION"] = ActionController::HttpAuthentication::Basic.encode_credentials("gitlab-ci-token", token) end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 91af694fc03..1fd475d205e 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -111,7 +111,7 @@ describe Ci::Build, models: true do let(:token) { 'my_secret_token' } before do - build.project.update_attributes(token: token) + build.project.update_attributes(runners_token: token) build.update_attributes(trace: token) end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b962f66ba26..87582e07494 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -98,13 +98,13 @@ describe Project, models: true do describe 'project token' do it 'should set an random token if none provided' do - project = FactoryGirl.create :empty_project, token: '' - expect(project.token).not_to eq('') + project = FactoryGirl.create :empty_project, runners_token: '' + expect(project.runners_token).not_to eq('') end it 'should not set an random toke if one provided' do - project = FactoryGirl.create :empty_project, token: 'my-token' - expect(project.token).to eq('my-token') + project = FactoryGirl.create :empty_project, runners_token: 'my-token' + expect(project.runners_token).to eq('my-token') end end diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 550d74d158a..c27e87c4acc 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -131,7 +131,7 @@ describe Ci::API::API do let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") } let(:get_url) { ci_api("/builds/#{build.id}/artifacts") } let(:headers) { { "GitLab-Workhorse" => "1.0" } } - let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) } + let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token) } describe "POST /builds/:id/artifacts/authorize" do context "should authorize posting artifact to running build" do @@ -140,7 +140,7 @@ describe Ci::API::API do end it "using token as parameter" do - post authorize_url, { token: build.project.token }, headers + post authorize_url, { token: build.token }, headers expect(response.status).to eq(200) expect(json_response["TempPath"]).to_not be_nil end @@ -159,7 +159,7 @@ describe Ci::API::API do it "using token as parameter" do stub_application_setting(max_artifacts_size: 0) - post authorize_url, { token: build.project.token, filesize: 100 }, headers + post authorize_url, { token: build.token, filesize: 100 }, headers expect(response.status).to eq(413) end @@ -239,7 +239,7 @@ describe Ci::API::API do end it do - post post_url, { token: build.project.token }, {} + post post_url, { token: build.token }, {} expect(response.status).to eq(403) end end @@ -279,12 +279,12 @@ describe Ci::API::API do describe "DELETE /builds/:id/artifacts" do before do build.run! - post delete_url, token: build.project.token, file: file_upload + post delete_url, token: build.token, file: file_upload end it "should delete artifact build" do build.success - delete delete_url, token: build.project.token + delete delete_url, token: build.token expect(response.status).to eq(200) end end @@ -296,12 +296,12 @@ describe Ci::API::API do it "should download artifact" do build.update_attributes(artifacts_file: file_upload) - get get_url, token: build.project.token + get get_url, token: build.token expect(response.status).to eq(200) end it "should fail to download if no artifact uploaded" do - get get_url, token: build.project.token + get get_url, token: build.token expect(response.status).to eq(404) end end diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 944d910d055..b98c4d506c7 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -31,7 +31,7 @@ describe Ci::API::API do describe "should create a runner if project token provided" do let(:project) { FactoryGirl.create(:empty_project) } - before { post ci_api("/runners/register"), token: project.token } + before { post ci_api("/runners/register"), token: project.runners_token } it { expect(response.status).to eq(201) } it { expect(project.runners.size).to eq(1) } -- cgit v1.2.1 From dd8102f2d1acd803793777b26c84d7c6e977b8e2 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 14:37:16 +0100 Subject: Fix specs --- app/models/project.rb | 6 ++++-- app/views/projects/commit/_builds.html.haml | 8 ++++---- app/views/projects/triggers/index.html.haml | 2 +- lib/gitlab/backend/grack_auth.rb | 2 +- spec/models/build_spec.rb | 2 +- spec/models/commit_status_spec.rb | 3 ++- spec/services/create_commit_builds_service_spec.rb | 4 ++-- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/app/models/project.rb b/app/models/project.rb index ba04470c64e..e1f7bf971e3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -830,11 +830,13 @@ class Project < ActiveRecord::Base end def valid_runners_token? token - self.token && self.token == token + self.runners_token && self.runners_token == token end + # TODO (ayufan): For now we use runners_token (backward compatibility) + # In 8.4 every build will have its own individual token valid for time of build def valid_build_token? token - self.token && self.token == token + self.builds_enabled? && self.runners_token && self.runners_token == token end def build_coverage_enabled? diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml index e65a5a9c2de..329aaa0bb8b 100644 --- a/app/views/projects/commit/_builds.html.haml +++ b/app/views/projects/commit/_builds.html.haml @@ -39,12 +39,12 @@ %th Name %th Duration %th Finished at - - if @ci_commit.project.coverage_enabled? + - if @ci_commit.project.build_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, - locals: { coverage: @ci_commit.project.coverage_enabled?, stage: true, allow_retry: true } + locals: { coverage: @ci_commit.project.build_coverage_enabled?, stage: true, allow_retry: true } - if @ci_commit.retried.any? .gray-content-block.second-block @@ -61,8 +61,8 @@ %th Name %th Duration %th Finished at - - if @ci_commit.project.coverage_enabled? + - if @ci_commit.project.build_coverage_enabled? %th Coverage %th = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, - locals: { coverage: @ci_commit.project.coverage_enabled?, stage: true } + locals: { coverage: @ci_commit.project.build_coverage_enabled?, stage: true } diff --git a/app/views/projects/triggers/index.html.haml b/app/views/projects/triggers/index.html.haml index fb3794764c5..bd346c4b8e6 100644 --- a/app/views/projects/triggers/index.html.haml +++ b/app/views/projects/triggers/index.html.haml @@ -68,4 +68,4 @@ -F token=TOKEN \ -F "ref=REF_NAME" \ -F "variables[RUN_NIGHTLY_BUILD]=true" \ - #{builds_trigger_url(@project.id, 'TOKEN')} + #{builds_trigger_url(@project.id)} diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb index d854c1c8683..cdcaae8094c 100644 --- a/lib/gitlab/backend/grack_auth.rb +++ b/lib/gitlab/backend/grack_auth.rb @@ -78,7 +78,7 @@ module Grack underscored_service = matched_login['s'].underscore if underscored_service == 'gitlab_ci' - return project && project.builds_enabled? && project.valid_build_token?(password) + return project && project.valid_build_token?(password) elsif Service.available_services_names.include?(underscored_service) service_method = "#{underscored_service}_service" service = project.send(service_method) diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 1fd475d205e..96b6f1dbca6 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -386,7 +386,7 @@ describe Ci::Build, models: true do it { is_expected.to be_a(String) } it { is_expected.to end_with(".git") } it { is_expected.to start_with(project.web_url[0..6]) } - it { is_expected.to include(project.token) } + it { is_expected.to include(build.token) } it { is_expected.to include('gitlab-ci-token') } it { is_expected.to include(project.web_url[7..-1]) } end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 6131191f0fa..b8f901b3433 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -39,12 +39,13 @@ describe CommitStatus, models: true do it { is_expected.to belong_to(:commit) } it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:project) } + 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(:project).to(:commit) } it { is_expected.to respond_to :success? } it { is_expected.to respond_to :failed? } diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index ae56f0b3a9a..798c480b81a 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -20,11 +20,11 @@ describe CreateCommitBuildsService, services: true do ) end - it { expect(commit).to be_kind_of(Commit) } + it { expect(commit).to be_kind_of(Ci::Commit) } it { expect(commit).to be_valid } it { expect(commit).to be_persisted } it { expect(commit).to eq(project.ci_commits.last) } - it { expect(commit.builds.first).to be_kind_of(Build) } + it { expect(commit.builds.first).to be_kind_of(Ci::Build) } end context "skip tag if there is no build for it" do -- cgit v1.2.1 From 1c0683da7b593bb12e37d46e89a0764a93a0da95 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 17:44:00 +0100 Subject: Fix last specs --- app/views/projects/runners/_specific_runners.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml index 67767f3e34e..30cd1263a12 100644 --- a/app/views/projects/runners/_specific_runners.html.haml +++ b/app/views/projects/runners/_specific_runners.html.haml @@ -12,7 +12,7 @@ %code #{ci_root_url(only_path: false)} %li Use the following registration token during setup: - %code #{@project.token} + %code #{@project.runners_token} %li Start runner! -- cgit v1.2.1 From c9ac38a074423b30b5627553bfdbf0ba15737d8e Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 17:57:04 +0100 Subject: Use Gitlab::Git instead of Ci::Git --- app/models/ci/commit.rb | 2 +- app/services/create_commit_builds_service.rb | 6 +++--- lib/ci/git.rb | 5 ----- 3 files changed, 4 insertions(+), 9 deletions(-) delete mode 100644 lib/ci/git.rb diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 79193344545..d2a29236942 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -53,7 +53,7 @@ module Ci end def valid_commit_sha - if self.sha == Ci::Git::BLANK_SHA + if self.sha == Gitlab::Git::BLANK_SHA self.errors.add(:sha, " cant be 00000000 (branch removal)") end end diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb index 392fe21eecf..759c334ebe9 100644 --- a/app/services/create_commit_builds_service.rb +++ b/app/services/create_commit_builds_service.rb @@ -9,14 +9,14 @@ class CreateCommitBuildsService return false end - ref = origin_ref.gsub(/\Arefs\/(tags|heads)\//, '') + ref = Gitlab::Git.ref_name(origin_ref) # Skip branch removal - if sha == Ci::Git::BLANK_SHA + if sha == Gitlab::Git::BLANK_SHA return false end - tag = origin_ref.start_with?('refs/tags/') + tag = Gitlab::Git.tag_ref?(origin_ref) commit = project.ensure_ci_commit(sha) unless commit.skip_ci? commit.update_committed! diff --git a/lib/ci/git.rb b/lib/ci/git.rb deleted file mode 100644 index 7acc3f38edb..00000000000 --- a/lib/ci/git.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Ci - module Git - BLANK_SHA = '0' * 40 - end -end -- cgit v1.2.1 From fc1e2f89864a74d78d67ca27d99bd43407c703df Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Fri, 11 Dec 2015 18:03:48 +0100 Subject: Run builds from projects with enabled CI --- app/services/ci/register_build_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb index eba602da992..4ff268a6f06 100644 --- a/app/services/ci/register_build_service.rb +++ b/app/services/ci/register_build_service.rb @@ -11,7 +11,7 @@ module Ci builds.joins(:project).where(projects: { builds_enabled: true, shared_runners_enabled: true }) else # do run projects which are only assigned to this runner - builds.where(project: current_runner.projects) + builds.where(project: current_runner.projects.where(builds_enabled: true)) end builds = builds.order('created_at ASC') -- cgit v1.2.1 From baa97175abd25eb566d7273bc188a6459639abf3 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Fri, 11 Dec 2015 11:09:34 -0600 Subject: Clarify cache behavior --- doc/ci/yaml/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 3dbf1afc7a9..7e2edb945da 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -113,6 +113,9 @@ The YAML-defined variables are also set to all created service containers, thus ### cache `cache` is used to specify list of files and directories which should be cached between builds. +Caches are stored according to the branch/ref and the job name. Caches are not +currently shared between different job names or between branches/refs. This means +caching will benefit you if you push subsequent commits to an existing feature branch. **The global setting allows to specify default cached files for all jobs.** -- cgit v1.2.1 From 722f14816c85bc5996caac33adb4e4c54e6efdfc Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 11 Dec 2015 13:27:28 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 4c8d8dcd4df..cc6d19063fa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -46,6 +46,7 @@ v 8.3.0 (unreleased) - Fix emoji aliases problem - Fix award-emojis Flash alert's width - Fix deleting notes on a merge request diff + - Display referenced merge request statuses in the issue description (Greg Smethells) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 456ddb5eb7f905899bd1a6258617eb505f4dc289 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 11 Dec 2015 13:57:49 -0500 Subject: Fix time_ago_with_tooltip for activity feed Closes #4002 --- app/helpers/application_helper.rb | 8 ++++++-- spec/helpers/application_helper_spec.rb | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 21f962df206..39d3017162e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -204,12 +204,16 @@ module ApplicationHelper # Returns an HTML-safe String def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) element = content_tag :time, time.to_s, - class: "#{html_class} js-timeago", + class: "#{html_class} js-timeago js-timeago-pending", datetime: time.getutc.iso8601, title: time.in_time_zone.stamp('Aug 21, 2011 9:23pm'), data: { toggle: 'tooltip', placement: placement, container: 'body' } - element += javascript_tag "$('.js-timeago').last().timeago()" unless skip_js + unless skip_js + element << javascript_tag( + "$('.js-timeago-pending').removeClass('js-timeago-pending').timeago()" + ) + end element end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 5568f06639c..68527c3a4f8 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -263,11 +263,12 @@ describe ApplicationHelper do end it 'includes a default js-timeago class' do - expect(element.attr('class')).to eq 'time_ago js-timeago' + expect(element.attr('class')).to eq 'time_ago js-timeago js-timeago-pending' end it 'accepts a custom html_class' do - expect(element(html_class: 'custom_class').attr('class')).to eq 'custom_class js-timeago' + expect(element(html_class: 'custom_class').attr('class')). + to eq 'custom_class js-timeago js-timeago-pending' end it 'accepts a custom tooltip placement' do @@ -278,7 +279,7 @@ describe ApplicationHelper do el = element.next_element expect(el.name).to eq 'script' - expect(el.text).to include "$('.js-timeago').last().timeago()" + expect(el.text).to include "$('.js-timeago-pending').removeClass('js-timeago-pending').timeago()" end it 'allows the script tag to be excluded' do -- cgit v1.2.1 From f7bba47f8ab35f22882dc232229cbc5d36ead002 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:01:49 +0100 Subject: Implement issuable sidebar partial Signed-off-by: Dmitriy Zaporozhets --- app/views/shared/issuable/_sidebar.html.haml | 80 ++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 app/views/shared/issuable/_sidebar.html.haml diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml new file mode 100644 index 00000000000..10f1aaf8c31 --- /dev/null +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -0,0 +1,80 @@ +.issuable-sidebar.issuable-affix + = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| + .block + .title + Cross-project reference + .cross-project-reference + %span#cross-project-reference.has_tooltip{title: 'Cross-project reference'} + = cross_project_reference(@project, issuable) + = clipboard_button(clipboard_target: 'span#cross-project-reference') + + .block + .title + %label + Assignee: + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value + - if issuable.assignee + %strong= link_to_member(@project, issuable.assignee, size: 24) + - else + none + + .selectbox + = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) + + .block + .title + %label + Milestone: + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value + - if issuable.milestone + %span.back-to-milestone + = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do + %strong + = icon('clock-o') + = issuable.milestone.title + - else + none + .selectbox + = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) + = hidden_field_tag :issuable_context + = f.submit class: 'btn hide' + + - if issuable.labels.any? + .block + .title + %label Labels + - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) + .pull-right + = link_to 'Edit', '#', class: 'edit-link' + .value.issuable-show-labels + - issuable.labels.each do |label| + = link_to_label(label) + .selectbox + = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, + { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } + + = render "shared/issuable/participants", participants: issuable.participants(current_user) + + - if current_user + - subscribed = issuable.subscribed?(current_user) + .block.light + .title + %label.light Notifications + - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' + %button.btn.btn-block.btn-gray.subscribe-button{:type => 'button'} + %span= subscribed ? 'Unsubscribe' : 'Subscribe' + .subscription-status{data: {status: subscribtion_status}} + .unsubscribed{class: ( 'hidden' if subscribed )} + You're not receiving notifications from this thread. + .subscribed{class: ( 'hidden' unless subscribed )} + You're receiving notifications because you're subscribed to this thread. + + :javascript + new Subscription("#{toggle_subscription_path(issuable)}"); + new IssuableContext(); -- cgit v1.2.1 From 76ff8eac26ef949b5bc99d99878cc8b43c4b4326 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:02:14 +0100 Subject: Move awards css to separate file Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/awards.scss | 68 ++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 app/assets/stylesheets/pages/awards.scss diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss new file mode 100644 index 00000000000..895bafebcef --- /dev/null +++ b/app/assets/stylesheets/pages/awards.scss @@ -0,0 +1,68 @@ +.awards { + @include clearfix; + line-height: 34px; + margin-top: $gl-padding; + + .award { + @include border-radius(5px); + + border: 1px solid; + padding: 0px 10px; + float: left; + margin-right: 5px; + border-color: $border-color; + cursor: pointer; + + &.active { + border-color: $border-gray-light; + background-color: $gray-light; + + .counter { + font-weight: bold; + } + } + + .icon { + float: left; + margin-right: 10px; + } + + .counter { + float: left; + } + } + + .awards-controls { + margin-left: 10px; + float: left; + + .add-award { + font-size: 24px; + color: $gl-gray; + position: relative; + top: 2px; + + &:hover, + &:link { + text-decoration: none; + } + } + + .awards-menu { + padding: $gl-padding; + min-width: 214px; + + > li { + cursor: pointer; + margin: 5px; + } + } + } + + .awards-menu{ + li { + float: left; + margin: 3px; + } + } +} -- cgit v1.2.1 From b0591ff03a11eff7d7bee9aab67cd73b80fe9105 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:03:48 +0100 Subject: Redesign issue page for new sidebar Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/issuable.scss | 152 +++++++++------------- app/views/projects/issues/_discussion.html.haml | 23 +--- app/views/projects/issues/show.html.haml | 51 +++++--- app/views/projects/issues/update.js.haml | 4 +- app/views/shared/issuable/_context.html.haml | 57 -------- app/views/shared/issuable/_participants.html.haml | 8 +- 6 files changed, 97 insertions(+), 198 deletions(-) delete mode 100644 app/views/shared/issuable/_context.html.haml diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 957da5c182e..35af92e7df3 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -24,20 +24,6 @@ } } -.issuable-context-title { - margin-bottom: 5px; - - .avatar { - margin-left: 0; - } - - label { - color: $gl-gray; - font-weight: normal; - margin-right: 4px; - } -} - .project-issuable-filter { .controls { float: right; @@ -50,23 +36,6 @@ } .issuable-details { - .page-title { - margin-top: -$gl-padding; - padding: 7px 0; - margin-bottom: 0; - color: #5c5d5e; - font-size: 16px; - line-height: 42px; - - .author { - color: #5c5d5e; - } - - .issue-id { - color: #5c5d5e; - } - } - .issue-title { margin: 0; font-size: 23px; @@ -80,6 +49,20 @@ margin-bottom: 0; } } + + section { + border-right: 1px solid #ECEEF1; + + > .tab-content { + margin-right: 1px; + } + + > .gray-content-block { + margin-top: 0; + border-top: none; + margin-right: -15px; + } + } } .issuable-filter-count { @@ -101,84 +84,67 @@ } } -.cross-project-reference { - text-align: center; - width: 100%; +.issuable-sidebar { + .block { + @include clearfix; + padding: $gl-padding 0; + border-bottom: 1px solid #F0F0F0; - .slead { - padding: 5px; - } - - span, button { - background-color: $background-color; + &:last-child { + border: none; + } } -} - -.awards { - @include clearfix; - line-height: 34px; - margin: 2px 0; - - .award { - @include border-radius(5px); - border: 1px solid; - padding: 0px 10px; - float: left; - margin: 0 5px; - border-color: $border-color; - cursor: pointer; + .title { + color: $gl-text-color; + margin-bottom: 8px; - &.active { - border-color: $border-gray-light; - background-color: $gray-light; - - .counter { - font-weight: bold; - } + .avatar { + margin-left: 0; } - .icon { - float: left; - margin-right: 10px; + label { + font-weight: normal; + margin-right: 4px; } - .counter { - float: left; + .edit-link { + color: $gl-gray; } } - .awards-controls { - margin-left: 10px; - float: left; + .cross-project-reference { + font-weight: bold; + color: $gl-link-color; - .add-award { - font-size: 24px; - color: $gl-gray; - position: relative; - top: 2px; - - &:hover, - &:link { - text-decoration: none; - } + button { + float: right; } + } - .awards-menu { - padding: $gl-padding; - min-width: 214px; + .selectbox { + display: none + } - > li { - cursor: pointer; - margin: 5px; - } - } + .btn-clipboard { + color: $gl-gray; } +} - .awards-menu{ - li { - float: left; - margin: 3px; - } +.issuable-title { + margin: -$gl-padding; + padding: 7px $gl-padding; + margin-bottom: 0px; + border-bottom: 1px solid $border-color; + color: #5c5d5e; + font-size: 16px; + line-height: 42px; + + .author { + color: #5c5d5e; + } + + .issuable-id { + color: #5c5d5e; } } diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index f2011542ca7..86d3dc546ba 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -5,24 +5,5 @@ - else = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close js-note-target-close', title: 'Close Issue' -= render 'shared/show_aside' - -.gray-content-block.second-block.oneline-block - .row - .col-md-9 - .votes-holder.pull-right - #votes= render 'votes/votes_block', votable: @issue - = render "shared/issuable/participants" - .col-md-3 - .input-group.cross-project-reference - %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @issue) - = clipboard_button(clipboard_target: 'span#cross-project-reference') - -.row - %section.col-md-9 - .voting_notes#notes= render 'projects/notes/notes_with_form' - %aside.col-md-3 - .issuable-affix - .context - = render 'shared/issuable/context', issuable: @issue +#notes + = render 'projects/notes/notes_with_form' diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 38254403fa6..79c9ef6b3c3 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -3,13 +3,13 @@ .issue .issue-details.issuable-details - .page-title + .issuable-title .issue-box{ class: issue_box_class(@issue) } - if @issue.closed? Closed - else Open - %span.issue-id Issue ##{@issue.iid} + %span.issuable-id Issue ##{@issue.iid} %span.creator · opened by #{link_to_member(@project, @issue.author, size: 24)} @@ -36,22 +36,31 @@ = icon('pencil-square-o') Edit - .gray-content-block.middle-block - %h2.issue-title - = markdown escape_once(@issue.title), pipeline: :single_line - %div - - if @issue.description.present? - .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} - .wiki - = preserve do - = markdown(@issue.description, cache_key: [@issue, "description"]) - %textarea.hidden.js-task-list-field - = @issue.description - - .merge-requests - = render 'merge_requests' - - - if @closed_by_merge_requests.present? - = render 'projects/issues/closed_by_box' - .issue-discussion - = render 'projects/issues/discussion' + .row + %section.col-md-9 + .gray-content-block + %h2.issue-title + = markdown escape_once(@issue.title), pipeline: :single_line + %div + - if @issue.description.present? + .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} + .wiki + = preserve do + = markdown(@issue.description, cache_key: [@issue, "description"]) + %textarea.hidden.js-task-list-field + = @issue.description + + .merge-requests + = render 'merge_requests' + + = render 'votes/votes_block', votable: @issue + + - if @closed_by_merge_requests.present? + = render 'projects/issues/closed_by_box' + .issue-discussion + = render 'projects/issues/discussion' + + %aside.col-md-3 + = render 'shared/issuable/sidebar', issuable: @issue + + = render 'shared/show_aside' diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index b7735aaf3c1..2f0f3fcfb06 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -1,3 +1,3 @@ -$('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @issue)}"); -$('.context').effect('highlight') +$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}"); +$('.issuable-sidebar').parent().effect('highlight') new Issue(); diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml deleted file mode 100644 index 2aa46662613..00000000000 --- a/app/views/shared/issuable/_context.html.haml +++ /dev/null @@ -1,57 +0,0 @@ -= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f| - %div.prepend-top-default - .issuable-context-title - %label - Assignee: - - if issuable.assignee - %strong= link_to_member(@project, issuable.assignee, size: 24) - - else - none - .issuable-context-selectbox - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) - - %div.prepend-top-default.clearfix - .issuable-context-title - %label - Milestone: - - if issuable.milestone - %span.back-to-milestone - = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do - %strong - = icon('clock-o') - = issuable.milestone.title - - else - none - .issuable-context-selectbox - - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) - = hidden_field_tag :issuable_context - = f.submit class: 'btn hide' - - - if issuable.labels.any? - %div.prepend-top-default.clearfix - .issuable-context-title - %label Labels - .issuable-show-labels - - issuable.labels.each do |label| - = link_to_label(label) - - - if current_user - - subscribed = issuable.subscribed?(current_user) - %div.prepend-top-default.clearfix - .issuable-context-title - %label Subscription - - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - .subscription-status{data: {status: subscribtion_status}} - .description-block.unsubscribed{class: ( 'hidden' if subscribed )} - You're not receiving notifications from this thread. - .description-block.subscribed{class: ( 'hidden' unless subscribed )} - You're receiving notifications because you're subscribed to this thread. - %button.btn.btn-block.subscribe-button{:type => 'button'} - = icon('eye') - %span= subscribed ? 'Unsubscribe' : 'Subscribe' - -:javascript - new Subscription("#{toggle_subscription_path(issuable)}"); - new IssuableContext(); diff --git a/app/views/shared/issuable/_participants.html.haml b/app/views/shared/issuable/_participants.html.haml index b4e0def48b6..da6bacbb74a 100644 --- a/app/views/shared/issuable/_participants.html.haml +++ b/app/views/shared/issuable/_participants.html.haml @@ -1,5 +1,5 @@ -.participants - %span - = pluralize @participants.count, "participant" - - @participants.each do |participant| +.block.participants + .title + = pluralize participants.count, "participant" + - participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) -- cgit v1.2.1 From aa5bf4097b97bbb8d644c87e552d2a60a860a3f6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:04:34 +0100 Subject: Make edit link on issuable sidebar works Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/issuable_context.js.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index c4d3e619f5e..02232698bc2 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -5,9 +5,9 @@ class @IssuableContext new UsersSelect() $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}) - $(".context .inline-update").on "change", "select", -> + $(".issuable-sidebar .inline-update").on "change", "select", -> $(this).submit() - $(".context .inline-update").on "change", ".js-assignee", -> + $(".issuable-sidebar .inline-update").on "change", ".js-assignee", -> $(this).submit() $('.issuable-details').waitForImages -> @@ -21,3 +21,9 @@ class @IssuableContext @top = ($('.issuable-affix').offset().top - 70) bottom: -> @bottom = $('.footer').outerHeight(true) + + $(".edit-link").click (e) -> + block = $(@).parents('.block') + block.find('.selectbox').show() + block.find('.value').hide() + block.find('.js-select2').select2("open") -- cgit v1.2.1 From c6c244315a4a0959894cf24aa931e4f027d02c3b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:04:58 +0100 Subject: Implement new sidebar for merge request page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/common.scss | 6 + app/assets/stylesheets/framework/selects.scss | 2 +- app/assets/stylesheets/pages/merge_requests.scss | 4 - app/controllers/projects/issues_controller.rb | 1 - .../projects/merge_requests_controller.rb | 2 - .../projects/merge_requests/_discussion.html.haml | 22 +--- app/views/projects/merge_requests/_show.html.haml | 127 +++++++++++---------- .../projects/merge_requests/show/_mr_box.html.haml | 2 + .../merge_requests/show/_mr_title.html.haml | 4 +- app/views/projects/merge_requests/update.js.haml | 4 +- 10 files changed, 82 insertions(+), 92 deletions(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 2e8515668f6..88da799ee2b 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -461,3 +461,9 @@ table { visibility: hidden; } } + +.content-separator { + margin-left: -$gl-padding; + margin-right: -$gl-padding; + border-top: 1px solid $border-color; +} diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index c01e1e32e41..af145191bc8 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -181,4 +181,4 @@ .ajax-users-dropdown { min-width: 250px !important; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index fc8c7161991..502e9552acd 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -121,10 +121,6 @@ } } -.merge-request-details { - margin-bottom: $gl-padding; -} - .mr_source_commit, .mr_target_commit { .commit { diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index cf617d53ed6..b59b52291fb 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -58,7 +58,6 @@ class Projects::IssuesController < Projects::ApplicationController end def show - @participants = @issue.participants(current_user) @note = @project.notes.new(noteable: @issue) @notes = @issue.notes.nonawards.with_associations.fresh @noteable = @issue diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 530f3d3dcb8..e8fa10fafb1 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -279,8 +279,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def define_show_vars - @participants = @merge_request.participants(current_user) - # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index d64b19ae91a..399e9cc1e1b 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -5,24 +5,4 @@ - if @merge_request.closed? = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" -= render 'shared/show_aside' - -.gray-content-block.middle-block.oneline-block - .row - .col-md-9 - .votes-holder.pull-right - #votes= render 'votes/votes_block', votable: @merge_request - = render "shared/issuable/participants" - .col-md-3 - .input-group.cross-project-reference - %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) - = clipboard_button(clipboard_target: 'span#cross-project-reference') - -.row - %section.col-md-9 - .voting_notes#notes= render "projects/notes/notes_with_form" - %aside.col-md-3 - .issuable-affix - .context - = render 'shared/issuable/context', issuable: @merge_request +#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 960d1561e73..b7b2859f95b 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -7,71 +7,80 @@ .merge-request{'data-url' => merge_request_path(@merge_request)} .merge-request-details.issuable-details = render "projects/merge_requests/show/mr_title" - = render "projects/merge_requests/show/mr_box" - .append-bottom-default.mr-source-target.prepend-top-default - - if @merge_request.open? - .pull-right - - if @merge_request.source_branch_exists? - = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do - = icon('cloud-download fw') - Check out branch + .row + %section.col-md-9 + = render "projects/merge_requests/show/mr_box" + .append-bottom-default.mr-source-target.prepend-top-default + - if @merge_request.open? + .pull-right + - if @merge_request.source_branch_exists? + = link_to "#modal_merge_info", class: "btn btn-sm", "data-toggle" => "modal" do + = icon('cloud-download fw') + Check out branch - %span.dropdown - %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} } - = icon('download') - Download as - %span.caret - %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) - .normal - %span Request to merge - %span.label-branch= source_branch_with_namespace(@merge_request) - %span into - = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do - = @merge_request.target_branch + %span.dropdown + %a.btn.btn-sm.dropdown-toggle{ data: {toggle: :dropdown} } + = icon('download') + Download as + %span.caret + %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) + .normal + %span Request to merge + %span.label-branch= source_branch_with_namespace(@merge_request) + %span into + = link_to namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch), class: "label-branch" do + = @merge_request.target_branch - = render "projects/merge_requests/show/how_to_merge" - = render "projects/merge_requests/widget/show.html.haml" + = render "projects/merge_requests/show/how_to_merge" + = render "projects/merge_requests/widget/show.html.haml" - - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) - .light.prepend-top-default - You can also accept this merge request manually using the - = succeed '.' do - = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" + - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) + .light.prepend-top-default + You can also accept this merge request manually using the + = succeed '.' do + = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal" - - if @commits.present? - %ul.merge-request-tabs.center-top-menu.no-top.no-bottom - %li.notes-tab - = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do - Discussion - %span.badge= @merge_request.mr_and_commit_notes.user.count - %li.commits-tab - = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do - Commits - %span.badge= @commits.size - %li.diffs-tab - = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do - Changes - %span.badge= @merge_request.diffs.size - - if @ci_commit - %li.builds-tab - = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do - Builds - %span.badge= @statuses.size + - if @commits.present? + %ul.merge-request-tabs.center-top-menu.no-top.no-bottom + %li.notes-tab + = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do + Discussion + %span.badge= @merge_request.mr_and_commit_notes.user.count + %li.commits-tab + = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do + Commits + %span.badge= @commits.size + %li.diffs-tab + = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'} do + Changes + %span.badge= @merge_request.diffs.size + - if @ci_commit + %li.builds-tab + = link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do + Builds + %span.badge= @statuses.size - .tab-content - #notes.notes.tab-pane.voting_notes - = render "projects/merge_requests/discussion" - #commits.commits.tab-pane - - # This tab is always loaded via AJAX - #diffs.diffs.tab-pane - - # This tab is always loaded via AJAX - #builds.builds.tab-pane - - # This tab is always loaded via AJAX + .tab-content + #notes.notes.tab-pane.voting_notes + .content-separator + = render "projects/merge_requests/discussion" + #commits.commits.tab-pane + - # This tab is always loaded via AJAX + #diffs.diffs.tab-pane + - # This tab is always loaded via AJAX + #builds.builds.tab-pane + - # This tab is always loaded via AJAX + + .mr-loading-status + = spinner + + %aside.col-md-3 + = render 'shared/issuable/sidebar', issuable: @merge_request + + = render 'shared/show_aside' - .mr-loading-status - = spinner :javascript var merge_request; diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 9bfe202589e..66433c6d4fe 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -10,3 +10,5 @@ = markdown(@merge_request.description, cache_key: [@merge_request, "description"]) %textarea.hidden.js-task-list-field = @merge_request.description + + = render 'votes/votes_block', votable: @merge_request diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 4dfe46e2b86..d65c3b16618 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,7 +1,7 @@ -.page-title +.issuable-title .issue-box{ class: issue_box_class(@merge_request) } = @merge_request.state_human_name - %span.issue-id Merge Request ##{@merge_request.iid} + %span.issuable-id Merge Request ##{@merge_request.iid} %span.creator · opened by #{link_to_member(@project, @merge_request.author, size: 24)} diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml index 25583b2cc6f..93db65ddf79 100644 --- a/app/views/projects/merge_requests/update.js.haml +++ b/app/views/projects/merge_requests/update.js.haml @@ -1,3 +1,3 @@ -$('.context').html("#{escape_javascript(render 'shared/issuable/context', issuable: @merge_request)}"); -$('.context').effect('highlight') +$('.issuable-sidebar').html("#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}"); +$('.issuable-sidebar').parent().effect('highlight') merge_request = new MergeRequest(); -- cgit v1.2.1 From 6117ae98da6fe9d3c5cf055546c82a97b46c87a6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 19:05:39 +0100 Subject: Update changelog Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index cc6d19063fa..09e3382becc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -47,6 +47,7 @@ v 8.3.0 (unreleased) - Fix award-emojis Flash alert's width - Fix deleting notes on a merge request diff - Display referenced merge request statuses in the issue description (Greg Smethells) + - Implement new sidebar for issue and merge request pages v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 617ba01322b0d2d8fbe6a543b2e2393b8178089e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 10 Dec 2015 21:06:26 +0100 Subject: Fix tests for new issuable sidebar Signed-off-by: Dmitriy Zaporozhets --- app/views/shared/issuable/_sidebar.html.haml | 17 ++++++++++------- spec/features/issues_spec.rb | 27 ++++++++++++++++++++------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 10f1aaf8c31..cbd01c7d4fd 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -8,7 +8,7 @@ = cross_project_reference(@project, issuable) = clipboard_button(clipboard_target: 'span#cross-project-reference') - .block + .block.assignee .title %label Assignee: @@ -19,12 +19,12 @@ - if issuable.assignee %strong= link_to_member(@project, issuable.assignee, size: 24) - else - none + None .selectbox = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) - .block + .block.milestone .title %label Milestone: @@ -39,13 +39,13 @@ = icon('clock-o') = issuable.milestone.title - else - none + None .selectbox = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) = hidden_field_tag :issuable_context = f.submit class: 'btn hide' - - if issuable.labels.any? + - if issuable.project.labels.any? .block .title %label Labels @@ -53,8 +53,11 @@ .pull-right = link_to 'Edit', '#', class: 'edit-link' .value.issuable-show-labels - - issuable.labels.each do |label| - = link_to_label(label) + - if issuable.labels.any? + - issuable.labels.each do |label| + = link_to_label(label) + - else + None .selectbox = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 3d6d87e764a..0cad8c78ad2 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -69,7 +69,10 @@ describe 'Issues', feature: true do click_button 'Save changes' - expect(page).to have_content 'Assignee: none' + page.within('.assignee') do + expect(page).to have_content 'None' + end + expect(issue.reload.assignee).to be_nil end end @@ -202,7 +205,7 @@ describe 'Issues', feature: true do it 'with dropdown menu' do visit namespace_project_issue_path(project.namespace, project, issue) - find('.context #issue_assignee_id'). + find('.issuable-sidebar #issue_assignee_id'). set project.team.members.first.id click_button 'Update Issue' @@ -241,12 +244,16 @@ describe 'Issues', feature: true do it 'with dropdown menu' do visit namespace_project_issue_path(project.namespace, project, issue) - find('.context'). + find('.issuable-sidebar'). select(milestone.title, from: 'issue_milestone_id') click_button 'Update Issue' expect(page).to have_content "Milestone changed to #{milestone.title}" - expect(page).to have_content "Milestone: #{milestone.title}" + + page.within('.milestone') do + expect(page).to have_content milestone.title + end + has_select?('issue_assignee_id', selected: milestone.title) end end @@ -279,13 +286,19 @@ describe 'Issues', feature: true do it 'allows user to remove assignee', js: true do visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_content "Assignee: #{user2.name}" - first('#s2id_issue_assignee_id').click + page.within('.assignee') do + expect(page).to have_content user2.name + end + + find('.assignee .edit-link').click sleep 2 # wait for ajax stuff to complete first('.user-result').click - expect(page).to have_content 'Assignee: none' + page.within('.assignee') do + expect(page).to have_content 'None' + end + sleep 2 # wait for ajax stuff to complete expect(issue.reload.assignee).to be_nil end -- cgit v1.2.1 From b4ea6ad16a63acfcf581f1e6e2f04f811a5efc90 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Dec 2015 19:38:18 +0100 Subject: Few UI improvements to new sidebar implementation Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/issuable_context.js.coffee | 2 +- app/assets/stylesheets/pages/issuable.scss | 7 ++++++- app/views/shared/issuable/_sidebar.html.haml | 12 ++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/issuable_context.js.coffee b/app/assets/javascripts/issuable_context.js.coffee index 02232698bc2..01bd515cc02 100644 --- a/app/assets/javascripts/issuable_context.js.coffee +++ b/app/assets/javascripts/issuable_context.js.coffee @@ -18,7 +18,7 @@ class @IssuableContext $('.issuable-affix').affix offset: top: -> - @top = ($('.issuable-affix').offset().top - 70) + @top = ($('.issuable-affix').offset().top - 60) bottom: -> @bottom = $('.footer').outerHeight(true) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 35af92e7df3..1f1d720ae4b 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -18,7 +18,7 @@ &.affix { position: fixed; - top: 70px; + top: 60px; margin-right: 35px; } } @@ -129,6 +129,11 @@ .btn-clipboard { color: $gl-gray; } + + .participants .avatar { + margin-top: 6px; + margin-right: 2px; + } } .issuable-title { diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index cbd01c7d4fd..0019f739b89 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -4,14 +4,14 @@ .title Cross-project reference .cross-project-reference - %span#cross-project-reference.has_tooltip{title: 'Cross-project reference'} + %span#cross-project-reference = cross_project_reference(@project, issuable) = clipboard_button(clipboard_target: 'span#cross-project-reference') .block.assignee .title %label - Assignee: + Assignee - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .pull-right = link_to 'Edit', '#', class: 'edit-link' @@ -19,7 +19,7 @@ - if issuable.assignee %strong= link_to_member(@project, issuable.assignee, size: 24) - else - None + .light None .selectbox = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true, first_user: true) @@ -27,7 +27,7 @@ .block.milestone .title %label - Milestone: + Milestone - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) .pull-right = link_to 'Edit', '#', class: 'edit-link' @@ -39,7 +39,7 @@ = icon('clock-o') = issuable.milestone.title - else - None + .light None .selectbox = f.select(:milestone_id, milestone_options(issuable), { include_blank: true }, { class: 'select2 select2-compact js-select2 js-milestone', data: { placeholder: 'Select milestone' }}) = hidden_field_tag :issuable_context @@ -57,7 +57,7 @@ - issuable.labels.each do |label| = link_to_label(label) - else - None + .light None .selectbox = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } -- cgit v1.2.1 From 1911811438606e3ca8c6629ec2900afc5ba1e11a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Dec 2015 19:48:11 +0100 Subject: Move awards back to gray panel and few improvements to sidebar Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/awards.scss | 1 - app/assets/stylesheets/pages/issuable.scss | 1 + app/views/projects/issues/_discussion.html.haml | 3 +++ app/views/projects/issues/show.html.haml | 2 -- app/views/projects/merge_requests/_discussion.html.haml | 3 +++ app/views/projects/merge_requests/_show.html.haml | 1 - app/views/projects/merge_requests/show/_mr_box.html.haml | 2 -- 7 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 895bafebcef..f6d85221484 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -1,7 +1,6 @@ .awards { @include clearfix; line-height: 34px; - margin-top: $gl-padding; .award { @include border-radius(5px); diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 1f1d720ae4b..797a0af3720 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -57,6 +57,7 @@ margin-right: 1px; } + .issue-discussion > .gray-content-block, > .gray-content-block { margin-top: 0; border-top: none; diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index 86d3dc546ba..405bae1bbb9 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -5,5 +5,8 @@ - else = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-grouped btn-close js-note-target-close', title: 'Close Issue' +.gray-content-block.second-block.oneline-block + = render 'votes/votes_block', votable: @issue + #notes = render 'projects/notes/notes_with_form' diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 79c9ef6b3c3..cc2cf8c8716 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -53,8 +53,6 @@ .merge-requests = render 'merge_requests' - = render 'votes/votes_block', votable: @issue - - if @closed_by_merge_requests.present? = render 'projects/issues/closed_by_box' .issue-discussion diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index 399e9cc1e1b..7a7428d35cc 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -5,4 +5,7 @@ - if @merge_request.closed? = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" +.gray-content-block.second-block.oneline-block + = render 'votes/votes_block', votable: @merge_request + #notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index b7b2859f95b..04f8fd74422 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -64,7 +64,6 @@ .tab-content #notes.notes.tab-pane.voting_notes - .content-separator = render "projects/merge_requests/discussion" #commits.commits.tab-pane - # This tab is always loaded via AJAX diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 66433c6d4fe..9bfe202589e 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -10,5 +10,3 @@ = markdown(@merge_request.description, cache_key: [@merge_request, "description"]) %textarea.hidden.js-task-list-field = @merge_request.description - - = render 'votes/votes_block', votable: @merge_request -- cgit v1.2.1 From 7cbcc21dcc4e38285dd9d097007df0979653bfe4 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Dec 2015 20:03:58 +0100 Subject: Fix tests Signed-off-by: Dmitriy Zaporozhets --- spec/features/issues_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 0cad8c78ad2..a2fb3e4c75d 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -209,7 +209,7 @@ describe 'Issues', feature: true do set project.team.members.first.id click_button 'Update Issue' - expect(page).to have_content 'Assignee:' + expect(page).to have_content 'Assignee' has_select?('issue_assignee_id', selected: project.team.members.first.name) end -- cgit v1.2.1 From d86e2f33a8b80c5da30f26a320f542c1ecc5c540 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Dec 2015 20:33:24 +0100 Subject: Increase fixed layout width to 1280px Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/variables.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 91954683c3e..2ef40a6e517 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -19,7 +19,7 @@ $border-color: #dce0e6; $table-border-color: #eef0f2; $background-color: #F7F8FA; $header-height: 58px; -$fixed-layout-width: 1200px; +$fixed-layout-width: 1280px; $gl-gray: #7f8fa4; $gl-padding: 16px; $gl-avatar-size: 46px; -- cgit v1.2.1 From 51c8d037d37f19fdbf4ad89e1a1c39da9ca4f150 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 11 Dec 2015 20:39:26 +0100 Subject: Make profile navigation full wide Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/framework/common.scss | 5 +++++ app/views/users/show.html.haml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 88da799ee2b..7562ef6d24b 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -401,6 +401,11 @@ table { border-bottom: 1px solid $border-color; height: 57px; } + + &.wide { + margin-left: -$gl-padding; + margin-right: -$gl-padding; + } } .center-middle-menu { diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index a0a6e2d9810..b7a7eb4e6f7 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -73,7 +73,7 @@ .user-calendar-activities -%ul.center-top-menu.no-top.no-bottom.bottom-border +%ul.center-top-menu.no-top.no-bottom.bottom-border.wide %li.active = link_to "#activity", 'data-toggle' => 'tab' do Activity -- cgit v1.2.1 From e6e27f03d77fa57b82f9189f45be7eec1ffe37ac Mon Sep 17 00:00:00 2001 From: Ben Bodenmiller Date: Fri, 11 Dec 2015 23:16:37 +0000 Subject: add details on how to change saml button label --- doc/integration/saml.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/integration/saml.md b/doc/integration/saml.md index 1b8c28dd0f4..1632e42f701 100644 --- a/doc/integration/saml.md +++ b/doc/integration/saml.md @@ -38,7 +38,8 @@ First configure SAML 2.0 support in GitLab, then register the GitLab application idp_sso_target_url: 'https://login.example.com/idp', issuer: 'https://gitlab.example.com', name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' - } + }, + "label" => "Company Login" # optional label for SAML login button, defaults to "Saml" } ] ``` @@ -79,4 +80,4 @@ On the sign in page there should now be a SAML button below the regular sign in If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user. -Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. +Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username. \ No newline at end of file -- cgit v1.2.1 From 3efae53bd79db118463bfaeceb209bc91f63bd0b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 11 Dec 2015 23:17:36 -0800 Subject: Add open_issues_count to project API This is needed to support Huboard and a generally useful value. --- CHANGELOG | 1 + app/models/project.rb | 4 ++++ doc/api/projects.md | 3 +++ lib/api/entities.rb | 1 + spec/models/project_spec.rb | 6 +++++- spec/requests/api/projects_spec.rb | 16 ++++++++++++++++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index e38c8b363e7..ae544c08c79 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.3.0 (unreleased) + - Add open_issues_count to project API (Stan Hu) - Expand character set of usernames created by Omniauth (Corey Hinshaw) - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg) - Merge when build succeeds (Zeger-Jan van de Weg) diff --git a/app/models/project.rb b/app/models/project.rb index e78868af1cc..6756e45caa8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -825,4 +825,8 @@ class Project < ActiveRecord::Base forked_project_link.destroy end end + + def open_issues_count + issues.opened.count + end end diff --git a/doc/api/projects.md b/doc/api/projects.md index 43a50a9a810..15956fe6df2 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -59,6 +59,7 @@ Parameters: "path": "diaspora-client", "path_with_namespace": "diaspora/diaspora-client", "issues_enabled": true, + "open_issues_count": 1, "merge_requests_enabled": true, "builds_enabled": true, "wiki_enabled": true, @@ -101,6 +102,7 @@ Parameters: "path": "puppet", "path_with_namespace": "brightbox/puppet", "issues_enabled": true, + "open_issues_count": 1, "merge_requests_enabled": true, "builds_enabled": true, "wiki_enabled": true, @@ -192,6 +194,7 @@ Parameters: "path": "diaspora-project-site", "path_with_namespace": "diaspora/diaspora-project-site", "issues_enabled": true, + "open_issues_count": 1, "merge_requests_enabled": true, "builds_enabled": true, "wiki_enabled": true, diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 81bf7a8222b..014116ef130 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -68,6 +68,7 @@ module API expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? } expose :avatar_url expose :star_count, :forks_count + expose :open_issues_count, if: lambda { | project, options | project.issues_enabled? && project.default_issues_tracker? } end class ProjectMember < UserBasic diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 6ddb0e2b8f7..37ac0495154 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -153,13 +153,17 @@ describe Project, models: true do describe '#get_issue' do let(:project) { create(:empty_project) } - let(:issue) { create(:issue, project: project) } + let!(:issue) { create(:issue, project: project) } context 'with default issues tracker' do it 'returns an issue' do expect(project.get_issue(issue.iid)).to eq issue end + it 'returns count of open issues' do + expect(project.open_issues_count).to eq(1) + end + it 'returns nil when no issue found' do expect(project.get_issue(999)).to be_nil end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 24b765f4979..55a7b1a95f5 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -65,6 +65,22 @@ describe API::API, api: true do expect(json_response.first.keys).to include('tag_list') end + it 'should include open_issues_count' do + get api('/projects', user) + expect(response.status).to eq 200 + expect(json_response).to be_an Array + expect(json_response.first.keys).to include('open_issues_count') + end + + it 'should not include open_issues_count' do + project.update_attributes( { issues_enabled: false } ) + + get api('/projects', user) + expect(response.status).to eq 200 + expect(json_response).to be_an Array + expect(json_response.first.keys).not_to include('open_issues_count') + end + context 'and using search' do it 'should return searched project' do get api('/projects', user), { search: project.name } -- cgit v1.2.1 From 0b60723d1cdef3804ae052defcb77e2641f22dcb Mon Sep 17 00:00:00 2001 From: Pelle Date: Sat, 12 Dec 2015 09:48:32 +0000 Subject: Fix wording on runner setup page --- app/views/projects/runners/_shared_runners.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml index 316ea747b14..8be3d379828 100644 --- a/app/views/projects/runners/_shared_runners.html.haml +++ b/app/views/projects/runners/_shared_runners.html.haml @@ -12,8 +12,8 @@   for this project - if @shared_runners_count.zero? - This application has no shared runners yet. - Please use specific runners or ask administrator to create one + This GitLab server does not provide any shared runners yet. + Please use specific runners or ask the administrator to create one. - else %h4.underlined-title Available shared runners - #{@shared_runners_count} %ul.bordered-list.available-shared-runners -- cgit v1.2.1 From 9c5d8079a367ac24d04466f03f6b9abf5c333f59 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 24 Nov 2015 08:28:18 -0800 Subject: Bump Redis requirement to 2.8 for Sidekiq 4 requirements Closes #3649 [ci skip] --- CHANGELOG | 1 + README.md | 2 +- doc/install/installation.md | 22 +++++++++++++++++++--- lib/tasks/gitlab/check.rake | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 09e3382becc..7359bda421b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,7 @@ v 8.3.0 (unreleased) - Update project repositorize size and commit count during import:repos task (Stan Hu) - Fix API setting of 'public' attribute to false will make a project private (Stan Hu) - Handle and report SSL errors in Web hook test (Stan Hu) + - Bump Redis requirement to 2.8 for Sidekiq 4 (Stan Hu) - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera) - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg) - Fix 500 error when update group member permission diff --git a/README.md b/README.md index c459e67baa1..3ec1d4a776c 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,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.4+ +- Redis 2.8+ - MySQL or PostgreSQL For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html). diff --git a/doc/install/installation.md b/doc/install/installation.md index 618391e16d2..0a19a060a9a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -62,7 +62,7 @@ up-to-date and install it. Install the required packages (needed to compile Ruby and native extensions to Ruby gems): - sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake nodejs + sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake nodejs If you want to use Kerberos for user authentication, then install libkrb5-dev: @@ -174,7 +174,23 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ## 6. Redis - sudo apt-get install redis-server +As of this writing, most Debian/Ubuntu distributions ship with Redis 2.2 or +2.4. GitLab requires at least Redis 2.8. If your platform doesn't provide +this, the following instructions cover building and installing Redis from +scratch. + +Ubuntu users [can also use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server) +to install a recent version of Redis. + + # Build Redis + wget http://download.redis.io/releases/redis-2.8.23.tar.gz + tar xzf redis-2.8.23.tar.gz + cd redis-2.8.23 + make + + # Install Redis + cd utils + sudo ./install_server.sh # Configure redis to use sockets sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig @@ -197,7 +213,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da fi # Activate the changes to redis.conf - sudo service redis-server restart + sudo service redis_6379 start # Add git to the redis group sudo usermod -aG redis git diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index a25fac62cfc..b5af3d88b4c 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -331,7 +331,7 @@ namespace :gitlab do end def check_redis_version - min_redis_version = "2.4.0" + min_redis_version = "2.8.0" print "Redis version >= #{min_redis_version}? ... " redis_version = run(%W(redis-cli --version)) -- cgit v1.2.1 From 118d96906ae3923206ca91ca9ccd3c5bc6c2fd3a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 12 Dec 2015 12:38:12 -0500 Subject: Fix note polling Closes #4032 --- app/controllers/projects/notes_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 88b949a27ab..ae6e9f6fd38 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -13,7 +13,8 @@ class Projects::NotesController < Projects::ApplicationController @notes.each do |note| notes_json[:notes] << { id: note.id, - html: note_to_html(note) + html: note_to_html(note), + valid: note.valid? } end -- cgit v1.2.1 From 3084c8c37064d7309ac4ca7b9ecb24ca8c625d24 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 12 Dec 2015 13:18:51 -0500 Subject: Define CI status icon colors in SCSS instead of a helper --- app/assets/stylesheets/pages/status.scss | 17 +++++++++++++++++ app/helpers/ci_status_helper.rb | 22 ++++------------------ spec/helpers/ci_status_helper_spec.rb | 11 +++-------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index a7d3b2197f1..4b6ef035673 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -35,3 +35,20 @@ border-color: $gl-warning; } } + +.ci-status-icon-success { + @extend .cgreen; +} +.ci-status-icon-failed { + @extend .cred; +} +.ci-status-icon-running, +.ci-status-icon-pending { + // These are standard text color +} +.ci-status-icon-canceled, +.ci-status-icon-disabled, +.ci-status-icon-not-found, +.ci-status-icon-skipped { + @extend .cgray; +} diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f8f2cbf1319..fe5cad54a30 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -12,19 +12,6 @@ module CiStatusHelper ci_label_for_status(ci_commit.status) end - def ci_status_color(ci_commit) - case ci_commit.status - when 'success' - 'green' - when 'failed' - 'red' - when 'running', 'pending' - 'yellow' - else - 'gray' - end - end - def ci_status_with_icon(status) content_tag :span, class: "ci-status ci-#{status}" do ci_icon_for_status(status) + ' '.html_safe + ci_label_for_status(status) @@ -56,11 +43,10 @@ module CiStatusHelper end def render_ci_status(ci_commit) - link_to ci_status_path(ci_commit), - class: "c#{ci_status_color(ci_commit)}", + link_to ci_status_icon(ci_commit), + ci_status_path(ci_commit), + class: "ci-status-icon-#{ci_commit.status.dasherize}", title: "Build #{ci_status_label(ci_commit)}", - data: { toggle: 'tooltip', placement: 'left' } do - ci_status_icon(ci_commit) - end + data: { toggle: 'tooltip', placement: 'left' } end end diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb index 7fc53eb1472..4f8d9c67262 100644 --- a/spec/helpers/ci_status_helper_spec.rb +++ b/spec/helpers/ci_status_helper_spec.rb @@ -6,13 +6,8 @@ describe CiStatusHelper do let(:success_commit) { double("Ci::Commit", status: 'success') } let(:failed_commit) { double("Ci::Commit", status: 'failed') } - describe 'ci_status_color' do - it { expect(ci_status_icon(success_commit)).to include('fa-check') } - it { expect(ci_status_icon(failed_commit)).to include('fa-close') } - end - - describe 'ci_status_color' do - it { expect(ci_status_color(success_commit)).to eq('green') } - it { expect(ci_status_color(failed_commit)).to eq('red') } + describe 'ci_status_icon' do + it { expect(helper.ci_status_icon(success_commit)).to include('fa-check') } + it { expect(helper.ci_status_icon(failed_commit)).to include('fa-close') } end end -- cgit v1.2.1 From 08e67983bef013e0aeeff14741c0fc1b01c470e9 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sun, 13 Dec 2015 01:20:38 -0200 Subject: Update rerun to remove celluloid as dependency --- Gemfile | 2 +- Gemfile.lock | 29 ++++------------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/Gemfile b/Gemfile index 7298e21ce66..3391bd38dd5 100644 --- a/Gemfile +++ b/Gemfile @@ -215,7 +215,7 @@ group :development do gem "annotate", "~> 2.6.0" gem "letter_opener", '~> 1.1.2' gem 'quiet_assets', '~> 1.0.2' - gem 'rerun', '~> 0.10.0' + gem 'rerun', '~> 0.11.0' gem 'bullet', require: false gem 'rblineprof', platform: :mri, require: false gem 'web-console', '~> 2.0' diff --git a/Gemfile.lock b/Gemfile.lock index ff57460f5bb..7dbf346654a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -117,23 +117,6 @@ GEM activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) - celluloid (0.17.2) - celluloid-essentials - celluloid-extras - celluloid-fsm - celluloid-pool - celluloid-supervision - timers (>= 4.1.1) - celluloid-essentials (0.20.5) - timers (>= 4.1.1) - celluloid-extras (0.20.5) - timers (>= 4.1.1) - celluloid-fsm (0.20.5) - timers (>= 4.1.1) - celluloid-pool (0.20.5) - timers (>= 4.1.1) - celluloid-supervision (0.20.5) - timers (>= 4.1.1) charlock_holmes (0.7.3) chunky_png (1.3.5) cliver (0.3.2) @@ -369,7 +352,6 @@ GEM hipchat (1.5.2) httparty mimemagic - hitimes (1.2.3) html-pipeline (1.11.0) activesupport (>= 2) nokogiri (~> 1.4) @@ -410,8 +392,7 @@ GEM addressable (~> 2.3) letter_opener (1.1.2) launchy (~> 2.2) - listen (2.9.0) - celluloid (>= 0.15.2) + listen (3.0.5) rb-fsevent (>= 0.9.3) rb-inotify (>= 0.9) loofah (2.0.3) @@ -601,8 +582,8 @@ GEM redis-store (1.1.7) redis (>= 2.2) request_store (1.2.1) - rerun (0.10.0) - listen (~> 2.7, >= 2.7.3) + rerun (0.11.0) + listen (~> 3.0) responders (2.1.0) railties (>= 4.2.0, < 5) rest-client (1.8.0) @@ -758,8 +739,6 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) - timers (4.1.1) - hitimes timfel-krb5-auth (0.8.3) tinder (1.10.1) eventmachine (~> 1.0) @@ -940,7 +919,7 @@ DEPENDENCIES redis-namespace redis-rails (~> 4.0.0) request_store (~> 1.2.0) - rerun (~> 0.10.0) + rerun (~> 0.11.0) responders (~> 2.0) rouge (~> 1.10.1) rqrcode-rails3 (~> 0.1.7) -- cgit v1.2.1 From 41f5bb035bd648c6fb0023bfbfb5ae40551cb180 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 14 Dec 2015 00:15:29 -0800 Subject: Rename mention of gitlab-git-http-server to gitlab-workhorse [ci skip] Closes https://github.com/gitlabhq/gitlabhq/issues/9904 --- doc/update/8.1-to-8.2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index b08a79ca0aa..46dfa2232b4 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -142,7 +142,7 @@ git diff origin/8-1-stable:lib/support/nginx/gitlab origin/8-2-stable:lib/suppor If you are using Apache instead of NGINX please see the updated [Apache templates]. Also note that because Apache does not support upstreams behind Unix sockets you -will need to let gitlab-git-http-server listen on a TCP port. You can do this +will need to let gitlab-workhorse listen on a TCP port. You can do this via [/etc/default/gitlab]. [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache -- cgit v1.2.1 From 00155b6c694d7d9a016b532694f5049b70e9a4db Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 8 Dec 2015 11:02:13 +0100 Subject: Suppress warning about missing `.gitlab-ci.yml` if builds are disabled When user disables GitLab Ci Service in project's settings then warning about missing `.gitlab-ci.yml` file should be supressed. This a matter of user experience as stated in #3761 (closes #3761). --- spec/features/commits_spec.rb | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 90739cd6a28..130a5016b55 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -60,15 +60,29 @@ describe "Commits" do end describe ".gitlab-ci.yml not found warning" do - it "does not show warning" do - visit ci_status_path(@commit) - expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" + context 'ci service enabled' do + it "does not show warning" do + visit ci_status_path(@commit) + expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" + end + + it "shows warning" do + stub_ci_commit_yaml_file(nil) + visit ci_status_path(@commit) + expect(page).to have_content ".gitlab-ci.yml not found in this commit" + end end - it "shows warning" do - stub_ci_commit_yaml_file(nil) - visit ci_status_path(@commit) - expect(page).to have_content ".gitlab-ci.yml not found in this commit" + context 'ci service disabled' do + before do + allow_any_instance_of(GitlabCiService).to receive(:active).and_return(false) + stub_ci_commit_yaml_file(nil) + visit ci_status_path(@commit) + end + + it 'does not show warning' do + expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' + end end end end -- cgit v1.2.1 From 65fe4bb1ceed6bae05724f3494fb57e8b09fa616 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 8 Dec 2015 11:59:46 +0100 Subject: Improve gitlab ci commits specs (refactoring) This minimizes usage of instance variables in this spec, and changes double quotation marks to single when string interpolation is not being used. --- spec/features/commits_spec.rb | 70 +++++++++++++++++++-------------------- spec/support/stub_gitlab_calls.rb | 4 +++ 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 130a5016b55..80ff4d3751f 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -1,83 +1,83 @@ require 'spec_helper' -describe "Commits" do +describe 'Commits' do include CiStatusHelper let(:project) { create(:project) } - describe "CI" do + describe 'CI' do before do login_as :user project.team << [@user, :master] - @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 + project.ensure_gitlab_ci_project + stub_ci_commit_to_return_yaml_file end - before do - stub_ci_commit_to_return_yaml_file + let!(:commit) do + FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha end - describe "GET /:project/commits/:sha/ci" do + let!(:build) { FactoryGirl.create :ci_build, commit: commit } + + describe 'GET /:project/commits/:sha/ci' do before do - visit ci_status_path(@commit) + visit ci_status_path(commit) end - it { expect(page).to have_content @commit.sha[0..7] } - it { expect(page).to have_content @commit.git_commit_message } - it { expect(page).to have_content @commit.git_author_name } + it { expect(page).to have_content commit.sha[0..7] } + it { expect(page).to have_content commit.git_commit_message } + it { expect(page).to have_content commit.git_author_name } end - context "Download artifacts" do + context 'Download artifacts' do let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } before do - @build.update_attributes(artifacts_file: artifacts_file) + build.update_attributes(artifacts_file: artifacts_file) end it do - visit ci_status_path(@commit) - click_on "Download artifacts" + visit ci_status_path(commit) + click_on 'Download artifacts' expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) end end - describe "Cancel all builds" do - it "cancels commit" do - visit ci_status_path(@commit) - click_on "Cancel running" - expect(page).to have_content "canceled" + describe 'Cancel all builds' do + it 'cancels commit' do + visit ci_status_path(commit) + click_on 'Cancel running' + 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" + describe 'Cancel build' do + it 'cancels build' do + visit ci_status_path(commit) + click_on 'Cancel' + expect(page).to have_content 'canceled' end end - describe ".gitlab-ci.yml not found warning" do + describe '.gitlab-ci.yml not found warning' do context 'ci service enabled' do it "does not show warning" do - visit ci_status_path(@commit) - expect(page).not_to have_content ".gitlab-ci.yml not found in this commit" + visit ci_status_path(commit) + expect(page).not_to have_content '.gitlab-ci.yml not found in this commit' end - it "shows warning" do + it 'shows warning' do stub_ci_commit_yaml_file(nil) - visit ci_status_path(@commit) - expect(page).to have_content ".gitlab-ci.yml not found in this commit" + visit ci_status_path(commit) + expect(page).to have_content '.gitlab-ci.yml not found in this commit' end end context 'ci service disabled' do before do - allow_any_instance_of(GitlabCiService).to receive(:active).and_return(false) + stub_ci_service_disabled stub_ci_commit_yaml_file(nil) - visit ci_status_path(@commit) + visit ci_status_path(commit) end it 'does not show warning' do diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb index 5b3eb1bfc5f..90936183824 100644 --- a/spec/support/stub_gitlab_calls.rb +++ b/spec/support/stub_gitlab_calls.rb @@ -21,6 +21,10 @@ module StubGitlabCalls allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml } end + def stub_ci_service_disabled + allow_any_instance_of(GitlabCiService).to receive(:active).and_return(false) + end + private def gitlab_url -- cgit v1.2.1 From 2dafec91dd542ac641fea4750bf8fd68211a58af Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 11:03:33 +0100 Subject: Add matcher class to ci status link --- app/helpers/ci_status_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f8f2cbf1319..c202c592067 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -57,7 +57,7 @@ module CiStatusHelper def render_ci_status(ci_commit) link_to ci_status_path(ci_commit), - class: "c#{ci_status_color(ci_commit)}", + class: "ci-status-link c#{ci_status_color(ci_commit)}", title: "Build #{ci_status_label(ci_commit)}", data: { toggle: 'tooltip', placement: 'left' } do ci_status_icon(ci_commit) -- cgit v1.2.1 From b8f67c5e4735eb25a3d03daeb95900dc87692123 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 11:28:49 +0100 Subject: Do not display ci build status if builds enabled but no `.gitlab-ci.yml` Ref #3827 --- app/models/ci/commit.rb | 10 ++++++++ app/views/projects/commit/_commit_box.html.haml | 2 +- app/views/projects/commits/_commit.html.haml | 4 ++-- spec/features/commits_spec.rb | 31 ++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index 75465685e98..fca18ba79be 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -220,6 +220,16 @@ module Ci update!(committed_at: DateTime.now) end + ## + # This method checks if build status should be displayed. + # + # Build status should be available only if builds are enabled + # on project level and `.gitlab-ci.yml` file is present. + # + def show_build_status? + gl_project.builds_enabled? && ci_yaml_file + end + private def save_yaml_error(error) diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 132cdc35c94..634924db247 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -40,7 +40,7 @@ - @commit.parents.each do |parent| = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace" -- if @ci_commit +- if @ci_commit && @ci_commit.show_build_status? .pull-right = link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do = ci_status_icon(@ci_commit) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 0d64486164e..1303b27c4f3 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -9,7 +9,7 @@ - cache_key.push(ci_commit.status) if ci_commit = cache(cache_key) do - %li.commit.js-toggle-container + %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } .commit-row-title %strong.str-truncated = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" @@ -17,7 +17,7 @@ %a.text-expander.js-toggle-button ... .pull-right - - if ci_commit + - if ci_commit && ci_commit.show_build_status? = render_ci_status(ci_commit)   = clipboard_button(clipboard_text: commit.id) diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index 80ff4d3751f..f48d96c35a4 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -19,7 +19,36 @@ describe 'Commits' do let!(:build) { FactoryGirl.create :ci_build, commit: commit } - describe 'GET /:project/commits/:sha/ci' do + describe 'Project commits' do + context 'builds enabled' do + context '.gitlab-ci.yml found' do + before do + visit namespace_project_commits_path(project.namespace, project, :master) + end + + it 'should show build status' do + page.within("//li[@id='commit-#{commit.short_sha}']") do + expect(page).to have_css(".ci-status-link") + end + end + end + + context 'no .gitlab-ci.yml found' do + before do + stub_ci_commit_yaml_file(nil) + visit namespace_project_commits_path(project.namespace, project, :master) + end + + it 'should not show build status' do + page.within("//li[@id='commit-#{commit.short_sha}']") do + expect(page).to have_no_css(".ci-status-link") + end + end + end + end + end + + describe 'Commit builds' do before do visit ci_status_path(commit) end -- cgit v1.2.1 From a0c2a7b0cbb4567a1f09c4cbc400bf75df47a072 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 11:04:47 +0100 Subject: Fix migrations [ci skip] --- db/migrate/20151209144329_migrate_ci_web_hooks.rb | 5 +++-- db/migrate/20151209145909_migrate_ci_emails.rb | 4 ++-- db/migrate/20151210125232_migrate_ci_slack_service.rb | 10 +++++++--- db/migrate/20151210125927_migrate_ci_hip_chat_service.rb | 10 +++++++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb index 0293be3f6ce..825ba1973ff 100644 --- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb +++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb @@ -3,8 +3,9 @@ class MigrateCiWebHooks < ActiveRecord::Migration def up execute( - 'INSERT INTO web_hooks (url, project_id, type, created_at, updated_at, push_events, build_events) ' \ - "SELECT ci_web_hooks.url, projects.id, 'ProjectHook', ci_web_hooks.created_at, ci_web_hooks.updated_at, #{false_value}, #{true_value} FROM ci_web_hooks " \ + 'INSERT INTO web_hooks (url, project_id, type, created_at, updated_at, push_events, issues_events, merge_requests_events, tag_push_events, note_events, build_events) ' \ + "SELECT ci_web_hooks.url, projects.id, 'ProjectHook', ci_web_hooks.created_at, ci_web_hooks.updated_at, " \ + "#{false_value}, #{false_value}, #{false_value}, #{false_value}, #{false_value}, #{true_value} FROM ci_web_hooks " \ 'JOIN ci_projects ON ci_web_hooks.project_id = ci_projects.id ' \ 'JOIN projects ON ci_projects.gitlab_id = projects.id' ) diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb index 5ee11893582..202fac8e3fc 100644 --- a/db/migrate/20151209145909_migrate_ci_emails.rb +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -30,9 +30,9 @@ class MigrateCiEmails < ActiveRecord::Migration # This function returns 0 or 1 for column def convert_bool(name) - if self.postgresql? + if Gitlab::Database.postgresql? # PostgreSQL uses BOOLEAN type - "CASE WHEN #{name} IS TRUE THEN '1' ELSE '0' END;" + "CASE WHEN #{name} IS TRUE THEN '1' ELSE '0' END" else # MySQL uses TINYINT "#{name}" diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb index 4a5dfe866a5..f14efa3e95d 100644 --- a/db/migrate/20151210125232_migrate_ci_slack_service.rb +++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb @@ -4,16 +4,20 @@ class MigrateCiSlackService < ActiveRecord::Migration def up properties_query = 'SELECT properties FROM ci_services ' \ 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ - 'WHERE ci_projects.gitlab_id=services.project_id' + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active " \ + 'LIMIT 1' active_query = 'SELECT 1 FROM ci_services ' \ 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ - "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active" + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::SlackService' AND ci_services.active " \ + 'LIMIT 1' # We update the service since services are always generated for project, even if they are inactive # Activate service and migrate properties if currently the service is not active execute( - "UPDATE services SET properties=(#{properties_query}), build_events=#{true_value}, active=#{true_value} " \ + "UPDATE services SET properties=(#{properties_query}), active=#{true_value}, " \ + "push_events=#{false_value}, issues_events=#{false_value}, merge_requests_events=#{false_value}, " \ + "tag_push_events=#{false_value}, note_events=#{false_value}, build_events=#{true_value} " \ "WHERE NOT services.active AND services.type='SlackService' AND (#{active_query}) IS NOT NULL" ) diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb index 2fdaf5fb917..b9e04323576 100644 --- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb +++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb @@ -5,16 +5,20 @@ class MigrateCiHipChatService < ActiveRecord::Migration # From properties strip `hipchat_` key properties_query = "SELECT REPLACE(properties, '\"hipchat_', '\"') FROM ci_services " \ 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ - 'WHERE ci_projects.gitlab_id=services.project_id' + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipChatService' AND ci_services.active " \ + 'LIMIT 1' active_query = 'SELECT 1 FROM ci_services ' \ 'JOIN ci_projects ON ci_services.project_id=ci_projects.id ' \ - "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipchatService' AND ci_services.active" + "WHERE ci_projects.gitlab_id=services.project_id AND ci_services.type='Ci::HipChatService' AND ci_services.active " \ + 'LIMIT 1' # We update the service since services are always generated for project, even if they are inactive # Activate service and migrate properties if currently the service is not active execute( - "UPDATE services SET properties=(#{properties_query}), build_events=#{true_value}, active=#{true_value} " \ + "UPDATE services SET properties=(#{properties_query}), active=#{true_value}, " \ + "push_events=#{false_value}, issues_events=#{false_value}, merge_requests_events=#{false_value}, " \ + "tag_push_events=#{false_value}, note_events=#{false_value}, build_events=#{true_value} " \ "WHERE NOT services.active AND services.type='HipchatService' AND (#{active_query}) IS NOT NULL" ) -- cgit v1.2.1 From baa38f0dc1a0e1af84cd06a35450d772eee2d1c4 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 11:30:10 +0100 Subject: Fix runners admin view [ci skip] --- app/views/admin/runners/show.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index cd0cc2d1776..32051852dc0 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -82,7 +82,7 @@ = project.name_with_namespace %td .pull-right - = form_for [:admin, project.namespace, project, project.runner_projects.new] do |f| + = form_for [:admin, project.namespace.becomes(Namespace), project, project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: @runner.id = f.submit 'Enable', class: 'btn btn-xs' = paginate @projects -- cgit v1.2.1 From 208f99ff6c24a2dd5a69ed5666a1fd224d78c59b Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 12:03:28 +0100 Subject: Fix MySQL migration of CI emails [ci skip] --- db/migrate/20151209145909_migrate_ci_emails.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb index 202fac8e3fc..7f330a2cf0a 100644 --- a/db/migrate/20151209145909_migrate_ci_emails.rb +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -25,7 +25,11 @@ class MigrateCiEmails < ActiveRecord::Migration # This function escapes double-quotes and slash def escape_text(name) - "REPLACE(REPLACE(#{name}, '\\', '\\\\'), '\"', '\\\"')" + if Gitlab::Database.postgresql? + "REPLACE(REPLACE(#{name}, '\\', '\\\\'), '\"', '\\\"')" + else + "REPLACE(REPLACE(#{name}, '\\\\', '\\\\\\\\'), '\\\"', '\\\\\\\"')" + end end # This function returns 0 or 1 for column -- cgit v1.2.1 From 927c40ca496dcb1e7daa567ce173de044b1d3377 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 12:31:23 +0100 Subject: Fix Ci::Project migration not migrating columns that cannot be NULL [ci skip] --- db/migrate/20151210125930_migrate_ci_to_project.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb index d17b2a425f8..7dfe05174ee 100644 --- a/db/migrate/20151210125930_migrate_ci_to_project.rb +++ b/db/migrate/20151210125930_migrate_ci_to_project.rb @@ -27,11 +27,11 @@ class MigrateCiToProject < ActiveRecord::Migration def migrate_project_column(column, new_column = nil) new_column ||= column subquery = "SELECT ci_projects.#{column} FROM ci_projects WHERE projects.id = ci_projects.gitlab_id" - execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE #{new_column} IS NULL AND (#{subquery}) IS NOT NULL") + execute("UPDATE projects SET #{new_column}=(#{subquery}) WHERE (#{subquery}) IS NOT NULL") end def migrate_ci_service - subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService'" - execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE builds_enabled IS NULL AND (#{subquery}) IS NOT NULL") + subquery = "SELECT active FROM services WHERE projects.id = services.project_id AND type='GitlabCiService' LIMIT 1" + execute("UPDATE projects SET builds_enabled=(#{subquery}) WHERE (#{subquery}) IS NOT NULL") end end -- cgit v1.2.1 From cb4b4c57864867ba4aff04c38516042ef89f0bf7 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 12:45:03 +0100 Subject: Fix 500 when viewing specific runners on runners page [ci skip] --- app/views/admin/runners/show.html.haml | 2 +- app/views/projects/runners/_runner.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 32051852dc0..8700b4820cd 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -60,7 +60,7 @@ = project.name_with_namespace %td .pull-right - = link_to 'Disable', [:admin, project.namespace, project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' + = link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-xs' %table.table %thead diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 4d95afc28bb..47ec420189d 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -18,7 +18,7 @@ - runner_project = @project.runner_projects.find_by(runner_id: runner) = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' - elsif runner.specific? - = form_for [@project.namespace, @project, @project.runner_projects.new] do |f| + = form_for [@project.namespace.becomes(Namespace), @project, @project.runner_projects.new] do |f| = f.hidden_field :runner_id, value: runner.id = f.submit 'Enable for this project', class: 'btn btn-sm' .pull-right -- cgit v1.2.1 From 5ea5494af6e6bd87981f69cd97447f1ec2c0915a Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Mon, 14 Dec 2015 13:06:16 +0100 Subject: update guides for feature proposals on the issue tracker --- CONTRIBUTING.md | 49 ++++++++++++++++++++++++++++++++++++++++++------- PROCESS.md | 16 +++++++++------- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f5da063fd4..284dc4606f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,15 +41,44 @@ This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodd To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/). -The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) is only for obvious errors in the latest [stable or development release of GitLab](MAINTENANCE.md). If something is wrong but it is not a regression compared to older versions of GitLab please do not open an issue but a feature request. When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. +The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) is +for bugs in the latest GitLab release and feature proposals. -Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple. +When submitting an issue please conform to the issue submission guidelines listed below. +Not all issues will be addressed and your issue is more likely to be addressed +if you submit a merge request which partially or fully addresses the issue. -Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. +Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. +If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) +or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. +There are a lot of helpful GitLab users there who may be able to help you quickly. +If your particular issue turns out to be a bug, it will find its way from there. + +### Feature proposals + +To create a feature proposal for CE and CI, open an issue on the +[issue tracker of CE](https://gitlab.com/gitlab-org/gitlab-ce/issues). + +For feature proposals for EE, open an issue on +[the issue tracker of EE](https://gitlab.com/gitlab-org/gitlab-ce/issues). + +Add the label `feature proposal` to the issue. + +Please keep feature proposals as small and simple as possible, +complex proposals might be edited to make them small and simple. + +For changes in the interface, it can be helpful to create a mockup first. +If you want to create something yourself, consider opening an issue first to +discuss whether it is interesting to include this in GitLab. ### Issue tracker guidelines -**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post): +**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** +for similar entries before submitting your own, +there's a good chance somebody else had the same issue or feature proposal. +Show your support with `:+1:` and/or join the discussion. + +Please submit bugs in the following format (as the first post): 1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen) 1. **Steps to reproduce:** How can we reproduce the issue @@ -65,7 +94,12 @@ Please send a merge request with a tested solution or a merge request with a fai ## Merge requests -We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the [status 'accepting merge requests' on our feature request forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome. If you want to add a new feature that is not marked it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. Please include screenshots or wireframes if the feature will also change the UI. +We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the +[label 'accepting merge requests' on our issue tracker for CE](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) +and [EE](https://gitlab.com/gitlab-org/gitlab-ee/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) +but other improvements are also welcome. +If you want to add a new feature that is not labeled it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. +Please include screenshots or wireframes if the feature will also change the UI. Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). @@ -89,7 +123,8 @@ If you can, please submit a merge request with the fix or improvements including 1. The MR description should give a motive for your change and the method you used to achieve it 1. If the MR changes the UI it should include before and after screenshots 1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R` -1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) and/or [feature requests](http://feedback.gitlab.com/) from the merge request description and leave a comment on them with a link back to the MR +1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) +from the merge request description and leave a comment on them with a link back to the MR 1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission 1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). 1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. @@ -136,7 +171,7 @@ If you add a dependency in GitLab (such as an operating system package) please c 1. What does this MR do? 1. Are there points in the code the reviewer needs to double check? 1. Why was this MR needed? -1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)? +1. What are the relevant issue numbers? 1. Screenshots (if relevant) ## Contribution acceptance criteria diff --git a/PROCESS.md b/PROCESS.md index 72fc3481447..5f4d67bc10e 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -8,7 +8,7 @@ Below we describe the contributing process to GitLab for two reasons. So that co ### Issue team - Looks for issues without [workflow labels](#how-we-handle-issues) and triages issue -- Closes invalid issues with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) +- Closes invalid issues with a comment (duplicates, [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) - Asks for feedback from issue reporter ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) - Monitors all issues for feedback (but especially ones commented on since automatically watching them) - Closes issues with no feedback from the reporter for two weeks @@ -45,6 +45,8 @@ Workflow labels are purposely not very detailed since that would be hard to keep - *Frontend* needs help from a Front-end engineer - *Graphics* needs help from a Graphics designer - *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. +- *feature proposal* is a proposal for a new feature for GitLab. People are encouraged to vote +in support or comment for further detail. Do not use `feature request`. Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label. @@ -62,7 +64,6 @@ If an issue is complex and needs the attention of a specific person, assignment - Bright orange `#eb6420`: workflow labels for core team members (attached MR, awaiting developer action/feedback) - Light blue `#82C5FF`: functional labels - Green labels `#009800`: issues that can generally be ignored. For example, issues given the following labels normally can be closed immediately: - - Feature request (see copy & paste response: [Feature requests](#feature-requests)) - Support (see copy & paste response: [Support requests and configuration questions](#support-requests-and-configuration-questions) ## Be kind @@ -75,10 +76,6 @@ Be kind to people trying to contribute. Be aware that people may be a non-native Thanks for the issue report. Please reformat your issue to conform to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). -### Feature requests - -Thank you for your interest in improving GitLab. We don't use the issue tracker for feature requests. Things that are wrong but are not a regression compared to older versions of GitLab are considered feature requests and not issues. Please use the \[feature request forum\]\(http://feedback.gitlab.com/) for this purpose or create a merge request implementing this feature. Have a look at the \[contribution guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md) for more information. - ### Issue report for old version Thanks for the issue report but we only support issues for the latest stable version of GitLab. I'm closing this issue but if you still experience this problem in the latest stable version, please open a new issue (but also reference the old issue(s)). Make sure to also include the necessary debugging information conforming to the issue tracker guidelines found in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). @@ -113,7 +110,12 @@ This merge request has been closed because a request for more information has no ### Accepting merge requests -Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first. +Is there an issue on the [issue tracker](https://gitlab.com/gitlab-org/gitlab-ce/issues) +that is similar to this? +Could you please link it here? +Please be aware that new functionality that is not marked +[accepting merge requests](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) +might not make it into GitLab. ### Only accepting merge requests with green tests -- cgit v1.2.1 From c0ff4fdd99d44e154083242e32207f567374c8b1 Mon Sep 17 00:00:00 2001 From: Kamil Trzcinski Date: Mon, 14 Dec 2015 13:06:59 +0100 Subject: Move CI admin builds and runners specs to correct directory [ci skip] --- spec/features/admin/admin_builds_spec.rb | 69 +++++++++++++++++++++++++++++++ spec/features/admin/admin_runners_spec.rb | 64 ++++++++++++++++++++++++++++ spec/features/atom/builds_spec.rb | 69 ------------------------------- spec/features/atom/runners_spec.rb | 64 ---------------------------- 4 files changed, 133 insertions(+), 133 deletions(-) create mode 100644 spec/features/admin/admin_builds_spec.rb create mode 100644 spec/features/admin/admin_runners_spec.rb delete mode 100644 spec/features/atom/builds_spec.rb delete mode 100644 spec/features/atom/runners_spec.rb diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb new file mode 100644 index 00000000000..72764b1629d --- /dev/null +++ b/spec/features/admin/admin_builds_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe "Admin Builds" do + let(:commit) { FactoryGirl.create :ci_commit } + let(:build) { FactoryGirl.create :ci_build, commit: commit } + + before do + login_as :admin + end + + describe "GET /admin/builds" do + before do + build + visit admin_builds_path + end + + it { expect(page).to have_content "Running" } + it { expect(page).to have_content build.short_sha } + end + + describe "Tabs" do + it "shows all builds" do + FactoryGirl.create :ci_build, commit: commit, status: "pending" + FactoryGirl.create :ci_build, commit: commit, status: "running" + FactoryGirl.create :ci_build, commit: commit, status: "success" + FactoryGirl.create :ci_build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "All" + end + + expect(page.all(".build-link").size).to eq(4) + end + + it "shows finished builds" do + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "Finished" + end + + expect(page.find(".build-link")).not_to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build1.id) + expect(page.find(".build-link")).to have_content(build2.id) + end + + it "shows running builds" do + build = FactoryGirl.create :ci_build, commit: commit, status: "pending" + build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" + + visit admin_builds_path + + within ".center-top-menu" do + click_on "Running" + end + + expect(page.find(".build-link")).to have_content(build.id) + expect(page.find(".build-link")).not_to have_content(build2.id) + expect(page.find(".build-link")).not_to have_content(build3.id) + end + end +end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb new file mode 100644 index 00000000000..b1f2d401042 --- /dev/null +++ b/spec/features/admin/admin_runners_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe "Admin Runners" do + before do + login_as :admin + end + + describe "Runners page" do + before do + runner = FactoryGirl.create(:ci_runner) + commit = FactoryGirl.create(:ci_commit) + FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) + visit admin_runners_path + end + + it { page.has_text? "Manage Runners" } + it { page.has_text? "To register a new runner" } + it { page.has_text? "Runners with last contact less than a minute ago: 1" } + + describe 'search' do + before do + FactoryGirl.create :ci_runner, description: 'runner-foo' + FactoryGirl.create :ci_runner, description: 'runner-bar' + + search_form = find('#runners-search') + search_form.fill_in 'search', with: 'runner-foo' + search_form.click_button 'Search' + end + + it { expect(page).to have_content("runner-foo") } + it { expect(page).not_to have_content("runner-bar") } + end + end + + describe "Runner show page" do + let(:runner) { FactoryGirl.create :ci_runner } + + before do + @project1 = FactoryGirl.create(:empty_project) + @project2 = FactoryGirl.create(:empty_project) + visit admin_runner_path(runner) + end + + describe 'runner info' do + it { expect(find_field('runner_token').value).to eq runner.token } + end + + describe 'projects' do + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).to have_content(@project2.name_with_namespace) } + end + + describe 'search' do + before do + search_form = find('#runner-projects-search') + search_form.fill_in 'search', with: @project1.name + search_form.click_button 'Search' + end + + it { expect(page).to have_content(@project1.name_with_namespace) } + it { expect(page).not_to have_content(@project2.name_with_namespace) } + end + end +end diff --git a/spec/features/atom/builds_spec.rb b/spec/features/atom/builds_spec.rb deleted file mode 100644 index 72764b1629d..00000000000 --- a/spec/features/atom/builds_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'spec_helper' - -describe "Admin Builds" do - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - - before do - login_as :admin - end - - describe "GET /admin/builds" do - before do - build - visit admin_builds_path - end - - it { expect(page).to have_content "Running" } - it { expect(page).to have_content build.short_sha } - end - - describe "Tabs" do - it "shows all builds" do - FactoryGirl.create :ci_build, commit: commit, status: "pending" - FactoryGirl.create :ci_build, commit: commit, status: "running" - FactoryGirl.create :ci_build, commit: commit, status: "success" - FactoryGirl.create :ci_build, commit: commit, status: "failed" - - visit admin_builds_path - - within ".center-top-menu" do - click_on "All" - end - - expect(page.all(".build-link").size).to eq(4) - end - - it "shows finished builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - - visit admin_builds_path - - within ".center-top-menu" do - click_on "Finished" - end - - expect(page.find(".build-link")).not_to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build1.id) - expect(page.find(".build-link")).to have_content(build2.id) - end - - it "shows running builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" - - visit admin_builds_path - - within ".center-top-menu" do - click_on "Running" - end - - expect(page.find(".build-link")).to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build2.id) - expect(page.find(".build-link")).not_to have_content(build3.id) - end - end -end diff --git a/spec/features/atom/runners_spec.rb b/spec/features/atom/runners_spec.rb deleted file mode 100644 index b1f2d401042..00000000000 --- a/spec/features/atom/runners_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'spec_helper' - -describe "Admin Runners" do - before do - login_as :admin - end - - describe "Runners page" do - before do - runner = FactoryGirl.create(:ci_runner) - commit = FactoryGirl.create(:ci_commit) - FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) - visit admin_runners_path - end - - it { page.has_text? "Manage Runners" } - it { page.has_text? "To register a new runner" } - it { page.has_text? "Runners with last contact less than a minute ago: 1" } - - describe 'search' do - before do - FactoryGirl.create :ci_runner, description: 'runner-foo' - FactoryGirl.create :ci_runner, description: 'runner-bar' - - search_form = find('#runners-search') - search_form.fill_in 'search', with: 'runner-foo' - search_form.click_button 'Search' - end - - it { expect(page).to have_content("runner-foo") } - it { expect(page).not_to have_content("runner-bar") } - end - end - - describe "Runner show page" do - let(:runner) { FactoryGirl.create :ci_runner } - - before do - @project1 = FactoryGirl.create(:empty_project) - @project2 = FactoryGirl.create(:empty_project) - visit admin_runner_path(runner) - end - - describe 'runner info' do - it { expect(find_field('runner_token').value).to eq runner.token } - end - - describe 'projects' do - it { expect(page).to have_content(@project1.name_with_namespace) } - it { expect(page).to have_content(@project2.name_with_namespace) } - end - - describe 'search' do - before do - search_form = find('#runner-projects-search') - search_form.fill_in 'search', with: @project1.name - search_form.click_button 'Search' - end - - it { expect(page).to have_content(@project1.name_with_namespace) } - it { expect(page).not_to have_content(@project2.name_with_namespace) } - end - end -end -- cgit v1.2.1 From 6586856a1572535e0b9ca2f9021dfd88a158ffdd Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 14:03:58 +0100 Subject: Use a new admin runners path when reseting runners token --- app/controllers/admin/application_settings_controller.rb | 2 +- spec/features/admin/admin_runners_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 48040359389..9dd16f8c735 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -16,7 +16,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController def reset_runners_token @application_setting.reset_runners_registration_token! flash[:notice] = 'New runners registration token has been generated!' - redirect_to ci_admin_runners_path + redirect_to admin_runners_path end private diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index d5dd11a01d3..66a2cc0c157 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -64,7 +64,7 @@ describe "Admin Runners" do describe 'runners registration token' do let!(:token) { current_application_settings.ensure_runners_registration_token } - before { visit ci_admin_runners_path } + before { visit admin_runners_path } it 'has a registration token' do expect(page).to have_content("Registration token is #{token}") -- cgit v1.2.1 From 9d2b8326b2c106389024a2c3e921fbb6283db748 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 14:25:18 +0100 Subject: Update commits spinach tests related to displaying build status --- features/project/commits/commits.feature | 3 ++- features/steps/project/commits/commits.rb | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature index 21367fd76a0..5bb2d0e976b 100644 --- a/features/project/commits/commits.feature +++ b/features/project/commits/commits.feature @@ -19,7 +19,8 @@ Feature: Project Commits Scenario: I browse commit with ci from list Given commit has ci status - And I click on commit link + And repository contains ".gitlab-ci.yml" file + When I click on commit link Then I see commit ci info And I click status link Then I see builds list diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 101eb408ba1..a3141fe3be1 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -108,6 +108,10 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps create :ci_build, commit: ci_commit end + step 'repository contains ".gitlab-ci.yml" file' do + allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file).and_return(String.new) + end + step 'I see commit ci info' do expect(page).to have_content "build: pending" end -- cgit v1.2.1 From 51ef4a2fed382833833f15afae1da3f98bfa5031 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Sat, 12 Dec 2015 23:06:23 -0200 Subject: Sidekiq-cron configuration moved to gitlab.yml --- config/gitlab.yml.example | 9 +++++++++ config/initializers/1_settings.rb | 9 +++++++++ config/initializers/sidekiq.rb | 7 ++----- config/schedule.yml | 10 ---------- 4 files changed, 20 insertions(+), 15 deletions(-) delete mode 100644 config/schedule.yml diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index db378118f85..54dddb41ac4 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -144,6 +144,15 @@ production: &base # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon + ## Auxiliary jobs + # Periodically executed jobs, to self-heal Gitlab, do external synchronizations, etc. + # Please read here for more information: https://github.com/ondrejbartas/sidekiq-cron#adding-cron-job + cron_jobs: + # Flag stuck CI builds as failed + stuck_ci_builds_worker: + cron: "0 0 * * *" + + # # 2. GitLab CI settings # ========================== diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 63d8ae17436..37b92d07946 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -224,6 +224,15 @@ Settings.gravatar['plain_url'] ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{ Settings.gravatar['ssl_url'] ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon' Settings.gravatar['host'] = Settings.get_host_without_www(Settings.gravatar['plain_url']) +# +# Cron Jobs +# +Settings['cron_jobs'] ||= Settingslogic.new({}) +Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' +Settings.cron_jobs['stuck_ci_builds_worker']['class'] = 'StuckCiBuildsWorker' + + # # GitLab Shell # diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 2e3a71912ef..b42b718a7db 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -18,11 +18,8 @@ Sidekiq.configure_server do |config| chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] end - # Sidekiq-cron: load recurring jobs from schedule.yml - schedule_file = 'config/schedule.yml' - if File.exists?(schedule_file) - Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) - end + # Sidekiq-cron: load recurring jobs from gitlab.yml + Sidekiq::Cron::Job.load_from_hash! Gitlab.config.cron_jobs # Database pool should be at least `sidekiq_concurrency` + 2 # For more info, see: https://github.com/mperham/sidekiq/blob/master/4.0-Upgrade.md diff --git a/config/schedule.yml b/config/schedule.yml deleted file mode 100644 index 993a95fef56..00000000000 --- a/config/schedule.yml +++ /dev/null @@ -1,10 +0,0 @@ -# Here is a list of jobs that are scheduled to run periodically. -# We use a UNIX cron notation to specify execution schedule. -# -# Please read here for more information: -# https://github.com/ondrejbartas/sidekiq-cron#adding-cron-job - -stuck_ci_builds_worker: - cron: "0 0 * * *" - class: "StuckCiBuildsWorker" - queue: "default" -- cgit v1.2.1 From 0a81a681585bf699a7a41a449a2c0c21e2e335c6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 18:14:50 +0100 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 365b9e973fc..6ff06091f61 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,9 @@ v 8.3.0 (unreleased) - Display referenced merge request statuses in the issue description (Greg Smethells) - Implement new sidebar for issue and merge request pages - Emoji picker improvements + - Suppress warning about missing `.gitlab-ci.yml` if builds are disabled + - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present + - Persist runners registration token in database v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 58bb985fe0e3aff148191d2a2a2c0246b60c2ebe Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 12 Dec 2015 13:59:53 -0800 Subject: Add upgrade guide for 8.2 to 8.3 [ci skip] --- doc/update/8.2-to-8.3.md | 200 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 doc/update/8.2-to-8.3.md diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md new file mode 100644 index 00000000000..071ed02494a --- /dev/null +++ b/doc/update/8.2-to-8.3.md @@ -0,0 +1,200 @@ +# From 8.2 to 8.3 + +**NOTE:** GitLab 8.0 introduced several significant changes related to +installation and configuration which *are not duplicated here*. Be sure you're +already running a working version of at least 8.0 before proceeding with this +guide. + +### 0. Double-check your Git version + +**This notice applies only to /usr/local/bin/git** + +If you compiled Git from source on your GitLab server then please double-check +that you are using a version that protects against CVE-2014-9390. For six +months after this vulnerability became known the GitLab installation guide +still contained instructions that would install the outdated, 'vulnerable' Git +version 2.1.2. + +Run the following command to get your current Git version: + +```sh +/usr/local/bin/git --version +``` + +If you see 'No such file or directory' then you did not install Git according +to the outdated instructions from the GitLab installation guide and you can go +to the next step 'Stop server' below. + +If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4, +v2.2.1 or newer. You can use the [instructions in the GitLab source +installation +guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) +to install a newer version of Git. + +### 1. Stop server + + sudo service gitlab stop + +### 2. Backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Get latest code + +```bash +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +``` + +For GitLab Community Edition: + +```bash +sudo -u git -H git checkout 8-3-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +sudo -u git -H git checkout 8-3-stable-ee +``` + +### 4. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch +sudo -u git -H git checkout v2.6.8 +``` + +### 5. Replace gitlab-git-http-server with gitlab-workhorse + +Install and compile gitlab-workhorse. This requires [Go +1.5](https://golang.org/dl) which should already be on your system +from GitLab 8.1. + +```bash +cd /home/git +sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git +cd gitlab-workhorse +sudo -u git -H git checkout 0.4.2 +sudo -u git -H make +``` + +Update the GitLab init script and 'default' file. + +``` +cd /home/git/gitlab +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +test -e /etc/default/gitlab && \ + sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab +``` + +Make sure that you also update your **NGINX configuration** to use +the new gitlab-workhorse.socket file. + +### 6. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment + +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +# Update init.d script +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +### 7. Update configuration files + +#### New configuration options for `gitlab.yml` + +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gitlab.yml.example +``` + +#### Nginx configuration + +View changes between the previous recommended Nginx configuration and the +current one: + +```sh +# For HTTPS configurations +git diff origin/8-2-stable:lib/support/nginx/gitlab-ssl origin/8-3-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/8-2-stable:lib/support/nginx/gitlab origin/8-3-stable:lib/support/nginx/gitlab +``` + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-git-http-server listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-3-stable/lib/support/init.d/gitlab.default.example#L34 + + +### 8. Use Redis v2.8.0+ + +Previous versions of GitLab allowed Redis versions >= 2.0 to be used, but +GitLab 8.3 uses Sidekiq 4.0, which requires Redis 2.8. You can check your Redis version +with the following command: + + redis-cli info | grep redis_version + +If you need to upgrade, see the [installation guide for Redis](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#6-redis). + +### 9. Start application + + sudo service gitlab start + sudo service nginx restart + +### 10. Check application status + +Check if GitLab and its environment are configured correctly: + + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations, the upgrade is complete! + +## Things went south? Revert to previous version (8.2) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 8.1 to 8.2](8.1-to-8.2.md), except for the +database migration (the backup is already migrated to the previous version). + +### 2. Restore from the backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +## Troubleshooting + +### "You appear to have cloned an empty repository." + +See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting). -- cgit v1.2.1 From 5bca9ec7508414ccba03f29ddca149ea93e62f45 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 14 Dec 2015 00:17:10 -0800 Subject: Rename gitlab-git-http-server mention with gitlab-workhorse [ci skip] --- doc/update/8.2-to-8.3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md index 071ed02494a..563754ff4f6 100644 --- a/doc/update/8.2-to-8.3.md +++ b/doc/update/8.2-to-8.3.md @@ -143,7 +143,7 @@ git diff origin/8-2-stable:lib/support/nginx/gitlab origin/8-3-stable:lib/suppor If you are using Apache instead of NGINX please see the updated [Apache templates]. Also note that because Apache does not support upstreams behind Unix sockets you -will need to let gitlab-git-http-server listen on a TCP port. You can do this +will need to let gitlab-workhorse listen on a TCP port. You can do this via [/etc/default/gitlab]. [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache -- cgit v1.2.1 From b9b35012de171fe03d0be2c3adac6f219bb6927a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 14 Dec 2015 09:22:42 -0800 Subject: Update gitlab-workhorse and remove mention of gitlab-git-http-server [ci skip] --- doc/update/8.2-to-8.3.md | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md index 563754ff4f6..f1000ef4650 100644 --- a/doc/update/8.2-to-8.3.md +++ b/doc/update/8.2-to-8.3.md @@ -67,36 +67,23 @@ sudo -u git -H git checkout 8-3-stable-ee ```bash cd /home/git/gitlab-shell -sudo -u git -H git fetch +sudo -u git -H git fetch --all sudo -u git -H git checkout v2.6.8 ``` -### 5. Replace gitlab-git-http-server with gitlab-workhorse +### 5. Update gitlab-workhorse Install and compile gitlab-workhorse. This requires [Go 1.5](https://golang.org/dl) which should already be on your system from GitLab 8.1. ```bash -cd /home/git -sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git -cd gitlab-workhorse -sudo -u git -H git checkout 0.4.2 +cd /home/git/gitlab-workhorse +sudo -u git -H git fetch --all +sudo -u git -H git checkout 0.4.3 sudo -u git -H make ``` -Update the GitLab init script and 'default' file. - -``` -cd /home/git/gitlab -sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab -test -e /etc/default/gitlab && \ - sudo sed -i.pre-8.2 's/^\([^=]*\)gitlab_git_http_server/\1gitlab_workhorse/' /etc/default/gitlab -``` - -Make sure that you also update your **NGINX configuration** to use -the new gitlab-workhorse.socket file. - ### 6. Install libs, migrations, etc. ```bash -- cgit v1.2.1 From 739ce7883aaaf2652c7da985254bdec60cb15c51 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 14 Dec 2015 12:28:17 -0500 Subject: Prepare Installation doc for 8.3.0-rc1 [ci skip] --- doc/install/installation.md | 73 ++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/doc/install/installation.md b/doc/install/installation.md index 0a19a060a9a..f8116a8a31c 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -38,6 +38,7 @@ The GitLab installation consists of setting up the following components: 1. Packages / Dependencies 1. Ruby +1. Go 1. System Users 1. Database 1. Redis @@ -175,48 +176,52 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ## 6. Redis As of this writing, most Debian/Ubuntu distributions ship with Redis 2.2 or -2.4. GitLab requires at least Redis 2.8. If your platform doesn't provide -this, the following instructions cover building and installing Redis from -scratch. +2.4. GitLab requires at least Redis 2.8. -Ubuntu users [can also use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server) +Ubuntu users [can use a PPA](https://launchpad.net/~chris-lea/+archive/ubuntu/redis-server) to install a recent version of Redis. - # Build Redis - wget http://download.redis.io/releases/redis-2.8.23.tar.gz - tar xzf redis-2.8.23.tar.gz - cd redis-2.8.23 - make +The following instructions cover building and installing Redis from scratch: + +```sh +# Build Redis +wget http://download.redis.io/releases/redis-2.8.23.tar.gz +tar xzf redis-2.8.23.tar.gz +cd redis-2.8.23 +make + +# Install Redis +cd utils +sudo ./install_server.sh + +# Configure redis to use sockets +sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig - # Install Redis - cd utils - sudo ./install_server.sh +# Disable Redis listening on TCP by setting 'port' to 0 +sed 's/^port .*/port 0/' /etc/redis/redis.conf.orig | sudo tee /etc/redis/redis.conf - # Configure redis to use sockets - sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.orig +# Enable Redis socket for default Debian / Ubuntu path +echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf - # Disable Redis listening on TCP by setting 'port' to 0 - sed 's/^port .*/port 0/' /etc/redis/redis.conf.orig | sudo tee /etc/redis/redis.conf +# Grant permission to the socket to all members of the redis group +echo 'unixsocketperm 770' | sudo tee -a /etc/redis/redis.conf - # Enable Redis socket for default Debian / Ubuntu path - echo 'unixsocket /var/run/redis/redis.sock' | sudo tee -a /etc/redis/redis.conf - # Grant permission to the socket to all members of the redis group - echo 'unixsocketperm 770' | sudo tee -a /etc/redis/redis.conf +# Create the directory which contains the socket +mkdir /var/run/redis +chown redis:redis /var/run/redis +chmod 755 /var/run/redis - # Create the directory which contains the socket - mkdir /var/run/redis - chown redis:redis /var/run/redis - chmod 755 /var/run/redis - # Persist the directory which contains the socket, if applicable - if [ -d /etc/tmpfiles.d ]; then - echo 'd /var/run/redis 0755 redis redis 10d -' | sudo tee -a /etc/tmpfiles.d/redis.conf - fi +# Persist the directory which contains the socket, if applicable +if [ -d /etc/tmpfiles.d ]; then + echo 'd /var/run/redis 0755 redis redis 10d -' | sudo tee -a /etc/tmpfiles.d/redis.conf +fi - # Activate the changes to redis.conf - sudo service redis_6379 start +# Activate the changes to redis.conf +sudo service redis_6379 start - # Add git to the redis group - sudo usermod -aG redis git +# Add git to the redis group +sudo usermod -aG redis git +``` ## 7. GitLab @@ -226,9 +231,9 @@ to install a recent version of Redis. ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-2-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-3-stable gitlab -**Note:** You can change `8-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It -- cgit v1.2.1 From 381a9951e91275f41ce09a17380222f1af3d6a22 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 14 Dec 2015 13:17:23 -0500 Subject: Update 8.2-to-8.3 guide [ci skip] --- doc/update/8.2-to-8.3.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md index f1000ef4650..e69c4f7ed3c 100644 --- a/doc/update/8.2-to-8.3.md +++ b/doc/update/8.2-to-8.3.md @@ -26,8 +26,7 @@ to the outdated instructions from the GitLab installation guide and you can go to the next step 'Stop server' below. If you see a version string then it should be v1.8.5.6, v1.9.5, v2.0.5, v2.1.4, -v2.2.1 or newer. You can use the [instructions in the GitLab source -installation +v2.2.1 or newer. You can use the [instructions in the GitLab source installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) to install a newer version of Git. @@ -73,14 +72,13 @@ sudo -u git -H git checkout v2.6.8 ### 5. Update gitlab-workhorse -Install and compile gitlab-workhorse. This requires [Go -1.5](https://golang.org/dl) which should already be on your system -from GitLab 8.1. +Install and compile gitlab-workhorse. This requires [Go 1.5](https://golang.org/dl) +which should already be on your system from GitLab 8.1. ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch --all -sudo -u git -H git checkout 0.4.3 +sudo -u git -H git checkout 0.4.2 sudo -u git -H make ``` @@ -136,7 +134,6 @@ via [/etc/default/gitlab]. [Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache [/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-3-stable/lib/support/init.d/gitlab.default.example#L34 - ### 8. Use Redis v2.8.0+ Previous versions of GitLab allowed Redis versions >= 2.0 to be used, but @@ -145,7 +142,7 @@ with the following command: redis-cli info | grep redis_version -If you need to upgrade, see the [installation guide for Redis](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#6-redis). +If you need to upgrade, see the [installation guide for Redis](https://gitlab.com/gitlab-org/gitlab-ce/blob/8-3-stable/doc/install/installation.md#6-redis). ### 9. Start application -- cgit v1.2.1 From 0b2c5003e67c219cef564c9cdceadc290d9271d3 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 14 Dec 2015 17:18:32 -0200 Subject: Updated Rubocop to latest version --- Gemfile | 2 +- Gemfile.lock | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index 7298e21ce66..9131797ed15 100644 --- a/Gemfile +++ b/Gemfile @@ -261,7 +261,7 @@ group :development, :test do gem 'spring-commands-spinach', '~> 1.0.0' gem 'spring-commands-teaspoon', '~> 0.0.2' - gem 'rubocop', '~> 0.28.0', require: false + gem 'rubocop', '~> 0.35.0', require: false gem 'coveralls', '~> 0.8.2', require: false gem 'simplecov', '~> 0.10.0', require: false gem 'flog', require: false diff --git a/Gemfile.lock b/Gemfile.lock index ff57460f5bb..796c90573cc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -513,7 +513,7 @@ GEM multi_json (~> 1.0) websocket-driver (>= 0.2.0) posix-spawn (0.3.11) - powerpack (0.0.9) + powerpack (0.1.1) pry (0.10.3) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -637,12 +637,13 @@ GEM rspec-mocks (~> 3.3.0) rspec-support (~> 3.3.0) rspec-support (3.3.0) - rubocop (0.28.0) + rubocop (0.35.1) astrolabe (~> 1.3) - parser (>= 2.2.0.pre.7, < 3.0) - powerpack (~> 0.0.6) + parser (>= 2.2.3.0, < 3.0) + powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) - ruby-progressbar (~> 1.4) + ruby-progressbar (~> 1.7) + tins (<= 1.6.0) ruby-fogbugz (0.2.1) crack (~> 0.4) ruby-progressbar (1.7.5) @@ -945,7 +946,7 @@ DEPENDENCIES rouge (~> 1.10.1) rqrcode-rails3 (~> 0.1.7) rspec-rails (~> 3.3.0) - rubocop (~> 0.28.0) + rubocop (~> 0.35.0) ruby-fogbugz (~> 0.2.1) sanitize (~> 2.0) sass-rails (~> 4.0.5) -- cgit v1.2.1 From 5fb1fd27aa7f305e910fb00229d4b92e8722731b Mon Sep 17 00:00:00 2001 From: Job van der Voort Date: Mon, 14 Dec 2015 20:22:06 +0100 Subject: move up text regarding where to post issue --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 284dc4606f9..105be7b25a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,12 +48,13 @@ When submitting an issue please conform to the issue submission guidelines liste Not all issues will be addressed and your issue is more likely to be addressed if you submit a merge request which partially or fully addresses the issue. -Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there. +Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. + ### Feature proposals To create a feature proposal for CE and CI, open an issue on the -- cgit v1.2.1 From 829dc1493cd82ff9c3ba7f517131ef5fe4c71e9c Mon Sep 17 00:00:00 2001 From: Mrinal Date: Mon, 14 Dec 2015 19:46:41 +0000 Subject: Fix Typo [mailinglist to mailing list] --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7f5da063fd4..306ffdaf68f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ Issues and merge requests should be in English and contain appropriate language Please help other GitLab users when you can. The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). -Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel. +Sign up for the mailing list, answer GitLab questions on StackOverflow or respond in the IRC channel. You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. ## I want to contribute! @@ -191,4 +191,4 @@ This code of conduct applies both within project spaces and in public spaces whe Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) \ No newline at end of file -- cgit v1.2.1 From e7d7cd67bb3e43676ae1a42d65ee45d7a01fc75b Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 14 Dec 2015 22:07:53 +0200 Subject: Clarify what CE and EE means for newcomers --- CONTRIBUTING.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 09edfc8c302..c10b9f9807b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,11 @@ Thank you for your interest in contributing to GitLab. This guide details how contribute to GitLab in a way that is efficient for everyone. If you have read this guide and want to know how the GitLab core-team operates please see [the GitLab contributing process](PROCESS.md). +GitLab comes into two flavors, GitLab Community Edition (CE) our free and open +source edition, and GitLab Enterprise Edition (EE) which is our commercial +edition. Throughout this guide you will see references to CE and EE for +abbreviation. + ## Contributor license agreement By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md). @@ -227,4 +232,4 @@ This code of conduct applies both within project spaces and in public spaces whe Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) \ No newline at end of file +This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) -- cgit v1.2.1 From 5afe03315d12d1379c31a87a02fc9d9b6952f539 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 14 Dec 2015 18:14:07 -0200 Subject: Preserve trailing new lines at the end of file on the online editor Because Haml automatically indents the HTML source code, the contents of whitespace-sensitive tags like pre and textarea can get screwed up. --- CHANGELOG | 1 + app/views/projects/blob/_editor.html.haml | 3 +-- features/project/source/browse_files.feature | 11 +++++++++++ features/steps/project/source/browse_files.rb | 8 ++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6ff06091f61..7f9dfd98cd7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -54,6 +54,7 @@ v 8.3.0 (unreleased) - Suppress warning about missing `.gitlab-ci.yml` if builds are disabled - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present - Persist runners registration token in database + - Fix online editor should not remove newlines at the end of the file v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml index 333f5d470ed..10b02813733 100644 --- a/app/views/projects/blob/_editor.html.haml +++ b/app/views/projects/blob/_editor.html.haml @@ -16,8 +16,7 @@ = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2' .file-content.code - %pre.js-edit-mode-pane#editor - = params[:content] || local_assigns[:blob_data] + %pre.js-edit-mode-pane#editor #{params[:content] || local_assigns[:blob_data]} - if local_assigns[:path] .js-edit-mode-pane#preview.hide .center diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index 439787f2641..02159ee3776 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -34,6 +34,17 @@ Feature: Project Source Browse Files Then I am redirected to the new file And I should see its new content + @javascript + Scenario: I can create and commit file with new lines at the end of file + Given I click on "New file" link in repo + And I edit code with new lines at end of file + And I fill the new file name + And I fill the commit message + And I click on "Commit Changes" + Then I am redirected to the new file + And I click button "Edit" + And I should see its content with new lines preserved at end of file + @javascript Scenario: I can upload file and commit Given I click on "Upload file" link in repo diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f2b95764267..b88709620ab 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -37,6 +37,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps expect(page).to have_content new_gitignore_content end + step 'I should see its content with new lines preserved at end of file' do + expect(evaluate_script('blob.editor.getValue()')).to eq "Sample\n\n\n" + end + step 'I click link "Raw"' do click_link 'Raw' end @@ -62,6 +66,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps set_new_content end + step 'I edit code with new lines at end of file' do + execute_script('blob.editor.setValue("Sample\n\n\n")') + end + step 'I fill the new file name' do fill_in :file_name, with: new_file_name end -- cgit v1.2.1 From f8bf6c4b2c5fb41ae92df09b9f968d5d5d61bb2b Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Fri, 11 Dec 2015 17:27:20 -0600 Subject: [ci skip] Add user repository integrity check rake task --- doc/raketasks/README.md | 3 +- doc/raketasks/check.md | 63 +++++++++++++++++++++++++++++++++++ doc/raketasks/check_repos_output.png | Bin 0 -> 73786 bytes lib/tasks/gitlab/check.rake | 56 ++++++++++++++++++++++++++++--- 4 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 doc/raketasks/check.md create mode 100644 doc/raketasks/check_repos_output.png diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md index a8dc5c24df2..cc8a22cd003 100644 --- a/doc/raketasks/README.md +++ b/doc/raketasks/README.md @@ -1,10 +1,11 @@ # Rake tasks - [Backup restore](backup_restore.md) +- [Check](check.md) - [Cleanup](cleanup.md) - [Features](features.md) - [Maintenance](maintenance.md) and self-checks - [User management](user_management.md) - [Web hooks](web_hooks.md) - [Import](import.md) of git repositories in bulk -- [Rebuild authorized_keys file](http://doc.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators \ No newline at end of file +- [Rebuild authorized_keys file](http://doc.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators diff --git a/doc/raketasks/check.md b/doc/raketasks/check.md new file mode 100644 index 00000000000..3ff3fee6a40 --- /dev/null +++ b/doc/raketasks/check.md @@ -0,0 +1,63 @@ +# Check Rake Tasks + +## Repository Integrity + +Even though Git is very resilient and tries to prevent data integrity issues, +there are times when things go wrong. The following Rake tasks intend to +help GitLab administrators diagnose problem repositories so they can be fixed. + +There are 3 things that are checked to determine integrity. + +1. Git repository file system check ([git fsck](https://git-scm.com/docs/git-fsck)). + This step verifies the connectivity and validity of objects in the repository. +1. Check for `config.lock` in the repository directory. +1. Check for any branch/references lock files in `refs/heads`. + +It's important to note that the existence of `config.lock` or reference locks +alone do not necessarily indicate a problem. Lock files are routinely created +and removed as Git and GitLab perform operations on the repository. They serve +to prevent data integrity issues. However, if a Git operation is interrupted these +locks may not be cleaned up properly. + +The following symptoms may indicate a problem with repository integrity. If users +experience these symptoms you may use the rake tasks described below to determine +exactly which repositories are causing the trouble. + +- Receiving an error when trying to push code - `remote: error: cannot lock ref` +- A 500 error when viewing the GitLab dashboard or when accessing a specific project. + +### Check all GitLab repositories + +This task loops through all repositories on the GitLab server and runs the +3 integrity checks described previously. + +``` +# omnibus-gitlab +sudo gitlab-rake gitlab:repo:check + +# installation from source +bundle exec rake gitlab:repo:check RAILS_ENV=production +``` + +### Check repositories for a specific user + +This task checks all repositories that a specific user has access to. This is important +because sometimes you know which user is experiencing trouble but you don't know +which project might be the cause. + +If the rake task is executed without brackets at the end, you will be prompted +to enter a username. + +```bash +# omnibus-gitlab +sudo gitlab-rake gitlab:user:check_repos +sudo gitlab-rake gitlab:user:check_repos[] + +# installation from source +bundle exec rake gitlab:user:check_repos RAILS_ENV=production +bundle exec rake gitlab:user:check_repos[] RAILS_ENV=production +``` + +Example output: + +![gitlab:user:check_repos output](check_repos_output.png) diff --git a/doc/raketasks/check_repos_output.png b/doc/raketasks/check_repos_output.png new file mode 100644 index 00000000000..916b1685101 Binary files /dev/null and b/doc/raketasks/check_repos_output.png differ diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index a25fac62cfc..167b0112666 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -822,10 +822,27 @@ namespace :gitlab do namespace_dirs.each do |namespace_dir| repo_dirs = Dir.glob(File.join(namespace_dir, '*')) - repo_dirs.each do |dir| - puts "\nChecking repo at #{dir}" - system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: dir) - end + repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) } + end + end + end + + namespace :user do + desc "GitLab | Check the integrity of a specific user's repositories" + task :check_repos, [:username] => :environment do |t, args| + username = args[:username] || prompt("Check repository integrity for which username? ".blue) + user = User.find_by(username: username) + if user + repo_dirs = user.authorized_projects.map do |p| + File.join( + Gitlab.config.gitlab_shell.repos_path, + "#{p.path_with_namespace}.git" + ) + end + + repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) } + else + puts "\nUser '#{username}' not found".red end end end @@ -952,4 +969,35 @@ namespace :gitlab do false end end + + def check_repo_integrity(repo_dir) + puts "\nChecking repo at #{repo_dir.yellow}" + + git_fsck(repo_dir) + check_config_lock(repo_dir) + check_ref_locks(repo_dir) + end + + def git_fsck(repo_dir) + puts "Running `git fsck`".yellow + system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir) + end + + def check_config_lock(repo_dir) + config_exists = File.exist?(File.join(repo_dir,'config.lock')) + config_output = config_exists ? 'yes'.red : 'no'.green + puts "'config.lock' file exists?".yellow + " ... #{config_output}" + end + + def check_ref_locks(repo_dir) + lock_files = Dir.glob(File.join(repo_dir,'refs/heads/*.lock')) + if lock_files.present? + puts "Ref lock files exist:".red + lock_files.each do |lock_file| + puts " #{lock_file}" + end + else + puts "No ref lock files exist".green + end + end end -- cgit v1.2.1 From be41d84fb078667694ecbf5f2729175fbf8b0343 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Mon, 14 Dec 2015 15:34:46 -0600 Subject: Allow account unlock via email --- CHANGELOG | 1 + app/models/user.rb | 1 + app/views/devise/mailer/unlock_instructions.html.erb | 7 ------- app/views/devise/mailer/unlock_instructions.html.haml | 10 ++++++++++ app/views/devise/unlocks/new.html.erb | 12 ------------ app/views/devise/unlocks/new.html.haml | 14 ++++++++++++++ config/initializers/devise.rb | 4 ++-- db/migrate/20151210030143_add_unlock_token_to_user.rb | 5 +++++ db/schema.rb | 1 + spec/models/user_spec.rb | 1 + 10 files changed, 35 insertions(+), 21 deletions(-) delete mode 100644 app/views/devise/mailer/unlock_instructions.html.erb create mode 100644 app/views/devise/mailer/unlock_instructions.html.haml delete mode 100644 app/views/devise/unlocks/new.html.erb create mode 100644 app/views/devise/unlocks/new.html.haml create mode 100644 db/migrate/20151210030143_add_unlock_token_to_user.rb diff --git a/CHANGELOG b/CHANGELOG index 6ff06091f61..84e2684fb6b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -19,6 +19,7 @@ v 8.3.0 (unreleased) - Recognize issue/MR/snippet/commit links as references - Add ignore whitespace change option to commit view - Fire update hook from GitLab + - Allow account unlock via email - Style warning about mentioning many people in a comment - Fix: sort milestones by due date once again (Greg Smethells) - Migrate all CI::Services and CI::WebHooks to Services and WebHooks diff --git a/app/models/user.rb b/app/models/user.rb index fdd14f4571d..829c37e88c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -26,6 +26,7 @@ # bio :string(255) # failed_attempts :integer default(0) # locked_at :datetime +# unlock_token :string(255) # username :string(255) # can_create_group :boolean default(TRUE), not null # can_create_team :boolean default(TRUE), not null diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb deleted file mode 100644 index 79d6c761d8f..00000000000 --- a/app/views/devise/mailer/unlock_instructions.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -

    Hello <%= @resource.email %>!

    - -

    Your account has been locked due to an excessive amount of unsuccessful sign in attempts.

    - -

    Click the link below to unlock your account:

    - -

    <%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %>

    diff --git a/app/views/devise/mailer/unlock_instructions.html.haml b/app/views/devise/mailer/unlock_instructions.html.haml new file mode 100644 index 00000000000..52b327e20c5 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.haml @@ -0,0 +1,10 @@ +%p +Hello #{@resource.name}! + +%p + Your GitLab account has been locked due to an excessive amount of unsuccessful + sign in attempts. Your account will automatically unlock in + = time_ago_in_words(Devise.unlock_in.from_now) + or you may click the link below to unlock now. + +%p= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) diff --git a/app/views/devise/unlocks/new.html.erb b/app/views/devise/unlocks/new.html.erb deleted file mode 100644 index f9277d1673f..00000000000 --- a/app/views/devise/unlocks/new.html.erb +++ /dev/null @@ -1,12 +0,0 @@ -

    Resend unlock instructions

    - -<%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> - <%= devise_error_messages! %> - -
    <%= f.label :email %>
    - <%= f.email_field :email %>
    - -
    <%= f.submit "Resend unlock instructions" %>
    -<% end %> - -<%= render partial: "devise/shared/links" %> diff --git a/app/views/devise/unlocks/new.html.haml b/app/views/devise/unlocks/new.html.haml new file mode 100644 index 00000000000..49c087c0646 --- /dev/null +++ b/app/views/devise/unlocks/new.html.haml @@ -0,0 +1,14 @@ +.login-box + .login-heading + %h3 Resend unlock email + .login-body + = form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| + .devise-errors + = devise_error_messages! + .clearfix.append-bottom-20 + = f.email_field :email, class: 'form-control', placeholder: 'Email', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off' + .clearfix + = f.submit 'Resend unlock instructions', class: 'btn btn-success' + +.clearfix.prepend-top-20 + = render 'devise/shared/sign_in_link' diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 5fb43a86e13..2a09a4d3739 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -121,14 +121,14 @@ Devise.setup do |config| config.lock_strategy = :failed_attempts # Defines which key will be used when locking and unlocking an account - # config.unlock_keys = [ :email ] + config.unlock_keys = [ :email ] # Defines which strategy will be used to unlock an account. # :email = Sends an unlock link to the user email # :time = Re-enables login after a certain amount of time (see :unlock_in below) # :both = Enables both strategies # :none = No unlock strategy. You should handle unlocking by yourself. - config.unlock_strategy = :time + config.unlock_strategy = :both # Number of authentication tries before locking an account if lock_strategy # is failed attempts. diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb new file mode 100644 index 00000000000..0ea66ba65df --- /dev/null +++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb @@ -0,0 +1,5 @@ +class AddUnlockTokenToUser < ActiveRecord::Migration + def change + add_column :users, :unlock_token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 0167e30ff8b..60b42f7a473 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -837,6 +837,7 @@ ActiveRecord::Schema.define(version: 20151210125932) do t.integer "consumed_timestep" t.integer "layout", default: 0 t.boolean "hide_project_limit", default: false + t.string "unlock_token" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index daa9d1087bf..47889c04cfc 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -26,6 +26,7 @@ # bio :string(255) # failed_attempts :integer default(0) # locked_at :datetime +# unlock_token :string(255) # username :string(255) # can_create_group :boolean default(TRUE), not null # can_create_team :boolean default(TRUE), not null -- cgit v1.2.1 From 2e5442817142651bc9ff16d8d431cb5c589ad9c2 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 15 Dec 2015 00:14:23 +0200 Subject: Clean up CONTRIBUTING.md [ci skip] * Use 80 chars width where possible * Remove easyfix label reference * Add new issue submission template * Add long links to the bottom of the page --- CONTRIBUTING.md | 367 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 254 insertions(+), 113 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c10b9f9807b..ff86b90287d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,77 +1,99 @@ # Contribute to GitLab -Thank you for your interest in contributing to GitLab. -This guide details how contribute to GitLab in a way that is efficient for everyone. -If you have read this guide and want to know how the GitLab core-team operates please see [the GitLab contributing process](PROCESS.md). +Thank you for your interest in contributing to GitLab. This guide details how +to contribute to GitLab in a way that is efficient for everyone. GitLab comes into two flavors, GitLab Community Edition (CE) our free and open source edition, and GitLab Enterprise Edition (EE) which is our commercial edition. Throughout this guide you will see references to CE and EE for abbreviation. +If you have read this guide and want to know how the GitLab [core-team][] +operates please see [the GitLab contributing process](PROCESS.md). + ## Contributor license agreement -By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md). +By submitting code as an individual you agree to the +[individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). +By submitting code as an entity you agree to the +[corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md). ## Security vulnerability disclosure -Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. +Please report suspected security vulnerabilities in private to +`support@gitlab.com`, also see the +[disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). +Please do **NOT** create publicly viewable issues for suspected security +vulnerabilities. ## Closing policy for issues and merge requests -GitLab is a popular open source project and the capacity to deal with issues and merge requests is limited. Out of respect for our volunteers, issues and merge requests not in line with the guidelines listed in this document may be closed without notice. +GitLab is a popular open source project and the capacity to deal with issues +and merge requests is limited. Out of respect for our volunteers, issues and +merge requests not in line with the guidelines listed in this document may be +closed without notice. -Please treat our volunteers with courtesy and respect, it will go a long way towards getting your issue resolved. +Please treat our volunteers with courtesy and respect, it will go a long way +towards getting your issue resolved. -Issues and merge requests should be in English and contain appropriate language for audiences of all ages. +Issues and merge requests should be in English and contain appropriate language +for audiences of all ages. ## Helping others -Please help other GitLab users when you can. -The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/). -Sign up for the mailing list, answer GitLab questions on StackOverflow or respond in the IRC channel. -You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day. +Please help other GitLab users when you can. The channels people will reach out +on can be found on the [getting help page][]. + +Sign up for the mailing list, answer GitLab questions on StackOverflow or +respond in the IRC channel. You can also sign up on [CodeTriage][] to help with +the remaining issues on the GitHub issue tracker. ## I want to contribute! If you want to contribute to GitLab, but are not sure where to start, -look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs) -with the label `up-for-grabs`. -These issues will be of reasonable size and challenge, for anyone to start -contributing to GitLab. +look for [issues with the label `up-for-grabs`][up-for-grabs]. These issues +will be of reasonable size and challenge, for anyone to start contributing to +GitLab. -This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4). +This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. ## Issue tracker -To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/). +To get support for your particular problem please use the +[getting help channels](https://about.gitlab.com/getting-help/). -The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues) is -for bugs in the latest GitLab release and feature proposals. +The [GitLab CE issue tracker on GitLab.com][ce-tracker] is for bugs concerning +the latest GitLab release and [feature proposals](#feature-proposals). -When submitting an issue please conform to the issue submission guidelines listed below. -Not all issues will be addressed and your issue is more likely to be addressed -if you submit a merge request which partially or fully addresses the issue. +When submitting an issue please conform to the issue submission guidelines +listed below. Not all issues will be addressed and your issue is more likely to +be addressed if you submit a merge request which partially or fully solves +the issue. -If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) -or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. -There are a lot of helpful GitLab users there who may be able to help you quickly. -If your particular issue turns out to be a bug, it will find its way from there. +If you're unsure where to post, post to the [mailing list][google-group] or +[Stack Overflow][stackoverflow] first. There are a lot of helpful GitLab users +there who may be able to help you quickly. If your particular issue turns out +to be a bug, it will find its way from there. -Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. +If it happens that you know the solution to an existing bug, please first +open the issue in order to keep track of it and then open the relevant merge +request that potentially fixes it. ### Feature proposals To create a feature proposal for CE and CI, open an issue on the -[issue tracker of CE](https://gitlab.com/gitlab-org/gitlab-ce/issues). +[issue tracker of CE][ce-tracker]. -For feature proposals for EE, open an issue on -[the issue tracker of EE](https://gitlab.com/gitlab-org/gitlab-ce/issues). +For feature proposals for EE, open an issue on the +[issue tracker of EE][ee-tracker]. -Add the label `feature proposal` to the issue. +In order to help track the feature proposals, we have created a +[`feature proposal`][fpl] label. For the time being, users that are not members +of the project cannot add labels. You can instead ask one of the [core team][] +members to add the label `feature proposal` to the issue. -Please keep feature proposals as small and simple as possible, -complex proposals might be edited to make them small and simple. +Please keep feature proposals as small and simple as possible, complex ones +might be edited to make them small and simple. For changes in the interface, it can be helpful to create a mockup first. If you want to create something yourself, consider opening an issue first to @@ -79,76 +101,143 @@ discuss whether it is interesting to include this in GitLab. ### Issue tracker guidelines -**[Search the issues](https://gitlab.com/gitlab-org/gitlab-ce/issues)** -for similar entries before submitting your own, -there's a good chance somebody else had the same issue or feature proposal. -Show your support with `:+1:` and/or join the discussion. - -Please submit bugs in the following format (as the first post): - -1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen) -1. **Steps to reproduce:** How can we reproduce the issue -1. **Expected behavior:** Describe your issue in detail -1. **Observed behavior** -1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise. -1. **Output of checks** - * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing - * Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md) - * Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page) - * Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) -1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem +**[Search the issue tracker][ce-tracker]** for similar entries before +submitting your own, there's a good chance somebody else had the same issue or +feature proposal. Show your support with an award emoji and/or join the +discussion. + +Please submit bugs using the following template in the issue description area. +The text in the parenthesis is there to help you with what to include. Omit it +when submitting the actual issue. You can copy-paste it and then edit as you +see fit. + +``` +## Summary + +(Summarize your issue in one sentence - what goes wrong, what did you expect to happen) + +## Steps to reproduce + +(How one can reproduce the issue - this is very important) + +## Expected behavior + +(What you should see instead) + +## Relevant logs and/or screenshots + +(Paste any relevant logs - please use code blocks (```) to format console output, +logs, and code as it's very hard to read otherwise.) + +## Output of checks + +### Results of GitLab Application Check + +(For installations with omnibus-gitlab package run and paste the output of: +sudo gitlab-rake gitlab:check SANITIZE=true) + +(For installations from source run and paste the output of: +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true) + +(we will only investigate if the tests are passing) + +### Results of GitLab Environment Info + +(For installations with omnibus-gitlab package run and paste the output of: +sudo gitlab-rake gitlab:env:info) + +(For installations from source run and paste the output of: +sudo -u git -H bundle exec rake gitlab:env:info) + +## Possible fixes + +(If you can, link to the line of code that might be responsible for the problem) + +``` ## Merge requests -We welcome merge requests with fixes and improvements to GitLab code, tests, and/or documentation. The features we would really like a merge request for are listed with the -[label 'accepting merge requests' on our issue tracker for CE](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) -and [EE](https://gitlab.com/gitlab-org/gitlab-ee/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=Accepting+Merge+Requests) -but other improvements are also welcome. -If you want to add a new feature that is not labeled it is best to first create a feedback issue (if there isn't one already) and leave a comment asking for it to be marked accepting merge requests. -Please include screenshots or wireframes if the feature will also change the UI. +We welcome merge requests with fixes and improvements to GitLab code, tests, +and/or documentation. The features we would really like a merge request for are +listed with the label [`Accepting Merge Requests` on our issue tracker for CE][accepting-mrs-ce] +and [EE][accepting-mrs-ee] but other improvements are also welcome. -Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). +If you want to add a new feature that is not labeled it is best to first create +a feedback issue (if there isn't one already) and leave a comment asking for it +to be marked as `Accepting merge requests`. Please include screenshots or +wireframes if the feature will also change the UI. -If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint. +Merge requests can be filed either at [GitLab.com][gitlab-mr-tracker] or at +[github.com][github-mr-tracker]. -To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file. +If you are new to GitLab development (or web development in general), see the +[I want to contribute!](#i-want-to-contribute) section to get you started with +some potentially easy issues. + +To start with GitLab development download the [GitLab Development Kit][gdk] and +see the [Development section](doc/development/README.md) for some guidelines. ### Merge request guidelines -If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows: +If you can, please submit a merge request with the fix or improvements +including tests. If you don't know how to fix the issue but can write a test +that exposes the issue we will accept that as well. In general bug fixes that +include a regression test are merged quickly while new features without proper +tests are least likely to receive timely feedback. The workflow to make a merge +request is as follows: 1. Fork the project into your personal space on GitLab.com 1. Create a feature branch 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code 1. Add your changes to the [CHANGELOG](CHANGELOG) -1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message -1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) -1. Push the commit to your fork +1. If you are changing the README, some documentation or other things which + have no effect on the tests, add `[ci skip]` somewhere in the commit message +1. If you have multiple commits please combine them into one commit by + [squashing them][git-squash] +1. Push the commit(s) to your fork 1. Submit a merge request (MR) to the master branch 1. The MR title should describe the change you want to make -1. The MR description should give a motive for your change and the method you used to achieve it +1. The MR description should give a motive for your change and the method you + used to achieve it 1. If the MR changes the UI it should include before and after screenshots -1. If the MR changes CSS classes please include the list of affected pages `grep css-class ./app -R` -1. Link relevant [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues) -from the merge request description and leave a comment on them with a link back to the MR -1. Be prepared to answer questions and incorporate feedback even if requests for this arrive weeks or months after your MR submission -1. If your MR touches code that executes shell commands, make sure it adheres to the [shell command guidelines]( doc/development/shell_commands.md). -1. Also have a look at the [shell command guidelines](doc/development/shell_commands.md) if your code reads or opens files, or handles paths to files on disk. -1. If your code creates new files on disk please read the [shared files guidelines](doc/development/shared_files.md). - -The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. -Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as regressions requiring patch releases. -After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features. - -Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it. - -For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the contribution acceptance criteria. +1. If the MR changes CSS classes please include the list of affected pages + `grep css-class ./app -R` +1. Link any relevant [issues][ce-tracker] in the merge request description and + leave a comment on them with a link back to the MR +1. Be prepared to answer questions and incorporate feedback even if requests + for this arrive weeks or months after your MR submission +1. If your MR touches code that executes shell commands, reads or opens files or + handles paths to files on disk, make sure it adheres to the + [shell command guidelines](doc/development/shell_commands.md) +1. If your code creates new files on disk please read the + [shared files guidelines](doc/development/shared_files.md). + +The **official merge window** is in the beginning of the month from the 1st to +the 7th day of the month. This is the best time to submit an MR and get +feedback fast. Before this time the GitLab Inc. team is still dealing with work +that is created by the monthly release such as regressions requiring patch +releases. After the 7th it is already getting closer to the release date of the +next version. This means there is less time to fix the issues created by +merging large new features. + +Please keep the change in a single MR **as small as possible**. If you want to +contribute a large feature think very hard what the minimum viable change is. +Can you split the functionality? Can you only submit the backend/API code? Can +you start with a very simple UI? Can you do part of the refactor? The increased +reviewability of small MRs that leads to higher code quality is more important +to us than having a minimal commit log. The smaller an MR is the more likely it +is it will be merged (quickly). After that you can send more MRs to enhance it. + +For examples of feedback on merge requests please look at already +[closed merge requests][]. If you would like quick feedback on your merge +request feel free to mention one of the Merge Marshalls of the [core team][]. +Please ensure that your merge request meets the contribution acceptance criteria. ## Definition of done -If you contribute to GitLab please know that changes involve more than just code. -We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html). -Please ensure you support the feature you contribute through all of these steps. +If you contribute to GitLab please know that changes involve more than just +code. We have the following [definition of done][]. Please ensure you support +the feature you contribute through all of these steps. 1. Description explaining the relevancy (see following item) 1. Working and clean code that is commented where needed @@ -162,7 +251,9 @@ Please ensure you support the feature you contribute through all of these steps. 1. Community questions answered 1. Answers to questions radiated (in docs/wiki/etc.) -If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request: +If you add a dependency in GitLab (such as an operating system package) please +consider updating the following and note the applicability of each in your +merge request: 1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ 1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md @@ -183,22 +274,31 @@ If you add a dependency in GitLab (such as an operating system package) please c ## Contribution acceptance criteria 1. The change is as small as possible (see the above paragraph for details) -1. Include proper tests and make all tests pass (unless it contains a test exposing a bug in existing code) -1. All tests have to pass, if you suspect a failing CI build is unrelated to your contribution ask for tests to be restarted. See [the CI setup document](http://doc.gitlab.com/ce/development/ci_setup.html) on who you can ask for test restart. -1. Initially contains a single commit (please use `git rebase -i` to squash commits) -1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server) +1. Include proper tests and make all tests pass (unless it contains a test + exposing a bug in existing code) +1. If you suspect a failing CI build is unrelated to your contribution, you may + try and restart the failing CI job or ask a developer to fix the + aforementioned failing test +1. Your MR initially contains a single commit (please use `git rebase -i` to + squash commits) +1. Your changes can merge without problems (if not please merge `master`, never + rebase commits pushed to the remote server) 1. Does not break any existing functionality -1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed) -1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure +1. Fixes one specific issue or implements one specific feature (do not combine + things, send separate merge requests if needed) +1. Migrations should do only one thing (eg: either create a table, move data to + a new table or remove an old table) to aid retrying on failure 1. Keeps the GitLab code base clean and well structured 1. Contains functionality we think other users will benefit from too 1. Doesn't add configuration options since they complicate future changes -1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging. -1. It conforms to the following style guides. - If your change touches a line that does not follow the style, - modify the entire line to follow it. This prevents linting tools from generating warnings. - Don't touch neighbouring lines. As an exception, automatic mass refactoring modifications - may leave style non-compliant. +1. Changes after submitting the merge request should be in separate commits + (no squashing). If necessary, you will be asked to squash when the review is + over, before merging. +1. It conforms to the following style guides: + * If your change touches a line that does not follow the style, modify the + entire line to follow it. This prevents linting tools from generating warnings. + * Don't touch neighbouring lines. As an exception, automatic mass + refactoring modifications may leave style non-compliant. ## Style guides @@ -210,26 +310,67 @@ If you add a dependency in GitLab (such as an operating system package) please c 1. [Rails](https://github.com/bbatsov/rails-style-guide) 1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing) 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) -1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security +1. [Shell commands](doc/development/shell_commands.md) created by GitLab + contributors to enhance security 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Database Migrations](doc/development/migration_style_guide.md) 1. [Documentation styleguide](doc_styleguide.md) -1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing). +1. Interface text should be written subjectively instead of objectively. It + should be the GitLab core team addressing a person. It should be written in + present time and never use past tense (has been/was). For example instead + of _prohibited this user from being saved due to the following errors:_ the + text should be _sorry, we could not create your account because:_ -This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). +This is also the style used by linting tools such as +[RuboCop](https://github.com/bbatsov/rubocop), +[PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). ## Code of conduct -As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. - -Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. - -This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com - -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/) +As contributors and maintainers of this project, we pledge to respect all +people who contribute through reporting issues, posting feature requests, +updating documentation, submitting pull requests or patches, and other +activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual +language or imagery, derogatory comments or personal attacks, trolling, public +or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. Project maintainers who do not +follow the Code of Conduct may be removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior can be +reported by emailing contact@gitlab.com + +This Code of Conduct is adapted from the [Contributor Covenant][], version 1.1.0, +available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). + +[core team]: https://about.gitlab.com/core-team/ +[getting help page]: https://about.gitlab.com/getting-help/ +[Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq +[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up+for+grabs +[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4 +[ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues +[ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues +[google-group]: https://groups.google.com/forum/#!forum/gitlabhq +[stackoverflow]: https://stackoverflow.com/questions/tagged/gitlab +[fpl]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=feature+proposal +[accepting-mrs-ce]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=Accepting+Merge+Requests +[accepting-mrs-ee]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Accepting+Merge+Requests +[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests +[github-mr-tracker]: https://github.com/gitlabhq/gitlabhq/pulls +[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit +[git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits +[closed merge requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed +[definition of done]: http://guide.agilealliance.org/guide/definition-of-done.html +[Contribution Covenant]: http://contributor-covenant.org -- cgit v1.2.1 From d1f1c5c60bfd58f966671d7895c1ef612e8f8897 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 14 Dec 2015 18:10:27 -0200 Subject: Updated .rubocop.yml to match 0.35.x changes --- .rubocop.yml | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index b4ca11c8343..e92f10a7917 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -76,7 +76,7 @@ Style/BlockEndNewline: Description: 'Put end statement of multiline block on its own line.' Enabled: true -Style/Blocks: +Style/BlockDelimiters: Description: >- Avoid using {...} for multi-line blocks (multiline chaining is always ugly). @@ -232,6 +232,10 @@ Style/EvenOdd: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' Enabled: false +Style/ExtraSpacing: + Description: 'Do not use unnecessary spacing.' + Enabled: false + Style/FileName: Description: 'Use snake_case for source file names.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' @@ -431,6 +435,14 @@ Style/OpMethod: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' Enabled: false +Style/ParallelAssignment: + Description: >- + Check for simple usages of parallel assignment. + It will only warn when the number of variables + matches on both sides of the assignment. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' + Enabled: false + Style/ParenthesesAroundCondition: Description: >- Don't use parentheses around the condition of an @@ -669,6 +681,13 @@ Style/TrailingWhitespace: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' Enabled: false +Style/TrailingUnderscoreVariable: + Description: >- + Checks for the usage of unneeded trailing underscores at the + end of parallel variable assignment. + AllowNamedUnderscoreVariables: true + Enabled: false + Style/TrivialAccessors: Description: 'Prefer attr_* methods to trivial readers/writers.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' @@ -690,11 +709,6 @@ Style/UnneededPercentQ: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' Enabled: false -Style/UnneededPercentX: - Description: 'Checks for %x when `` would do.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' - Enabled: false - Style/VariableInterpolation: Description: >- Don't interpolate global, instance and class variables @@ -778,6 +792,10 @@ Metrics/MethodLength: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' Enabled: false +Metrics/ModuleLength: + Description: 'Avoid modules longer than 100 lines of code.' + Enabled: false + #################### Lint ################################ ### Warnings @@ -987,6 +1005,12 @@ Rails/ScopeArgs: Description: 'Checks the arguments of ActiveRecord scopes.' Enabled: false +Rails/TimeZone: + Description: 'Checks the correct usage of time zone aware methods.' + StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' + Reference: 'http://danilenko.org/2012/7/6/rails_timezones' + Enabled: false + Rails/Validation: Description: 'Use validates :attribute, hash of validations.' Enabled: false -- cgit v1.2.1 From 1c7128f34c10e558cac2080df7770b45863779d7 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 15 Dec 2015 00:34:22 +0200 Subject: Fix links [ci skip] --- CONTRIBUTING.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff86b90287d..7ced7c57889 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -260,7 +260,7 @@ merge request: 1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool 1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies 1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit -1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md +1. Test suite https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh 1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab ## Merge request description format @@ -303,13 +303,13 @@ merge request: ## Style guides 1. [Ruby](https://github.com/bbatsov/ruby-style-guide). - Important sections include [Source Code Layout](https://github.com/bbatsov/ruby-style-guide#source-code-layout) - and [Naming](https://github.com/bbatsov/ruby-style-guide#naming). Use: + Important sections include [Source Code Layout][rss-source] and + [Naming][rss-naming]. Use: - multi-line method chaining style **Option B**: dot `.` on previous line - string literal quoting style **Option A**: single quoted by default 1. [Rails](https://github.com/bbatsov/rails-style-guide) -1. [Testing](https://github.com/thoughtbot/guides/tree/master/style#testing) -1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) +1. [Testing](https://github.com/thoughtbot/guides/tree/master/style/testing) +1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript) 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) @@ -350,7 +350,7 @@ This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior can be -reported by emailing contact@gitlab.com +reported by emailing `contact@gitlab.com`. This Code of Conduct is adapted from the [Contributor Covenant][], version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). @@ -359,7 +359,7 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [getting help page]: https://about.gitlab.com/getting-help/ [Codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up+for+grabs -[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4 +[medium-up-for-grabs]: https://medium.com/@kentcdodds/first-timers-only-78281ea47455 [ce-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/issues [ee-tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues [google-group]: https://groups.google.com/forum/#!forum/gitlabhq @@ -373,4 +373,6 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor [git-squash]: https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits [closed merge requests]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed [definition of done]: http://guide.agilealliance.org/guide/definition-of-done.html -[Contribution Covenant]: http://contributor-covenant.org +[Contributor Covenant]: http://contributor-covenant.org +[rss-source]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#source-code-layout +[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming -- cgit v1.2.1 From b5291f95996743067bbec5a32f9c6cf0d34b36c7 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Tue, 15 Dec 2015 00:53:52 -0200 Subject: Fixed Rubocop offenses --- app/controllers/dashboard/snippets_controller.rb | 3 ++- app/controllers/projects/notes_controller.rb | 2 +- .../projects/protected_branches_controller.rb | 2 +- app/helpers/application_helper.rb | 2 +- app/helpers/external_wiki_helper.rb | 2 +- app/helpers/gitlab_markdown_helper.rb | 3 ++- app/helpers/projects_helper.rb | 7 +++---- app/helpers/tree_helper.rb | 2 +- app/mailers/notify.rb | 2 +- app/models/application_setting.rb | 12 ++++++------ app/models/concerns/token_authenticatable.rb | 4 ++-- app/models/merge_request.rb | 4 +--- app/models/namespace.rb | 4 ++-- app/models/project.rb | 10 +++++----- app/models/project_services/bamboo_service.rb | 6 ++---- app/models/project_services/flowdock_service.rb | 2 +- app/models/project_services/gemnasium_service.rb | 2 +- app/models/project_services/teamcity_service.rb | 8 +++----- app/models/user.rb | 6 +++--- app/services/merge_requests/refresh_service.rb | 2 +- config/initializers/carrierwave.rb | 10 +++++----- config/routes.rb | 2 +- features/steps/explore/groups.rb | 8 ++++---- features/steps/explore/projects.rb | 12 ++++++------ features/steps/groups.rb | 2 +- features/steps/profile/profile.rb | 2 +- features/steps/project/project.rb | 2 +- features/steps/project/source/browse_files.rb | 12 ++++++------ features/steps/shared/paths.rb | 8 ++++---- lib/api/entities.rb | 2 +- lib/gitlab/backend/shell.rb | 2 +- lib/gitlab/bitbucket_import/project_creator.rb | 3 ++- lib/gitlab/diff/file.rb | 4 ++-- lib/gitlab/fogbugz_import/importer.rb | 2 +- lib/gitlab/fogbugz_import/project_creator.rb | 3 ++- lib/gitlab/gitlab_import/project_creator.rb | 3 ++- lib/gitlab/gitorious_import/project_creator.rb | 3 ++- lib/gitlab/google_code_import/importer.rb | 6 +++--- lib/gitlab/google_code_import/project_creator.rb | 3 ++- lib/gitlab/markdown/filter/markdown_filter.rb | 2 +- .../markdown/filter/table_of_contents_filter.rb | 2 +- lib/rouge/formatters/html_gitlab.rb | 2 +- spec/factories.rb | 3 ++- spec/features/security/group_access_spec.rb | 4 ++-- spec/helpers/groups_helper.rb | 2 +- spec/models/ci/commit_spec.rb | 4 ++-- spec/models/key_spec.rb | 2 +- .../project_services/hipchat_service_spec.rb | 22 ++++++++++------------ .../slack_service/note_message_spec.rb | 8 ++++---- spec/models/user_spec.rb | 4 ++-- spec/requests/api/merge_requests_spec.rb | 2 +- spec/requests/api/services_spec.rb | 2 +- spec/services/create_commit_builds_service_spec.rb | 22 +++++++++++----------- spec/services/git_tag_push_service_spec.rb | 16 ++++++++-------- spec/services/update_snippet_service_spec.rb | 2 +- spec/support/repo_helpers.rb | 12 ++++++------ spec/workers/repository_fork_worker_spec.rb | 20 +++++++++++--------- spec/workers/stuck_ci_builds_worker_spec.rb | 4 ++-- 58 files changed, 155 insertions(+), 154 deletions(-) diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb index f4354c6d8ca..b3594d82530 100644 --- a/app/controllers/dashboard/snippets_controller.rb +++ b/app/controllers/dashboard/snippets_controller.rb @@ -1,6 +1,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController def index - @snippets = SnippetsFinder.new.execute(current_user, + @snippets = SnippetsFinder.new.execute( + current_user, filter: :by_user, user: current_user, scope: params[:scope] diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 88b949a27ab..4f1fddb4583 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -68,7 +68,7 @@ class Projects::NotesController < Projects::ApplicationController data = { author: current_user, is_award: true, - note: note_params[:note].gsub(":", '') + note: note_params[:note].delete(":") } note = noteable.notes.find_by(data) diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb index 6b52eccebf7..e49259c34b6 100644 --- a/app/controllers/projects/protected_branches_controller.rb +++ b/app/controllers/projects/protected_branches_controller.rb @@ -21,7 +21,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController if protected_branch && protected_branch.update_attributes( - developers_can_push: params[:developers_can_push] + developers_can_push: params[:developers_can_push] ) respond_to do |format| diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 21f962df206..e3bafce6910 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -61,7 +61,7 @@ module ApplicationHelper options[:class] ||= '' options[:class] << ' identicon' bg_key = project.id % 7 - style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555" + style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555" content_tag(:div, class: options[:class], style: style) do project.name[0, 1].upcase diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb index 838b85afdfe..1f3401f2906 100644 --- a/app/helpers/external_wiki_helper.rb +++ b/app/helpers/external_wiki_helper.rb @@ -1,7 +1,7 @@ module ExternalWikiHelper def get_project_wiki_path(project) external_wiki_service = project.services. - select { |service| service.to_param == 'external_wiki' }.first + find { |service| service.to_param == 'external_wiki' } if external_wiki_service.present? && external_wiki_service.active? external_wiki_service.properties['external_wiki_url'] else diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 5004e02ea0b..5d15b3513c2 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -65,7 +65,8 @@ module GitlabMarkdownHelper end def asciidoc(text) - Gitlab::Asciidoc.render(text, + Gitlab::Asciidoc.render( + text, project: @project, current_user: (current_user if defined?(current_user)), diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d061136b7b8..777817e24aa 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -330,10 +330,9 @@ module ProjectsHelper def filename_path(project, filename) if project && blob = project.repository.send(filename) namespace_project_blob_path( - project.namespace, - project, - tree_join(project.default_branch, - blob.name) + project.namespace, + project, + tree_join(project.default_branch, blob.name) ) end end diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 886a1e734b5..f448dd0ab61 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -79,7 +79,7 @@ module TreeHelper part_path = File.join(part_path, part) unless part_path.empty? part_path = part if part_path.empty? - next unless parts.last(2).include?(part) if parts.count > max_links + next if parts.count > max_links && !parts.last(2).include?(part) yield(part, tree_join(@ref, part_path)) end end diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 9beb0de68f3..3bbdd9cee76 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -17,7 +17,7 @@ class Notify < BaseMailer subject: subject, body: body.html_safe, content_type: 'text/html' - ) + ) end # Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com", diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index faa0bdf840b..1f4e8b3ef24 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -126,12 +126,12 @@ class ApplicationSetting < ActiveRecord::Base def restricted_signup_domains_raw=(values) self.restricted_signup_domains = [] self.restricted_signup_domains = values.split( - /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace - | # or - \s # any whitespace character - | # or - [\r\n] # any number of newline characters - /x) + /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace + | # or + \s # any whitespace character + | # or + [\r\n] # any number of newline characters + /x) self.restricted_signup_domains.reject! { |d| d.empty? } end end diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index 56d38fe8250..488ff8c31b7 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -13,7 +13,7 @@ module TokenAuthenticatable @token_fields << token_field define_singleton_method("find_by_#{token_field}") do |token| - where(token_field => token).first if token + find_by(token_field => token) if token end define_method("ensure_#{token_field}") do @@ -37,7 +37,7 @@ module TokenAuthenticatable def generate_token_for(token_field) loop do token = Devise.friendly_token - break token unless self.class.unscoped.where(token_field => token).first + break token unless self.class.unscoped.find_by(token_field => token) end end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f6f77a16267..d7430d36c41 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -194,9 +194,7 @@ class MergeRequest < ActiveRecord::Base similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id if similar_mrs.any? errors.add :validate_branches, - "Cannot Create: This merge request already exists: #{ - similar_mrs.pluck(:title) - }" + "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}" end end end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 1c4e101cc10..adafabbec07 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -45,7 +45,7 @@ class Namespace < ActiveRecord::Base class << self def by_path(path) - where('lower(path) = :value', value: path.downcase).first + find_by('lower(path) = :value', value: path.downcase) end # Case insensetive search for namespace by path or name @@ -148,6 +148,6 @@ class Namespace < ActiveRecord::Base end def find_fork_of(project) - projects.joins(:forked_project_link).where('forked_project_links.forked_from_project_id = ?', project.id).first + projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id) end end diff --git a/app/models/project.rb b/app/models/project.rb index e1f7bf971e3..87116451caa 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -265,7 +265,7 @@ class Project < ActiveRecord::Base joins(:namespace). iwhere('namespaces.path' => namespace_path) - projects.where('projects.path' => project_path).take || + projects.find_by('projects.path' => project_path) || projects.iwhere('projects.path' => project_path).take end @@ -450,7 +450,7 @@ class Project < ActiveRecord::Base end def external_issue_tracker - @external_issues_tracker ||= external_issues_trackers.select(&:activated?).first + @external_issues_tracker ||= external_issues_trackers.find(&:activated?) end def can_have_issues_tracker_id? @@ -496,7 +496,7 @@ class Project < ActiveRecord::Base end def ci_service - @ci_service ||= ci_services.select(&:activated?).first + @ci_service ||= ci_services.find(&:activated?) end def avatar_type @@ -547,7 +547,7 @@ class Project < ActiveRecord::Base end def project_member_by_name_or_email(name = nil, email = nil) - user = users.where('name like ? or email like ?', name, email).first + user = users.find_by('name like ? or email like ?', name, email) project_members.where(user: user) if user end @@ -722,7 +722,7 @@ class Project < ActiveRecord::Base end def project_member(user) - project_members.where(user_id: user).first + project_members.find_by(user_id: user) end def default_branch diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 0a61ad96a0e..73f99b229f2 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -27,12 +27,10 @@ class BambooService < CiService validates :build_key, presence: true, if: :activated? validates :username, presence: true, - if: ->(service) { service.password? }, - if: :activated? + if: ->(service) { service.activated? && service.password? } validates :password, presence: true, - if: ->(service) { service.username? }, - if: :activated? + if: ->(service) { service.activated? && service.username? } attr_accessor :response diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 27fc19379f1..15c7c907f7e 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -58,6 +58,6 @@ class FlowdockService < Service repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}", commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s", diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s", - ) + ) end end diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index 91ef267ad79..202fee042e3 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -57,6 +57,6 @@ class GemnasiumService < Service token: token, api_key: api_key, repo: project.repository.path_to_repo - ) + ) end end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 29d4236745a..a2d55190de7 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -27,12 +27,10 @@ class TeamcityService < CiService validates :build_type, presence: true, if: :activated? validates :username, presence: true, - if: ->(service) { service.password? }, - if: :activated? + if: ->(service) { service.activated? && service.password? } validates :password, presence: true, - if: ->(service) { service.username? }, - if: :activated? + if: ->(service) { service.activated? && service.username? } attr_accessor :response @@ -147,6 +145,6 @@ class TeamcityService < CiService '', headers: { 'Content-type' => 'application/xml' }, basic_auth: auth - ) + ) end end diff --git a/app/models/user.rb b/app/models/user.rb index fdd14f4571d..e0ce091c54e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -220,9 +220,9 @@ class User < ActiveRecord::Base def find_for_database_authentication(warden_conditions) conditions = warden_conditions.dup if login = conditions.delete(:login) - where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first + where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase) else - where(conditions).first + find_by(conditions) end end @@ -285,7 +285,7 @@ class User < ActiveRecord::Base end def by_username_or_id(name_or_id) - where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first + find_by('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i) end def build_user(attrs = {}) diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb index b26c7513f5b..8b3d56c2b4c 100644 --- a/app/services/merge_requests/refresh_service.rb +++ b/app/services/merge_requests/refresh_service.rb @@ -112,7 +112,7 @@ module MergeRequests merge_requests_for_source_branch.each do |merge_request| SystemNoteService.change_branch_presence( - merge_request, merge_request.project, @current_user, + merge_request, merge_request.project, @current_user, :source, @branch_name, presence) end end diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb index bfb8656df55..df28d30d750 100644 --- a/config/initializers/carrierwave.rb +++ b/config/initializers/carrierwave.rb @@ -31,11 +31,11 @@ if File.exists?(aws_file) if Rails.env.test? Fog.mock! connection = ::Fog::Storage.new( - aws_access_key_id: AWS_CONFIG['access_key_id'], - aws_secret_access_key: AWS_CONFIG['secret_access_key'], - provider: 'AWS', - region: AWS_CONFIG['region'] - ) + aws_access_key_id: AWS_CONFIG['access_key_id'], + aws_secret_access_key: AWS_CONFIG['secret_access_key'], + provider: 'AWS', + region: AWS_CONFIG['region'] + ) connection.directories.create(key: AWS_CONFIG['bucket']) end end diff --git a/config/routes.rb b/config/routes.rb index e2d4fcb65a8..57be57e3251 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -441,7 +441,7 @@ Rails.application.routes.draw do scope do post( - '/create_dir/*id', + '/create_dir/*id', to: 'tree#create_dir', constraints: { id: /.+/ }, as: 'create_dir' diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb index 87cd33c37eb..87f32e70d59 100644 --- a/features/steps/explore/groups.rb +++ b/features/steps/explore/groups.rb @@ -75,18 +75,18 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps name: projectname, path: "#{groupname}-#{projectname}", visibility_level: visibility_level - ) + ) create(:issue, title: "#{projectname} feature", project: project - ) + ) create(:merge_request, title: "#{projectname} feature implemented", source_project: project, target_project: project - ) + ) create(:closed_issue_event, project: project - ) + ) end end diff --git a/features/steps/explore/projects.rb b/features/steps/explore/projects.rb index f819dec2192..742ba5d71f6 100644 --- a/features/steps/explore/projects.rb +++ b/features/steps/explore/projects.rb @@ -61,11 +61,11 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps create(:issue, title: "Bug", project: public_project - ) + ) create(:issue, title: "New feature", project: public_project - ) + ) visit namespace_project_issues_path(public_project.namespace, public_project) end @@ -80,11 +80,11 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps create(:issue, title: "Internal Bug", project: internal_project - ) + ) create(:issue, title: "New internal feature", project: internal_project - ) + ) visit namespace_project_issues_path(internal_project.namespace, internal_project) end @@ -104,7 +104,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps title: "Bug fix for public project", source_project: public_project, target_project: public_project, - ) + ) end step 'I should see list of merge requests for "Community" project' do @@ -121,7 +121,7 @@ class Spinach::Features::ExploreProjects < Spinach::FeatureSteps title: "Feature implemented", source_project: internal_project, target_project: internal_project - ) + ) end step 'I should see list of merge requests for "Internal" project' do diff --git a/features/steps/groups.rb b/features/steps/groups.rb index f5e3fee61c0..4c5122d1b7d 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -85,7 +85,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I should see new group "Owned" avatar' do expect(owned_group.avatar).to be_instance_of AvatarUploader - expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif" + expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{Group.find_by(name:"Owned").id}/banana_sample.gif" end step 'I should see the "Remove avatar" button' do diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 40b2aa7c357..0305f7e6da0 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -34,7 +34,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I should see new avatar' do expect(@user.avatar).to be_instance_of AvatarUploader - expect(@user.avatar.url).to eq "/uploads/user/avatar/#{ @user.id }/banana_sample.gif" + expect(@user.avatar.url).to eq "/uploads/user/avatar/#{@user.id}/banana_sample.gif" end step 'I should see the "Remove avatar" button' do diff --git a/features/steps/project/project.rb b/features/steps/project/project.rb index 9ca7c8ebbc7..37bf52b4a95 100644 --- a/features/steps/project/project.rb +++ b/features/steps/project/project.rb @@ -37,7 +37,7 @@ class Spinach::Features::Project < Spinach::FeatureSteps step 'I should see new project avatar' do expect(@project.avatar).to be_instance_of AvatarUploader url = @project.avatar.url - expect(url).to eq "/uploads/project/avatar/#{ @project.id }/banana_sample.gif" + expect(url).to eq "/uploads/project/avatar/#{@project.id}/banana_sample.gif" end step 'I should see the "Remove avatar" button' do diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb index f2b95764267..f94661c12a6 100644 --- a/features/steps/project/source/browse_files.rb +++ b/features/steps/project/source/browse_files.rb @@ -230,13 +230,13 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I am redirected to the new file' do - expect(current_path).to eq(namespace_project_blob_path( - @project.namespace, @project, 'master/' + new_file_name)) + expect(current_path).to eq( + namespace_project_blob_path(@project.namespace, @project, 'master/' + new_file_name)) end step 'I am redirected to the new file with directory' do - expect(current_path).to eq(namespace_project_blob_path( - @project.namespace, @project, 'master/' + new_file_name_with_directory)) + expect(current_path).to eq( + namespace_project_blob_path(@project.namespace, @project, 'master/' + new_file_name_with_directory)) end step 'I am redirected to the new merge request page' do @@ -244,8 +244,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps end step 'I am redirected to the root directory' do - expect(current_path).to eq(namespace_project_tree_path( - @project.namespace, @project, 'master/')) + expect(current_path).to eq( + namespace_project_tree_path(@project.namespace, @project, 'master/')) end step "I don't see the permalink link" do diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index c74a5fd3bc7..b33bd332655 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -212,8 +212,8 @@ module SharedPaths end step 'I visit a binary file in the repo' do - visit namespace_project_blob_path(@project.namespace, @project, File.join( - root_ref, 'files/images/logo-black.png')) + visit namespace_project_blob_path(@project.namespace, @project, + File.join(root_ref, 'files/images/logo-black.png')) end step "I visit my project's commits page" do @@ -316,8 +316,8 @@ module SharedPaths end step 'I am on the ".gitignore" edit file page' do - expect(current_path).to eq(namespace_project_edit_blob_path( - @project.namespace, @project, File.join(root_ref, '.gitignore'))) + expect(current_path).to eq( + namespace_project_edit_blob_path(@project.namespace, @project, File.join(root_ref, '.gitignore'))) end step 'I visit project source page for "6d39438"' do diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b1cd80bdf65..a5daa45faf0 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -67,7 +67,7 @@ module API expose :shared_runners_enabled 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/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 87ac30b5ffe..459e3d6bcdb 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -2,7 +2,7 @@ module Gitlab class Shell class Error < StandardError; end - class KeyAdder < Struct.new(:io) + KeyAdder = Struct.new(:io) do def add_key(id, key) key.gsub!(/[[:space:]]+/, ' ').strip! io.puts("#{id}\t#{key}") diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb index 35e34d033e0..03aac1a025a 100644 --- a/lib/gitlab/bitbucket_import/project_creator.rb +++ b/lib/gitlab/bitbucket_import/project_creator.rb @@ -11,7 +11,8 @@ module Gitlab end def execute - project = ::Projects::CreateService.new(current_user, + project = ::Projects::CreateService.new( + current_user, name: repo["name"], path: repo["slug"], description: repo["description"], diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 142058aa69d..79061cd0141 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -46,11 +46,11 @@ module Gitlab end def added_lines - diff_lines.select(&:added?).size + diff_lines.count(&:added?) end def removed_lines - diff_lines.select(&:removed?).size + diff_lines.count(&:removed?) end end end diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 496256700b8..403ebeec474 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -199,7 +199,7 @@ module Gitlab s = s.gsub(/^#/, "\\#") s = s.gsub(/^-/, "\\-") s = s.gsub("`", "\\~") - s = s.gsub("\r", "") + s = s.delete("\r") s = s.gsub("\n", " \n") s end diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb index 8b1b6f48ed5..e0163499e30 100644 --- a/lib/gitlab/fogbugz_import/project_creator.rb +++ b/lib/gitlab/fogbugz_import/project_creator.rb @@ -12,7 +12,8 @@ module Gitlab end def execute - project = ::Projects::CreateService.new(current_user, + project = ::Projects::CreateService.new( + current_user, name: repo.safe_name, path: repo.path, namespace: namespace, diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb index d9452de6a50..7baaadb813c 100644 --- a/lib/gitlab/gitlab_import/project_creator.rb +++ b/lib/gitlab/gitlab_import/project_creator.rb @@ -11,7 +11,8 @@ module Gitlab end def execute - project = ::Projects::CreateService.new(current_user, + project = ::Projects::CreateService.new( + current_user, name: repo["name"], path: repo["path"], description: repo["description"], diff --git a/lib/gitlab/gitorious_import/project_creator.rb b/lib/gitlab/gitorious_import/project_creator.rb index cc9a91c91f4..8e22aa9286d 100644 --- a/lib/gitlab/gitorious_import/project_creator.rb +++ b/lib/gitlab/gitorious_import/project_creator.rb @@ -10,7 +10,8 @@ module Gitlab end def execute - ::Projects::CreateService.new(current_user, + ::Projects::CreateService.new( + current_user, name: repo.name, path: repo.path, description: repo.description, diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 87fee28dc01..62da327931f 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -171,8 +171,6 @@ module Gitlab when /\AMilestone:/ "#fee3ff" - when *@closed_statuses.map { |s| nice_status_name(s) } - "#cfcfcf" when "Status: New" "#428bca" when "Status: Accepted" @@ -199,6 +197,8 @@ module Gitlab "#8e44ad" when "Type: Other" "#7f8c8d" + when *@closed_statuses.map { |s| nice_status_name(s) } + "#cfcfcf" else "#e2e2e2" end @@ -227,7 +227,7 @@ module Gitlab s = s.gsub("`", "\\`") # Carriage returns make me sad - s = s.gsub("\r", "") + s = s.delete("\r") # Markdown ignores single newlines, but we need them as
    . s = s.gsub("\n", " \n") diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb index 1cb7d16aeb3..87821c23460 100644 --- a/lib/gitlab/google_code_import/project_creator.rb +++ b/lib/gitlab/google_code_import/project_creator.rb @@ -11,7 +11,8 @@ module Gitlab end def execute - project = ::Projects::CreateService.new(current_user, + project = ::Projects::CreateService.new( + current_user, name: repo.name, path: repo.name, description: repo.summary, diff --git a/lib/gitlab/markdown/filter/markdown_filter.rb b/lib/gitlab/markdown/filter/markdown_filter.rb index 921e2a0794e..b1b974fcc70 100644 --- a/lib/gitlab/markdown/filter/markdown_filter.rb +++ b/lib/gitlab/markdown/filter/markdown_filter.rb @@ -3,7 +3,7 @@ module Gitlab class MarkdownFilter < HTML::Pipeline::TextFilter def initialize(text, context = nil, result = nil) super text, context, result - @text = @text.gsub "\r", '' + @text = @text.delete "\r" end def call diff --git a/lib/gitlab/markdown/filter/table_of_contents_filter.rb b/lib/gitlab/markdown/filter/table_of_contents_filter.rb index bbb3bf7fc8b..6be644b0f67 100644 --- a/lib/gitlab/markdown/filter/table_of_contents_filter.rb +++ b/lib/gitlab/markdown/filter/table_of_contents_filter.rb @@ -31,7 +31,7 @@ module Gitlab id = text.downcase id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation - id.gsub!(' ', '-') # replace spaces with dash + id.tr!(' ', '-') # replace spaces with dash id.squeeze!('-') # replace multiple dashes with one uniq = (headers[id] > 0) ? "-#{headers[id]}" : '' diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb index 6762ca47c32..8c309efc7b8 100644 --- a/lib/rouge/formatters/html_gitlab.rb +++ b/lib/rouge/formatters/html_gitlab.rb @@ -39,7 +39,7 @@ module Rouge lineanchorsid: 'L', anchorlinenos: false, inline_theme: nil - ) + ) @nowrap = nowrap @cssclass = cssclass @linenos = linenos diff --git a/spec/factories.rb b/spec/factories.rb index 4bf93adabe2..d6b4efa9a03 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -43,7 +43,8 @@ FactoryGirl.define do end after(:create) do |user, evaluator| - user.identities << create(:identity, + user.identities << create( + :identity, provider: evaluator.provider, extern_uid: evaluator.extern_uid ) diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb index 4b78e3a61f0..65f8073c693 100644 --- a/spec/features/security/group_access_spec.rb +++ b/spec/features/security/group_access_spec.rb @@ -16,11 +16,11 @@ describe 'Group access', feature: true do end end - def group_member(access_level, group = group) + def group_member(access_level, grp = group()) level = Object.const_get("Gitlab::Access::#{access_level.upcase}") create(:user).tap do |user| - group.add_user(user, level) + grp.add_user(user, level) end end diff --git a/spec/helpers/groups_helper.rb b/spec/helpers/groups_helper.rb index 5d174460681..4ea90a80a92 100644 --- a/spec/helpers/groups_helper.rb +++ b/spec/helpers/groups_helper.rb @@ -9,7 +9,7 @@ describe GroupsHelper do group.avatar = File.open(avatar_file_path) group.save! expect(group_icon(group.path).to_s). - to match("/uploads/group/avatar/#{ group.id }/banana_sample.gif") + to match("/uploads/group/avatar/#{group.id}/banana_sample.gif") end it 'should give default avatar_icon when no avatar is present' do diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index ac61c8fb525..b193e16e7f8 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -37,14 +37,14 @@ describe Ci::Commit, models: true do it 'returns ordered list of commits' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project - commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project expect(project.ci_commits.ordered).to eq([commit2, commit1]) end it 'returns commits ordered by committed_at and id, with nulls last' do commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project - commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, project: project + commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1]) end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index d7fe01976d8..c962b83644a 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -81,7 +81,7 @@ describe Key, models: true do it 'rejects the multiple line key' do key = build(:key) - key.key.gsub!(' ', "\n") + key.key.tr!(' ', "\n") expect(key).not_to be_valid end end diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index a5662b08bda..91dd92b7c67 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -57,23 +57,21 @@ describe HipchatService, models: true do it 'should use v1 if version is provided' do allow(hipchat).to receive(:api_version).and_return('v1') - expect(HipChat::Client).to receive(:new). - with(token, - api_version: 'v1', - server_url: server_url). - and_return( - double(:hipchat_service).as_null_object) + expect(HipChat::Client).to receive(:new).with( + token, + api_version: 'v1', + server_url: server_url + ).and_return(double(:hipchat_service).as_null_object) hipchat.execute(push_sample_data) end it 'should use v2 as the version when nothing is provided' do allow(hipchat).to receive(:api_version).and_return('') - expect(HipChat::Client).to receive(:new). - with(token, - api_version: 'v2', - server_url: server_url). - and_return( - double(:hipchat_service).as_null_object) + expect(HipChat::Client).to receive(:new).with( + token, + api_version: 'v2', + server_url: server_url + ).and_return(double(:hipchat_service).as_null_object) hipchat.execute(push_sample_data) end diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb index ebf8837570e..06006b9a4f5 100644 --- a/spec/models/project_services/slack_service/note_message_spec.rb +++ b/spec/models/project_services/slack_service/note_message_spec.rb @@ -89,10 +89,10 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on an issue' do message = SlackService::NoteMessage.new(@args) expect(message.pretext).to eq( - "Test User commented on " \ - " in : " \ - "*issue title*") - expected_attachments = [ + "Test User commented on " \ + " in : " \ + "*issue title*") + expected_attachments = [ { text: "comment on an issue", color: color, diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index daa9d1087bf..376266c0955 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -462,8 +462,8 @@ describe User, models: true do expect(User.search(user1.username.downcase).to_a).to eq([user1]) expect(User.search(user2.username.upcase).to_a).to eq([user2]) expect(User.search(user2.username.downcase).to_a).to eq([user2]) - expect(User.search(user1.username.downcase).to_a.count).to eq(2) - expect(User.search(user2.username.downcase).to_a.count).to eq(1) + expect(User.search(user1.username.downcase).to_a.size).to eq(2) + expect(User.search(user2.username.downcase).to_a.size).to eq(1) end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index a91fa735321..e194eb93cf4 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -6,7 +6,7 @@ describe API::API, api: true do let(:user) { create(:user) } let!(:project) {create(:project, creator_id: user.id, namespace: user.namespace) } let!(:merge_request) { create(:merge_request, :simple, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time) } - let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.seconds) } + let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second) } let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds) } let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") } diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index b180d2fec77..fed9ae1949b 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -29,7 +29,7 @@ describe API::API, api: true do if required_attributes.empty? expected_code = 200 else - attrs.delete(required_attributes.shuffle.first) + attrs.delete(required_attributes.sample) expected_code = 400 end diff --git a/spec/services/create_commit_builds_service_spec.rb b/spec/services/create_commit_builds_service_spec.rb index 798c480b81a..40ba6549e1f 100644 --- a/spec/services/create_commit_builds_service_spec.rb +++ b/spec/services/create_commit_builds_service_spec.rb @@ -17,7 +17,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: [{ message: "Message" }] - ) + ) end it { expect(commit).to be_kind_of(Ci::Commit) } @@ -34,7 +34,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: [{ message: "Message" }] - ) + ) expect(result).to be_persisted end @@ -47,7 +47,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: [{ message: "Message" }] - ) + ) expect(result).to be_persisted end end @@ -59,7 +59,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: [{ message: 'Message' }] - ) + ) expect(result).to be_persisted expect(result.builds.any?).to be_falsey expect(result.status).to eq('skipped') @@ -76,7 +76,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.any?).to be false expect(commit.status).to eq('failed') expect(commit.yaml_errors).to_not be_nil @@ -96,7 +96,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") end @@ -110,7 +110,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.first.name).to eq("staging") end @@ -123,7 +123,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.any?).to be false expect(commit.status).to eq("skipped") expect(commit.yaml_errors).to be_nil @@ -139,7 +139,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.count(:all)).to eq(2) commit = service.execute(project, user, @@ -147,7 +147,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.builds.count(:all)).to eq(2) end @@ -161,7 +161,7 @@ describe CreateCommitBuildsService, services: true do before: '00000000', after: '31das312', commits: commits - ) + ) expect(commit.status).to eq("failed") expect(commit.builds.any?).to be false diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index e2d15f1a83d..b982274c529 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -58,14 +58,14 @@ describe GitTagPushService, services: true do it { is_expected.to include(timestamp: @commit.date.xmlschema) } it do is_expected.to include( - url: [ - Gitlab.config.gitlab.url, - project.namespace.to_param, - project.to_param, - 'commit', - @commit.id - ].join('/') - ) + url: [ + Gitlab.config.gitlab.url, + project.namespace.to_param, + project.to_param, + 'commit', + @commit.id + ].join('/') + ) end context "with a author" do diff --git a/spec/services/update_snippet_service_spec.rb b/spec/services/update_snippet_service_spec.rb index 124bb76e678..48d114896d0 100644 --- a/spec/services/update_snippet_service_spec.rb +++ b/spec/services/update_snippet_service_spec.rb @@ -42,7 +42,7 @@ describe UpdateSnippetService, services: true do CreateSnippetService.new(project, user, opts).execute end - def update_snippet(project = nil, user, snippet, opts) + def update_snippet(project, user, snippet, opts) UpdateSnippetService.new(project, user, snippet, opts).execute end end diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb index aadf791bf3f..aa8258d6dad 100644 --- a/spec/support/repo_helpers.rb +++ b/spec/support/repo_helpers.rb @@ -45,12 +45,12 @@ eos def another_sample_commit OpenStruct.new( - id: "e56497bb5f03a90a51293fc6d516788730953899", - parent_id: '4cd80ccab63c82b4bad16faa5193fbd2aa06df40', - author_full_name: "Sytse Sijbrandij", - author_email: "sytse@gitlab.com", - files_changed_count: 1, - message: < Date: Tue, 15 Dec 2015 00:54:26 -0200 Subject: Disabled Rails/Date cop --- .rubocop.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index e92f10a7917..89aa0591c31 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -979,6 +979,12 @@ Rails/ActionFilter: Description: 'Enforces consistent use of action filter methods.' Enabled: true +Rails/Date: + Description: >- + Checks the correct usage of date aware methods, + such as Date.today, Date.current etc. + Enabled: false + Rails/DefaultScope: Description: 'Checks if the argument passed to default_scope is a block.' Enabled: false -- cgit v1.2.1 From 8e3f1fa629a61741282214b293c1bc9438aada59 Mon Sep 17 00:00:00 2001 From: tduehr Date: Wed, 11 Nov 2015 22:25:31 -0600 Subject: add CAS authentication support --- CHANGELOG | 1 + Gemfile | 1 + Gemfile.lock | 5 ++ app/controllers/application_controller.rb | 15 ++++++ app/controllers/omniauth_callbacks_controller.rb | 16 +++++- config/gitlab.yml.example | 13 +++++ config/initializers/1_settings.rb | 4 ++ config/initializers/devise.rb | 10 ++++ doc/integration/cas.md | 62 ++++++++++++++++++++++++ lib/gitlab/o_auth/session.rb | 17 +++++++ 10 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 doc/integration/cas.md create mode 100644 lib/gitlab/o_auth/session.rb diff --git a/CHANGELOG b/CHANGELOG index 7f9dfd98cd7..0089b2d8b4a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -66,6 +66,7 @@ v 8.2.3 v 8.2.3 - Webhook payload has an added, modified and removed properties for each commit - Fix 500 error when creating a merge request that removes a submodule + - Add CAS support (tduehr) v 8.2.2 - Fix 404 in redirection after removing a project (Stan Hu) diff --git a/Gemfile b/Gemfile index 7298e21ce66..fc6638b7510 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,7 @@ gem 'devise-async', '~> 0.9.0' gem 'doorkeeper', '~> 2.2.0' gem 'omniauth', '~> 1.2.2' gem 'omniauth-bitbucket', '~> 0.0.2' +gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-facebook', '~> 3.0.0' gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index ff57460f5bb..2f06713e5d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -458,6 +458,10 @@ GEM multi_json (~> 1.7) omniauth (~> 1.1) omniauth-oauth (~> 1.0) + omniauth-cas3 (1.1.3) + addressable (~> 2.3) + nokogiri (~> 1.6.6) + omniauth (~> 1.2) omniauth-facebook (3.0.0) omniauth-oauth2 (~> 1.2) omniauth-github (1.1.2) @@ -913,6 +917,7 @@ DEPENDENCIES octokit (~> 3.7.0) omniauth (~> 1.2.2) omniauth-bitbucket (~> 0.0.2) + omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.0) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0d182e8eb04..01e2e7b2f98 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,6 +10,7 @@ class ApplicationController < ActionController::Base before_action :authenticate_user_from_token! before_action :authenticate_user! + before_action :validate_user_service_ticket! before_action :reject_blocked! before_action :check_password_expiration before_action :ldap_security_check @@ -202,6 +203,20 @@ class ApplicationController < ActionController::Base end end + def validate_user_service_ticket! + return unless signed_in? && session[:service_tickets] + + valid = session[:service_tickets].all? do |provider, ticket| + Gitlab::OAuth::Session.valid?(provider, ticket) + end + + unless valid + session[:service_tickets] = nil + sign_out current_user + redirect_to new_user_session_path + end + end + def check_password_expiration if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user? redirect_to new_profile_password_path and return diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index f809fa7500a..4cad98b8e98 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,6 +1,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController - protect_from_forgery except: [:kerberos, :saml] + protect_from_forgery except: [:kerberos, :saml, :cas3] Gitlab.config.omniauth.providers.each do |provider| define_method provider['name'] do @@ -42,6 +42,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController render 'errors/omniauth_error', layout: "errors", status: 422 end + def cas3 + ticket = params['ticket'] + if ticket + handle_service_ticket oauth['provider'], ticket + end + handle_omniauth + end + private def handle_omniauth @@ -84,6 +92,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to new_user_session_path end + def handle_service_ticket provider, ticket + Gitlab::OAuth::Session.create provider, ticket + session[:service_tickets] ||= {} + session[:service_tickets][provider] = ticket + end + def oauth @oauth ||= request.env['omniauth.auth'] end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index db378118f85..fcf034d7911 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -287,6 +287,15 @@ production: &base # arguments, followed by optional 'args' which can be either a hash or an array. # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html providers: + # See omniauth-cas3 for more configuration details + # - { name: 'cas3', + # label: 'cas3', + # args: { + # url: 'https://sso.example.com', + # disable_ssl_verification: false, + # login_url: '/cas/login', + # service_validate_url: '/cas/p3/serviceValidate', + # logout_url: '/cas/logout'} } # - { name: 'github', # app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET', @@ -324,6 +333,10 @@ production: &base # application_name: 'YOUR_APP_NAME', # application_password: 'YOUR_APP_PASSWORD' } } + # SSO maximum session duration in seconds. Defaults to CAS default of 8 hours. + # cas3: + # session_duration: 28800 + # Shared file storage settings shared: # path: /mnt/gitlab # Default: shared diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 63d8ae17436..fe0aaaedde5 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -126,6 +126,10 @@ Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil? Settings.omniauth['providers'] ||= [] +Settings.omniauth['cas3'] ||= Settingslogic.new({}) +Settings.omniauth.cas3['session_duration'] ||= 8.hours +Settings.omniauth['session_tickets'] ||= Settingslogic.new({}) +Settings.omniauth.session_tickets['cas3'] = 'ticket' Settings['shared'] ||= Settingslogic.new({}) Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 5fb43a86e13..92149826da7 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -241,6 +241,16 @@ Devise.setup do |config| # An Array from the configuration will be expanded. provider_arguments.concat provider['args'] when Hash + # Add procs for handling SLO + if provider['name'] == 'cas3' + provider['args'][:on_single_sign_out] = lambda do |request| + ticket = request.params[:session_index] + raise "Service Ticket not found." unless Gitlab::OAuth::Session.valid?(:cas3, ticket) + Gitlab::OAuth::Session.destroy(:cas3, ticket) + true + end + end + # A Hash from the configuration will be passed as is. provider_arguments << provider['args'].symbolize_keys end diff --git a/doc/integration/cas.md b/doc/integration/cas.md new file mode 100644 index 00000000000..3490f6a38e0 --- /dev/null +++ b/doc/integration/cas.md @@ -0,0 +1,62 @@ +# CAS OmniAuth Provider + +To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL gitlab will supply to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout. + +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For instalations from source: + + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider configuration: + + For omnibus package: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + name: "cas3", + label: "cas", + args: { + url: 'CAS_SERVER', + login_url: '/CAS_PATH/login', + service_validate_url: '/CAS_PATH/p3/serviceValidate', + logout_url: '/CAS_PATH/logout'} } + } + } + ] + ``` + + For installations from source: + + ``` + - { name: 'cas3', + label: 'cas', + args: { + url: 'CAS_SERVER', + login_url: '/CAS_PATH/login', + service_validate_url: '/CAS_PATH/p3/serviceValidate', + logout_url: '/CAS_PATH/logout'} } + ``` + +1. Change 'CAS_PATH' to the root of your CAS instance (ie. `cas`). + +1. If your CAS instance does not use default TGC lifetimes, update the `cas3.session_duration` to at least the current TGC maximum lifetime. To explicitly disable SLO, regardless of CAS settings, set this to 0. + +1. Save the configuration file. + +1. Restart GitLab for the changes to take effect. + +On the sign in page there should now be a CAS tab in the sign in form. \ No newline at end of file diff --git a/lib/gitlab/o_auth/session.rb b/lib/gitlab/o_auth/session.rb new file mode 100644 index 00000000000..f33bfd0bd0e --- /dev/null +++ b/lib/gitlab/o_auth/session.rb @@ -0,0 +1,17 @@ +module Gitlab + module OAuth + module Session + def self.create(provider, ticket) + Rails.cache.write("gitlab:#{provider}:#{ticket}", ticket, expires_in: Gitlab.config.omniauth.cas3.session_duration) + end + + def self.destroy(provider, ticket) + Rails.cache.delete("gitlab:#{provider}:#{ticket}") + end + + def self.valid?(provider, ticket) + Rails.cache.read("gitlab:#{provider}:#{ticket}").present? + end + end + end +end -- cgit v1.2.1 From c12514fc2d3996e7cfc3553d8e2bac04d0c5afec Mon Sep 17 00:00:00 2001 From: The rugged tests are fragile Date: Tue, 15 Dec 2015 15:05:57 +0800 Subject: Ignore config/sidekiq.yml [ci skip] --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f5b6427ca03..91ea81bfc4e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ config/initializers/smtp_settings.rb config/resque.yml config/unicorn.rb config/secrets.yml +config/sidekiq.yml coverage/* db/*.sqlite3 db/*.sqlite3-journal -- cgit v1.2.1 From 7c14541d2bb9735402086c951730344fa5203278 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 12 Dec 2015 23:42:52 +0100 Subject: Add feature specs for note polling Ref. #4032, !2084 --- spec/features/issues/note_polling_spec.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 spec/features/issues/note_polling_spec.rb diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb new file mode 100644 index 00000000000..1698a0a3e5d --- /dev/null +++ b/spec/features/issues/note_polling_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +feature 'Issue notes polling' do + let!(:project) { create(:project, :public) } + let!(:issue) { create(:issue, project: project) } + + background do + visit namespace_project_issue_path(project.namespace, project, issue) + end + + scenario 'Another user adds a comment to an issue', js: true do + note = create(:note_on_issue, noteable: issue, + project_id: project.project_id, + note: 'Looks good!') + sleep 16 # refresh interval in notes.js.coffee, 15 seconds + expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') + end +end -- cgit v1.2.1 From 9470d05c70c15df75ae6be45812967abf89ae59a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sun, 13 Dec 2015 00:39:47 +0100 Subject: Add spinach test for note polling This also increases capybara timeout to 15 seconds (note polling interval). Capybara will look for new note for this period of time. --- features/project/issues/issues.feature | 6 ++++++ features/steps/project/issues/issues.rb | 10 ++++++++++ features/support/capybara.rb | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature index f08b30e0b88..ab234bc7507 100644 --- a/features/project/issues/issues.feature +++ b/features/project/issues/issues.feature @@ -197,3 +197,9 @@ Feature: Project Issues And I should not see labels field And I submit new issue "500 error on profile" Then I should see issue "500 error on profile" + + @javascript + Scenario: Another user adds a comment to issue I'm currently viewing + Given I visit issue page "Release 0.4" + And another user adds a comment with text "Yay!" to issue "Release 0.4" + Then I should see a new comment with text "Yay!" diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index a13044c3ae1..4a7ff21d385 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -284,6 +284,16 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end end + step 'another user adds a comment with text "Yay!" to issue "Release 0.4"' do + issue = Issue.find_by!(title: 'Release 0.4') + create(:note_on_issue, noteable: issue, note: 'Yay!') + end + + step 'I should see a new comment with text "Yay!"' do + page.within '#notes' do + expect(page).to have_content('Yay!') + end + end def filter_issue(text) fill_in 'issue_search', with: text end diff --git a/features/support/capybara.rb b/features/support/capybara.rb index 31dbf0feb2f..4156c7ec484 100644 --- a/features/support/capybara.rb +++ b/features/support/capybara.rb @@ -2,7 +2,7 @@ require 'spinach/capybara' require 'capybara/poltergeist' # Give CI some extra time -timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 10 +timeout = (ENV['CI'] || ENV['CI_SERVER']) ? 90 : 15 Capybara.javascript_driver = :poltergeist Capybara.register_driver :poltergeist do |app| -- cgit v1.2.1 From 3e789eab8be93c45685c417488da102d996209ba Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sun, 13 Dec 2015 00:44:45 +0100 Subject: Add minor fixes in note polling specs --- spec/features/issues/note_polling_spec.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index 1698a0a3e5d..5cf3dcf8904 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -9,10 +9,8 @@ feature 'Issue notes polling' do end scenario 'Another user adds a comment to an issue', js: true do - note = create(:note_on_issue, noteable: issue, - project_id: project.project_id, - note: 'Looks good!') - sleep 16 # refresh interval in notes.js.coffee, 15 seconds + note = create(:note_on_issue, noteable: issue, note: 'Looks good!') + sleep 15 # refresh interval in notes.js.coffee is 15 seconds expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') end end -- cgit v1.2.1 From 6fb120d1b0ad8adee7c5c431c7f8a399a2da5cea Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 09:41:30 +0100 Subject: Assign notes object to a variable --- app/views/projects/notes/_notes_with_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml index 99c1b0fa43e..eb378b42603 100644 --- a/app/views/projects/notes/_notes_with_form.html.haml +++ b/app/views/projects/notes/_notes_with_form.html.haml @@ -7,4 +7,4 @@ = render "projects/notes/form", view: diff_view :javascript - new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}") + var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}") -- cgit v1.2.1 From 8fe821cfa5acea2a4ab1fce7f35a76455d5a1cac Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 14 Dec 2015 09:41:59 +0100 Subject: Trigger notes refresh in specs instead of waiting for ajax --- spec/features/issues/note_polling_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb index 5cf3dcf8904..e4efdbe2421 100644 --- a/spec/features/issues/note_polling_spec.rb +++ b/spec/features/issues/note_polling_spec.rb @@ -10,7 +10,7 @@ feature 'Issue notes polling' do scenario 'Another user adds a comment to an issue', js: true do note = create(:note_on_issue, noteable: issue, note: 'Looks good!') - sleep 15 # refresh interval in notes.js.coffee is 15 seconds + page.execute_script('notes.refresh();') expect(page).to have_selector("#note_#{note.id}", text: 'Looks good!') end end -- cgit v1.2.1 From 3581927f5932746562581ff45052a62660f83a90 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 15 Dec 2015 14:30:00 +0100 Subject: Require gitlab-workhorse 0.5.0 --- GITLAB_WORKHORSE_VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 2b7c5ae0184..8f0916f768f 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.4.2 +0.5.0 -- cgit v1.2.1 From 7781bda9bd82997f4a03de4cf911b1156ceb2cde Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 15 Dec 2015 15:51:16 +0100 Subject: Move Markdown/reference logic from Gitlab::Markdown to Banzai --- app/helpers/gitlab_markdown_helper.rb | 6 +- app/helpers/issues_helper.rb | 2 +- app/helpers/labels_helper.rb | 2 +- app/models/concerns/mentionable.rb | 16 +- app/models/concerns/participable.rb | 29 +-- app/models/issue.rb | 2 +- app/models/note.rb | 4 +- lib/banzai.rb | 13 ++ lib/banzai/cross_project_reference.rb | 22 +++ lib/banzai/filter.rb | 10 + lib/banzai/filter/abstract_reference_filter.rb | 145 ++++++++++++++ lib/banzai/filter/autolink_filter.rb | 107 +++++++++++ lib/banzai/filter/commit_range_reference_filter.rb | 58 ++++++ lib/banzai/filter/commit_reference_filter.rb | 63 ++++++ lib/banzai/filter/emoji_filter.rb | 80 ++++++++ .../filter/external_issue_reference_filter.rb | 69 +++++++ lib/banzai/filter/external_link_filter.rb | 34 ++++ lib/banzai/filter/issue_reference_filter.rb | 23 +++ lib/banzai/filter/label_reference_filter.rb | 96 ++++++++++ lib/banzai/filter/markdown_filter.rb | 42 ++++ .../filter/merge_request_reference_filter.rb | 41 ++++ lib/banzai/filter/redactor_filter.rb | 43 +++++ lib/banzai/filter/reference_filter.rb | 190 +++++++++++++++++++ lib/banzai/filter/reference_gatherer_filter.rb | 62 ++++++ lib/banzai/filter/relative_link_filter.rb | 157 +++++++++++++++ lib/banzai/filter/sanitization_filter.rb | 99 ++++++++++ lib/banzai/filter/snippet_reference_filter.rb | 25 +++ lib/banzai/filter/syntax_highlight_filter.rb | 45 +++++ lib/banzai/filter/table_of_contents_filter.rb | 63 ++++++ lib/banzai/filter/task_list_filter.rb | 24 +++ lib/banzai/filter/upload_link_filter.rb | 47 +++++ lib/banzai/filter/user_reference_filter.rb | 129 +++++++++++++ lib/banzai/lazy_reference.rb | 27 +++ lib/banzai/pipeline.rb | 10 + lib/banzai/pipeline/asciidoc_pipeline.rb | 13 ++ lib/banzai/pipeline/atom_pipeline.rb | 14 ++ lib/banzai/pipeline/base_pipeline.rb | 30 +++ lib/banzai/pipeline/combined_pipeline.rb | 27 +++ lib/banzai/pipeline/description_pipeline.rb | 14 ++ lib/banzai/pipeline/email_pipeline.rb | 13 ++ lib/banzai/pipeline/full_pipeline.rb | 9 + lib/banzai/pipeline/gfm_pipeline.rb | 41 ++++ lib/banzai/pipeline/note_pipeline.rb | 14 ++ lib/banzai/pipeline/plain_markdown_pipeline.rb | 13 ++ lib/banzai/pipeline/post_process_pipeline.rb | 20 ++ .../pipeline/reference_extraction_pipeline.rb | 13 ++ lib/banzai/pipeline/single_line_pipeline.rb | 9 + lib/banzai/reference_extractor.rb | 55 ++++++ lib/banzai/renderer.rb | 76 ++++++++ lib/gitlab/asciidoc.rb | 2 +- lib/gitlab/markdown.rb | 115 ----------- lib/gitlab/markdown/abstract_reference_filter.rb | 145 -------------- lib/gitlab/markdown/combined_pipeline.rb | 27 --- lib/gitlab/markdown/cross_project_reference.rb | 24 --- lib/gitlab/markdown/filter/autolink_filter.rb | 107 ----------- .../filter/commit_range_reference_filter.rb | 58 ------ .../markdown/filter/commit_reference_filter.rb | 63 ------ lib/gitlab/markdown/filter/emoji_filter.rb | 80 -------- .../filter/external_issue_reference_filter.rb | 69 ------- lib/gitlab/markdown/filter/external_link_filter.rb | 34 ---- .../markdown/filter/issue_reference_filter.rb | 23 --- .../markdown/filter/label_reference_filter.rb | 96 ---------- lib/gitlab/markdown/filter/markdown_filter.rb | 39 ---- .../filter/merge_request_reference_filter.rb | 41 ---- lib/gitlab/markdown/filter/redactor_filter.rb | 43 ----- .../markdown/filter/reference_gatherer_filter.rb | 63 ------ lib/gitlab/markdown/filter/relative_link_filter.rb | 157 --------------- lib/gitlab/markdown/filter/sanitization_filter.rb | 99 ---------- .../markdown/filter/snippet_reference_filter.rb | 25 --- .../markdown/filter/syntax_highlight_filter.rb | 45 ----- .../markdown/filter/table_of_contents_filter.rb | 63 ------ lib/gitlab/markdown/filter/task_list_filter.rb | 24 --- lib/gitlab/markdown/filter/upload_link_filter.rb | 47 ----- .../markdown/filter/user_reference_filter.rb | 129 ------------- lib/gitlab/markdown/pipeline.rb | 4 +- lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb | 13 -- lib/gitlab/markdown/pipeline/atom_pipeline.rb | 14 -- .../markdown/pipeline/description_pipeline.rb | 14 -- lib/gitlab/markdown/pipeline/email_pipeline.rb | 13 -- lib/gitlab/markdown/pipeline/full_pipeline.rb | 9 - lib/gitlab/markdown/pipeline/gfm_pipeline.rb | 41 ---- lib/gitlab/markdown/pipeline/note_pipeline.rb | 14 -- .../markdown/pipeline/plain_markdown_pipeline.rb | 13 -- .../markdown/pipeline/post_process_pipeline.rb | 20 -- .../pipeline/reference_extraction_pipeline.rb | 13 -- .../markdown/pipeline/single_line_pipeline.rb | 9 - lib/gitlab/markdown/reference_filter.rb | 211 --------------------- lib/gitlab/reference_extractor.rb | 53 +----- .../lib/gitlab/markdown/reference_filter_spec.rb | 2 +- spec/lib/banzai/cross_project_reference_spec.rb | 34 ++++ spec/lib/banzai/filter/autolink_filter_spec.rb | 112 +++++++++++ .../filter/commit_range_reference_filter_spec.rb | 182 ++++++++++++++++++ .../banzai/filter/commit_reference_filter_spec.rb | 163 ++++++++++++++++ spec/lib/banzai/filter/emoji_filter_spec.rb | 98 ++++++++++ .../filter/external_issue_reference_filter_spec.rb | 77 ++++++++ .../lib/banzai/filter/external_link_filter_spec.rb | 29 +++ .../banzai/filter/issue_reference_filter_spec.rb | 209 ++++++++++++++++++++ .../banzai/filter/label_reference_filter_spec.rb | 179 +++++++++++++++++ .../filter/merge_request_reference_filter_spec.rb | 142 ++++++++++++++ spec/lib/banzai/filter/redactor_filter_spec.rb | 89 +++++++++ .../filter/reference_gatherer_filter_spec.rb | 87 +++++++++ .../lib/banzai/filter/relative_link_filter_spec.rb | 147 ++++++++++++++ spec/lib/banzai/filter/sanitization_filter_spec.rb | 197 +++++++++++++++++++ .../banzai/filter/snippet_reference_filter_spec.rb | 146 ++++++++++++++ .../banzai/filter/syntax_highlight_filter_spec.rb | 17 ++ .../banzai/filter/table_of_contents_filter_spec.rb | 97 ++++++++++ spec/lib/banzai/filter/task_list_filter_spec.rb | 10 + spec/lib/banzai/filter/upload_link_filter_spec.rb | 73 +++++++ .../banzai/filter/user_reference_filter_spec.rb | 147 ++++++++++++++ spec/lib/gitlab/asciidoc_spec.rb | 2 +- .../markdown/cross_project_reference_spec.rb | 34 ---- .../gitlab/markdown/filter/autolink_filter_spec.rb | 112 ----------- .../filter/commit_range_reference_filter_spec.rb | 182 ------------------ .../filter/commit_reference_filter_spec.rb | 163 ---------------- .../gitlab/markdown/filter/emoji_filter_spec.rb | 98 ---------- .../filter/external_issue_reference_filter_spec.rb | 77 -------- .../markdown/filter/external_link_filter_spec.rb | 29 --- .../markdown/filter/issue_reference_filter_spec.rb | 209 -------------------- .../markdown/filter/label_reference_filter_spec.rb | 179 ----------------- .../filter/merge_request_reference_filter_spec.rb | 142 -------------- .../gitlab/markdown/filter/redactor_filter_spec.rb | 89 --------- .../filter/reference_gatherer_filter_spec.rb | 87 --------- .../markdown/filter/relative_link_filter_spec.rb | 147 -------------- .../markdown/filter/sanitization_filter_spec.rb | 197 ------------------- .../filter/snippet_reference_filter_spec.rb | 146 -------------- .../filter/syntax_highlight_filter_spec.rb | 17 -- .../filter/table_of_contents_filter_spec.rb | 97 ---------- .../markdown/filter/task_list_filter_spec.rb | 10 - .../markdown/filter/upload_link_filter_spec.rb | 73 ------- .../markdown/filter/user_reference_filter_spec.rb | 147 -------------- spec/support/filter_spec_helper.rb | 36 ++-- 131 files changed, 4383 insertions(+), 4332 deletions(-) create mode 100644 lib/banzai.rb create mode 100644 lib/banzai/cross_project_reference.rb create mode 100644 lib/banzai/filter.rb create mode 100644 lib/banzai/filter/abstract_reference_filter.rb create mode 100644 lib/banzai/filter/autolink_filter.rb create mode 100644 lib/banzai/filter/commit_range_reference_filter.rb create mode 100644 lib/banzai/filter/commit_reference_filter.rb create mode 100644 lib/banzai/filter/emoji_filter.rb create mode 100644 lib/banzai/filter/external_issue_reference_filter.rb create mode 100644 lib/banzai/filter/external_link_filter.rb create mode 100644 lib/banzai/filter/issue_reference_filter.rb create mode 100644 lib/banzai/filter/label_reference_filter.rb create mode 100644 lib/banzai/filter/markdown_filter.rb create mode 100644 lib/banzai/filter/merge_request_reference_filter.rb create mode 100644 lib/banzai/filter/redactor_filter.rb create mode 100644 lib/banzai/filter/reference_filter.rb create mode 100644 lib/banzai/filter/reference_gatherer_filter.rb create mode 100644 lib/banzai/filter/relative_link_filter.rb create mode 100644 lib/banzai/filter/sanitization_filter.rb create mode 100644 lib/banzai/filter/snippet_reference_filter.rb create mode 100644 lib/banzai/filter/syntax_highlight_filter.rb create mode 100644 lib/banzai/filter/table_of_contents_filter.rb create mode 100644 lib/banzai/filter/task_list_filter.rb create mode 100644 lib/banzai/filter/upload_link_filter.rb create mode 100644 lib/banzai/filter/user_reference_filter.rb create mode 100644 lib/banzai/lazy_reference.rb create mode 100644 lib/banzai/pipeline.rb create mode 100644 lib/banzai/pipeline/asciidoc_pipeline.rb create mode 100644 lib/banzai/pipeline/atom_pipeline.rb create mode 100644 lib/banzai/pipeline/base_pipeline.rb create mode 100644 lib/banzai/pipeline/combined_pipeline.rb create mode 100644 lib/banzai/pipeline/description_pipeline.rb create mode 100644 lib/banzai/pipeline/email_pipeline.rb create mode 100644 lib/banzai/pipeline/full_pipeline.rb create mode 100644 lib/banzai/pipeline/gfm_pipeline.rb create mode 100644 lib/banzai/pipeline/note_pipeline.rb create mode 100644 lib/banzai/pipeline/plain_markdown_pipeline.rb create mode 100644 lib/banzai/pipeline/post_process_pipeline.rb create mode 100644 lib/banzai/pipeline/reference_extraction_pipeline.rb create mode 100644 lib/banzai/pipeline/single_line_pipeline.rb create mode 100644 lib/banzai/reference_extractor.rb create mode 100644 lib/banzai/renderer.rb delete mode 100644 lib/gitlab/markdown.rb delete mode 100644 lib/gitlab/markdown/abstract_reference_filter.rb delete mode 100644 lib/gitlab/markdown/combined_pipeline.rb delete mode 100644 lib/gitlab/markdown/cross_project_reference.rb delete mode 100644 lib/gitlab/markdown/filter/autolink_filter.rb delete mode 100644 lib/gitlab/markdown/filter/commit_range_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/commit_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/emoji_filter.rb delete mode 100644 lib/gitlab/markdown/filter/external_issue_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/external_link_filter.rb delete mode 100644 lib/gitlab/markdown/filter/issue_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/label_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/markdown_filter.rb delete mode 100644 lib/gitlab/markdown/filter/merge_request_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/redactor_filter.rb delete mode 100644 lib/gitlab/markdown/filter/reference_gatherer_filter.rb delete mode 100644 lib/gitlab/markdown/filter/relative_link_filter.rb delete mode 100644 lib/gitlab/markdown/filter/sanitization_filter.rb delete mode 100644 lib/gitlab/markdown/filter/snippet_reference_filter.rb delete mode 100644 lib/gitlab/markdown/filter/syntax_highlight_filter.rb delete mode 100644 lib/gitlab/markdown/filter/table_of_contents_filter.rb delete mode 100644 lib/gitlab/markdown/filter/task_list_filter.rb delete mode 100644 lib/gitlab/markdown/filter/upload_link_filter.rb delete mode 100644 lib/gitlab/markdown/filter/user_reference_filter.rb delete mode 100644 lib/gitlab/markdown/pipeline/asciidoc_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/atom_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/description_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/email_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/full_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/gfm_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/note_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/plain_markdown_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/post_process_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/reference_extraction_pipeline.rb delete mode 100644 lib/gitlab/markdown/pipeline/single_line_pipeline.rb delete mode 100644 lib/gitlab/markdown/reference_filter.rb create mode 100644 spec/lib/banzai/cross_project_reference_spec.rb create mode 100644 spec/lib/banzai/filter/autolink_filter_spec.rb create mode 100644 spec/lib/banzai/filter/commit_range_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/commit_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/emoji_filter_spec.rb create mode 100644 spec/lib/banzai/filter/external_issue_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/external_link_filter_spec.rb create mode 100644 spec/lib/banzai/filter/issue_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/label_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/merge_request_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/redactor_filter_spec.rb create mode 100644 spec/lib/banzai/filter/reference_gatherer_filter_spec.rb create mode 100644 spec/lib/banzai/filter/relative_link_filter_spec.rb create mode 100644 spec/lib/banzai/filter/sanitization_filter_spec.rb create mode 100644 spec/lib/banzai/filter/snippet_reference_filter_spec.rb create mode 100644 spec/lib/banzai/filter/syntax_highlight_filter_spec.rb create mode 100644 spec/lib/banzai/filter/table_of_contents_filter_spec.rb create mode 100644 spec/lib/banzai/filter/task_list_filter_spec.rb create mode 100644 spec/lib/banzai/filter/upload_link_filter_spec.rb create mode 100644 spec/lib/banzai/filter/user_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/cross_project_reference_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/autolink_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/commit_range_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/commit_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/emoji_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/external_issue_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/external_link_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/issue_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/label_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/merge_request_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/redactor_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/reference_gatherer_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/relative_link_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/sanitization_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/snippet_reference_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/syntax_highlight_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/table_of_contents_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/task_list_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/upload_link_filter_spec.rb delete mode 100644 spec/lib/gitlab/markdown/filter/user_reference_filter_spec.rb diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 5004e02ea0b..a0cf3dc0843 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -20,7 +20,7 @@ module GitlabMarkdownHelper end user = current_user if defined?(current_user) - gfm_body = Gitlab::Markdown.render(escaped_body, project: @project, current_user: user, pipeline: :single_line) + gfm_body = Banzai.render(escaped_body, project: @project, current_user: user, pipeline: :single_line) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body) if fragment.children.size == 1 && fragment.children[0].name == 'a' @@ -50,7 +50,7 @@ module GitlabMarkdownHelper context[:project] ||= @project - html = Gitlab::Markdown.render(text, context) + html = Banzai.render(text, context) context.merge!( current_user: (current_user if defined?(current_user)), @@ -61,7 +61,7 @@ module GitlabMarkdownHelper ref: @ref ) - Gitlab::Markdown.post_process(html, context) + Banzai.post_process(html, context) end def asciidoc(text) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index e66b9c628c7..21149a15b69 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -121,6 +121,6 @@ module IssuesHelper end end - # Required for Gitlab::Markdown::IssueReferenceFilter + # Required for Banzai::IssueReferenceFilter module_function :url_for_issue end diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index 795fb439f25..97e8baa179a 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -107,6 +107,6 @@ module LabelsHelper options_from_collection_for_select(grouped_labels, 'name', 'title', params[:label_name]) end - # Required for Gitlab::Markdown::LabelReferenceFilter + # Required for Banzai::LabelReferenceFilter module_function :render_colored_label, :text_color_for_bg, :escape_once end diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index d2ea9ab7313..d4e3099453d 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -23,7 +23,7 @@ module Mentionable included do if self < Participable - participant ->(current_user) { mentioned_users(current_user, load_lazy_references: false) } + participant ->(current_user) { mentioned_users(current_user) } end end @@ -43,9 +43,9 @@ module Mentionable self end - def all_references(current_user = self.author, text = nil, load_lazy_references: true) - ext = Gitlab::ReferenceExtractor.new(self.project, current_user, load_lazy_references: load_lazy_references) - + def all_references(current_user = self.author, text = nil) + ext = Gitlab::ReferenceExtractor.new(self.project, current_user) + if text ext.analyze(text) else @@ -59,13 +59,13 @@ module Mentionable ext end - def mentioned_users(current_user = nil, load_lazy_references: true) - all_references(current_user, load_lazy_references: load_lazy_references).users + def mentioned_users(current_user = nil) + all_references(current_user).users end # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. - def referenced_mentionables(current_user = self.author, text = nil, load_lazy_references: true) - refs = all_references(current_user, text, load_lazy_references: load_lazy_references) + def referenced_mentionables(current_user = self.author, text = nil) + refs = all_references(current_user, text) refs = (refs.issues + refs.merge_requests + refs.commits) # We're using this method instead of Array diffing because that requires diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index 85367f89f4f..808d80b0530 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -38,20 +38,21 @@ module Participable # Be aware that this method makes a lot of sql queries. # Save result into variable if you are going to reuse it inside same request def participants(current_user = self.author, load_lazy_references: true) - participants = self.class.participant_attrs.flat_map do |attr| - value = - if attr.respond_to?(:call) - instance_exec(current_user, &attr) - else - send(attr) - end + participants = + Gitlab::ReferenceExtractor.lazily do + self.class.participant_attrs.flat_map do |attr| + value = + if attr.respond_to?(:call) + instance_exec(current_user, &attr) + else + send(attr) + end - participants_for(value, current_user) - end.compact.uniq - - if load_lazy_references - participants = Gitlab::Markdown::ReferenceFilter::LazyReference.load(participants).uniq + participants_for(value, current_user) + end.compact.uniq + end + unless Gitlab::ReferenceExtractor.lazy? participants.select! do |user| user.can?(:read_project, project) end @@ -64,12 +65,12 @@ module Participable def participants_for(value, current_user = nil) case value - when User, Gitlab::Markdown::ReferenceFilter::LazyReference + when User, Banzai::LazyReference [value] when Enumerable, ActiveRecord::Relation value.flat_map { |v| participants_for(v, current_user) } when Participable - value.participants(current_user, load_lazy_references: false) + value.participants(current_user) end end end diff --git a/app/models/issue.rb b/app/models/issue.rb index e04035b3af8..9f4f4923e58 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -88,7 +88,7 @@ class Issue < ActiveRecord::Base note.all_references(load_lazy_references: false).merge_requests end.uniq - Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid) + Banzai::LazyReference.load(references).uniq.sort_by(&:iid) end # Reset issue events cache diff --git a/app/models/note.rb b/app/models/note.rb index 04053ccc61e..ea69ef65fcb 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -373,11 +373,11 @@ class Note < ActiveRecord::Base end def contains_emoji_only? - note =~ /\A#{Gitlab::Markdown::EmojiFilter.emoji_pattern}\s?\Z/ + note =~ /\A#{Banzai::EmojiFilter.emoji_pattern}\s?\Z/ end def award_emoji_name - original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1] + original_name = note.match(Banzai::EmojiFilter.emoji_pattern)[1] AwardEmoji.normilize_emoji_name(original_name) end end diff --git a/lib/banzai.rb b/lib/banzai.rb new file mode 100644 index 00000000000..093382261ae --- /dev/null +++ b/lib/banzai.rb @@ -0,0 +1,13 @@ +module Banzai + def self.render(text, context = {}) + Renderer.render(text, context) + end + + def self.render_result(text, context = {}) + Renderer.render_result(text, context) + end + + def self.post_process(html, context) + Renderer.post_process(html, context) + end +end diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb new file mode 100644 index 00000000000..ba2866e1efa --- /dev/null +++ b/lib/banzai/cross_project_reference.rb @@ -0,0 +1,22 @@ +require 'banzai' + +module Banzai + # Common methods for ReferenceFilters that support an optional cross-project + # reference. + module CrossProjectReference + # Given a cross-project reference string, get the Project record + # + # Defaults to value of `context[:project]` if: + # * No reference is given OR + # * Reference given doesn't exist + # + # ref - String reference. + # + # Returns a Project, or nil if the reference can't be found + def project_from_ref(ref) + return context[:project] unless ref + + Project.find_with_namespace(ref) + end + end +end diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb new file mode 100644 index 00000000000..fd4fe024252 --- /dev/null +++ b/lib/banzai/filter.rb @@ -0,0 +1,10 @@ +require 'active_support/core_ext/string/output_safety' +require 'banzai' + +module Banzai + module Filter + def self.[](name) + const_get("#{name.to_s.camelize}Filter") + end + end +end diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb new file mode 100644 index 00000000000..bdaa4721b4b --- /dev/null +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -0,0 +1,145 @@ +require 'banzai' + +module Banzai + module Filter + # Issues, Merge Requests, Snippets, Commits and Commit Ranges share + # similar functionality in reference filtering. + class AbstractReferenceFilter < ReferenceFilter + include CrossProjectReference + + def self.object_class + # Implement in child class + # Example: MergeRequest + end + + def self.object_name + object_class.name.underscore + end + + def self.object_sym + object_name.to_sym + end + + def self.data_reference + "data-#{object_name.dasherize}" + end + + # Public: Find references in text (like `!123` for merge requests) + # + # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches| + # object = find_object(project_ref, id) + # "#{object.to_reference}" + # end + # + # text - String text to search. + # + # Yields the String match, the Integer referenced object ID, an optional String + # of the external project reference, and all of the matchdata. + # + # Returns a String replaced with the return of the block. + def self.references_in(text, pattern = object_class.reference_pattern) + text.gsub(pattern) do |match| + yield match, $~[object_sym].to_i, $~[:project], $~ + end + end + + def self.referenced_by(node) + { object_sym => LazyReference.new(object_class, node.attr(data_reference)) } + end + + delegate :object_class, :object_sym, :references_in, to: :class + + def find_object(project, id) + # Implement in child class + # Example: project.merge_requests.find + end + + def url_for_object(object, project) + # Implement in child class + # Example: project_merge_request_url + end + + def call + # `#123` + replace_text_nodes_matching(object_class.reference_pattern) do |content| + object_link_filter(content, object_class.reference_pattern) + end + + # `[Issue](#123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| + object_link_filter(link, object_class.reference_pattern, link_text: text) + end + + # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into + # `http://gitlab.example.com/namespace/project/issues/123` + replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| + object_link_filter(text, object_class.link_reference_pattern) + end + + # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text| + object_link_filter(link, object_class.link_reference_pattern, link_text: text) + end + end + + # Replace references (like `!123` for merge requests) in text with links + # to the referenced object's details page. + # + # text - String text to replace references in. + # pattern - Reference pattern to match against. + # link_text - Original content of the link being replaced. + # + # Returns a String with references replaced with links. All links + # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. + def object_link_filter(text, pattern, link_text: nil) + references_in(text, pattern) do |match, id, project_ref, matches| + project = project_from_ref(project_ref) + + if project && object = find_object(project, id) + title = escape_once(object_link_title(object)) + klass = reference_class(object_sym) + + data = data_attribute( + original: link_text || match, + project: project.id, + object_sym => object.id + ) + + url = matches[:url] if matches.names.include?("url") + url ||= url_for_object(object, project) + + text = link_text + unless text + text = object.reference_link_text(context[:project]) + + extras = object_link_text_extras(object, matches) + text += " (#{extras.join(", ")})" if extras.any? + end + + %(#{text}) + else + match + end + end + end + + def object_link_text_extras(object, matches) + extras = [] + + if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/ + extras << "comment #{$1}" + end + + extras + end + + def object_link_title(object) + "#{object_class.name.titleize}: #{object.title}" + end + end + end +end diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb new file mode 100644 index 00000000000..da4ee80c1b5 --- /dev/null +++ b/lib/banzai/filter/autolink_filter.rb @@ -0,0 +1,107 @@ +require 'banzai' +require 'html/pipeline/filter' +require 'uri' + +module Banzai + module Filter + # HTML Filter for auto-linking URLs in HTML. + # + # Based on HTML::Pipeline::AutolinkFilter + # + # Context options: + # :autolink - Boolean, skips all processing done by this filter when false + # :link_attr - Hash of attributes for the generated links + # + class AutolinkFilter < HTML::Pipeline::Filter + include ActionView::Helpers::TagHelper + + # Pattern to match text that should be autolinked. + # + # A URI scheme begins with a letter and may contain letters, numbers, + # plus, period and hyphen. Schemes are case-insensitive but we're being + # picky here and allowing only lowercase for autolinks. + # + # See http://en.wikipedia.org/wiki/URI_scheme + # + # The negative lookbehind ensures that users can paste a URL followed by a + # period or comma for punctuation without those characters being included + # in the generated link. + # + # Rubular: http://rubular.com/r/cxjPyZc7Sb + LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?" + end + end + + private + + def emoji_url(name) + emoji_path = "emoji/#{emoji_filename(name)}" + if context[:asset_host] + # Asset host is specified. + url_to_image(emoji_path) + elsif context[:asset_root] + # Gitlab url is specified + File.join(context[:asset_root], url_to_image(emoji_path)) + else + # All other cases + url_to_image(emoji_path) + end + end + + def url_to_image(image) + ActionController::Base.helpers.url_to_image(image) + end + + # Build a regexp that matches all valid :emoji: names. + def self.emoji_pattern + @emoji_pattern ||= /:(#{Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/ + end + + def emoji_pattern + self.class.emoji_pattern + end + + def emoji_filename(name) + "#{Emoji.emoji_filename(name)}.png" + end + end + end +end diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb new file mode 100644 index 00000000000..f5737a7ac19 --- /dev/null +++ b/lib/banzai/filter/external_issue_reference_filter.rb @@ -0,0 +1,69 @@ +require 'banzai' + +module Banzai + module Filter + # HTML filter that replaces external issue tracker references with links. + # References are ignored if the project doesn't use an external issue + # tracker. + class ExternalIssueReferenceFilter < ReferenceFilter + # Public: Find `JIRA-123` issue references in text + # + # ExternalIssueReferenceFilter.references_in(text) do |match, issue| + # "##{issue}" + # end + # + # text - String text to search. + # + # Yields the String match and the String issue reference. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(ExternalIssue.reference_pattern) do |match| + yield match, $~[:issue] + end + end + + def call + # Early return if the project isn't using an external tracker + return doc if project.nil? || project.default_issues_tracker? + + replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content| + issue_link_filter(content) + end + + replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text| + issue_link_filter(link, link_text: text) + end + end + + # Replace `JIRA-123` issue references in text with links to the referenced + # issue's details page. + # + # text - String text to replace references in. + # + # Returns a String with `JIRA-123` references replaced with links. All + # links have `gfm` and `gfm-issue` class names attached for styling. + def issue_link_filter(text, link_text: nil) + project = context[:project] + + self.class.references_in(text) do |match, issue| + url = url_for_issue(issue, project, only_path: context[:only_path]) + + title = escape_once("Issue in #{project.external_issue_tracker.title}") + klass = reference_class(:issue) + data = data_attribute(project: project.id) + + text = link_text || match + + %(#{text}) + end + end + + def url_for_issue(*args) + IssuesHelper.url_for_issue(*args) + end + end + end +end diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb new file mode 100644 index 00000000000..ac87b9820af --- /dev/null +++ b/lib/banzai/filter/external_link_filter.rb @@ -0,0 +1,34 @@ +require 'banzai' +require 'html/pipeline/filter' + +module Banzai + module Filter + # HTML Filter to add a `rel="nofollow"` attribute to external links + # + class ExternalLinkFilter < HTML::Pipeline::Filter + def call + doc.search('a').each do |node| + link = node.attr('href') + + next unless link + + # Skip non-HTTP(S) links + next unless link.start_with?('http') + + # Skip internal links + next if link.start_with?(internal_url) + + node.set_attribute('rel', 'nofollow') + end + + doc + end + + private + + def internal_url + @internal_url ||= Gitlab.config.gitlab.url + end + end + end +end diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb new file mode 100644 index 00000000000..51180cb901a --- /dev/null +++ b/lib/banzai/filter/issue_reference_filter.rb @@ -0,0 +1,23 @@ +require 'banzai' + +module Banzai + module Filter + # HTML filter that replaces issue references with links. References to + # issues that do not exist are ignored. + # + # This filter supports cross-project references. + class IssueReferenceFilter < AbstractReferenceFilter + def self.object_class + Issue + end + + def find_object(project, id) + project.get_issue(id) + end + + def url_for_object(issue, project) + IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) + end + end + end +end diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb new file mode 100644 index 00000000000..07bac2dd7fd --- /dev/null +++ b/lib/banzai/filter/label_reference_filter.rb @@ -0,0 +1,96 @@ +require 'banzai' + +module Banzai + module Filter + # HTML filter that replaces label references with links. + class LabelReferenceFilter < ReferenceFilter + # Public: Find label references in text + # + # LabelReferenceFilter.references_in(text) do |match, id, name| + # "#{Label.find(id)}" + # end + # + # text - String text to search. + # + # Yields the String match, an optional Integer label ID, and an optional + # String label name. + # + # Returns a String replaced with the return of the block. + def self.references_in(text) + text.gsub(Label.reference_pattern) do |match| + yield match, $~[:label_id].to_i, $~[:label_name] + end + end + + def self.referenced_by(node) + { label: LazyReference.new(Label, node.attr("data-label")) } + end + + def call + replace_text_nodes_matching(Label.reference_pattern) do |content| + label_link_filter(content) + end + + replace_link_nodes_with_href(Label.reference_pattern) do |link, text| + label_link_filter(link, link_text: text) + end + end + + # Replace label references in text with links to the label specified. + # + # text - String text to replace references in. + # + # Returns a String with label references replaced with links. All links + # have `gfm` and `gfm-label` class names attached for styling. + def label_link_filter(text, link_text: nil) + project = context[:project] + + self.class.references_in(text) do |match, id, name| + params = label_params(id, name) + + if label = project.labels.find_by(params) + url = url_for_label(project, label) + klass = reference_class(:label) + data = data_attribute( + original: link_text || match, + project: project.id, + label: label.id + ) + + text = link_text || render_colored_label(label) + + %(#{text}) + else + match + end + end + end + + def url_for_label(project, label) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_issues_url( project.namespace, project, label_name: label.name, + only_path: context[:only_path]) + end + + def render_colored_label(label) + LabelsHelper.render_colored_label(label) + end + + # Parameters to pass to `Label.find_by` based on the given arguments + # + # id - Integer ID to pass. If present, returns {id: id} + # name - String name to pass. If `id` is absent, finds by name without + # surrounding quotes. + # + # Returns a Hash. + def label_params(id, name) + if name + { name: name.tr('"', '') } + else + { id: id } + end + end + end + end +end diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb new file mode 100644 index 00000000000..0072bab1f99 --- /dev/null +++ b/lib/banzai/filter/markdown_filter.rb @@ -0,0 +1,42 @@ +require 'banzai' +require 'html/pipeline/filter' + +module Banzai + module Filter + class MarkdownFilter < HTML::Pipeline::TextFilter + def initialize(text, context = nil, result = nil) + super text, context, result + @text = @text.gsub "\r", '' + end + + def call + html = self.class.renderer.render(@text) + html.rstrip! + html + end + + private + + def self.redcarpet_options + # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use + @redcarpet_options ||= { + fenced_code_blocks: true, + footnotes: true, + lax_spacing: true, + no_intra_emphasis: true, + space_after_headers: true, + strikethrough: true, + superscript: true, + tables: true + }.freeze + end + + def self.renderer + @renderer ||= begin + renderer = Redcarpet::Render::HTML.new + Redcarpet::Markdown.new(renderer, redcarpet_options) + end + end + end + end +end diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb new file mode 100644 index 00000000000..755b946a34b --- /dev/null +++ b/lib/banzai/filter/merge_request_reference_filter.rb @@ -0,0 +1,41 @@ +require 'banzai' + +module Banzai + module Filter + # HTML filter that replaces merge request references with links. References + # to merge requests that do not exist are ignored. + # + # This filter supports cross-project references. + class MergeRequestReferenceFilter < AbstractReferenceFilter + def self.object_class + MergeRequest + end + + def find_object(project, id) + project.merge_requests.find_by(iid: id) + end + + def url_for_object(mr, project) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_merge_request_url(project.namespace, project, mr, + only_path: context[:only_path]) + end + + def object_link_text_extras(object, matches) + extras = super + + path = matches[:path] if matches.names.include?("path") + case path + when '/diffs' + extras.unshift "diffs" + when '/commits' + extras.unshift "commits" + when '/builds' + extras.unshift "builds" + end + + extras + end + end + end +end diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb new file mode 100644 index 00000000000..89e7a79789a --- /dev/null +++ b/lib/banzai/filter/redactor_filter.rb @@ -0,0 +1,43 @@ +require 'banzai' +require 'html/pipeline/filter' + +module Banzai + module Filter + # HTML filter that removes references to records that the current user does + # not have permission to view. + # + # Expected to be run in its own post-processing pipeline. + # + class RedactorFilter < HTML::Pipeline::Filter + def call + doc.css('a.gfm').each do |node| + unless user_can_reference?(node) + # The reference should be replaced by the original text, + # which is not always the same as the rendered text. + text = node.attr('data-original') || node.text + node.replace(text) + end + end + + doc + end + + private + + def user_can_reference?(node) + if node.has_attribute?('data-reference-filter') + reference_type = node.attr('data-reference-filter') + reference_filter = Banzai::Filter.const_get(reference_type) + + reference_filter.user_can_reference?(current_user, node, context) + else + true + end + end + + def current_user + context[:current_user] + end + end + end +end diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb new file mode 100644 index 00000000000..33457a3f361 --- /dev/null +++ b/lib/banzai/filter/reference_filter.rb @@ -0,0 +1,190 @@ +require 'active_support/core_ext/string/output_safety' +require 'banzai' +require 'html/pipeline/filter' + +module Banzai + module Filter + # Base class for GitLab Flavored Markdown reference filters. + # + # References within
    , , , and