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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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.3 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 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.3