From 4f0a38f1a833cab8c83e77a6c5d323057883188d Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Wed, 21 Oct 2015 15:15:54 +0200 Subject: Added housekeeping for git repositories --- app/controllers/projects_controller.rb | 8 ++++++++ app/services/projects/housekeeping_service.rb | 22 ++++++++++++++++++++++ app/views/projects/edit.html.haml | 11 +++++++++++ config/routes.rb | 1 + lib/gitlab/backend/shell.rb | 12 ++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 app/services/projects/housekeeping_service.rb diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 82119022cf9..c3efdffe563 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -171,6 +171,14 @@ class ProjectsController < ApplicationController end end + def housekeeping + ::Projects::HousekeepingService.new(@project).execute + + respond_to do |format| + format.html { redirect_to project_path(@project) } + end + end + def toggle_star current_user.toggle_star(@project) @project.reload diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb new file mode 100644 index 00000000000..48875ac3449 --- /dev/null +++ b/app/services/projects/housekeeping_service.rb @@ -0,0 +1,22 @@ +# Projects::HousekeepingService class +# +# Used for git housekeeping +# +# Ex. +# Projects::HousekeepingService.new(project, user).execute +# +module Projects + class HousekeepingService < BaseService + include Gitlab::ShellAdapter + + def initialize(project) + @project = project + end + + def execute + if gitlab_shell.exists?(@project.path_with_namespace + '.git') + gitlab_shell.gc(@project.path_with_namespace) + end + end + end +end diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index afbf88b5507..8e49299223c 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -141,6 +141,17 @@ - else .nothing-here-block Only the project owner can archive a project + .panel.panel-default.panel.panel-warning + .panel-heading Housekeeping + .errors-holder + .panel-body + %p + Runs a number of housekeeping tasks within the current repository, + such as compressing file revisions and removing unreachable objects. + %br + = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), + method: :post, class: "btn btn-warning" + .panel.panel-default.panel.panel-warning .panel-heading Rename repository .errors-holder diff --git a/config/routes.rb b/config/routes.rb index f6812c9280a..f6e17a21479 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -381,6 +381,7 @@ Gitlab::Application.routes.draw do delete :remove_fork post :archive post :unarchive + post :housekeeping post :toggle_star post :markdown_preview get :autocomplete_sources diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 01b8bda05c6..59f7a45b791 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -149,6 +149,18 @@ module Gitlab "#{path}.git", tag_name]) end + # Gc repository + # + # path - project path with namespace + # + # Ex. + # gc("gitlab/gitlab-ci") + # + def gc(path) + Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'gc', + "#{path}.git"]) + end + # Add new key to gitlab-shell # # Ex. -- cgit v1.2.1 From c843722de2d778b6ec8fef0656797fd5a8074666 Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Mon, 20 Jul 2015 20:34:19 -0500 Subject: Add graphs showing commits ahead and behind default to branches page --- CHANGELOG | 1 + app/assets/stylesheets/pages/commits.scss | 59 +++++++++++++++++++++++++ app/controllers/projects/branches_controller.rb | 6 +++ app/models/project.rb | 2 + app/models/repository.rb | 42 +++++++++++++++++- app/views/projects/branches/_branch.html.haml | 14 ++++++ 6 files changed, 122 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6bec4f606e7..98702088199 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 8.2.0 (unreleased) - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu) - Remove deprecated CI events from project settings page - Use issue editor as cross reference comment author when issue is edited with a new mention. + - Add graphs of commits ahead and behind default branch (Jeff Stubler) v 8.1.1 - Fix cloning Wiki repositories via HTTP (Stan Hu) diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 4e121b95d13..4a080db7464 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -113,3 +113,62 @@ li.commit { } } } + +.divergence-graph { + padding: 12px 12px 0 0; + float: right; + + .graph-side { + position: relative; + width: 80px; + height: 22px; + padding: 5px 0 13px; + float: left; + + .bar { + position: absolute; + height: 4px; + background-color: #ccc; + } + + .bar-behind { + right: 0; + border-radius: 3px 0 0 3px; + } + + .bar-ahead { + left: 0; + border-radius: 0 3px 3px 0; + } + + .count { + padding-top: 6px; + padding-bottom: 0px; + font-size: 12px; + color: #333; + display: block; + } + + .count-behind { + padding-right: 4px; + text-align: right; + } + + .count-ahead { + padding-left: 4px; + text-align: left; + } + } + + .graph-separator { + position: relative; + width: 1px; + height: 18px; + margin: 5px 0 0; + float: left; + background-color: #ccc; + } +} + + + diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3ac0a75fa70..c3cd7642dd2 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -9,6 +9,12 @@ class Projects::BranchesController < Projects::ApplicationController @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) + + @max_commits = @branches.reduce(0) do + |memo, branch| + diverging_commit_counts = repository.diverging_commit_counts(branch) + [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max + end end def recent diff --git a/app/models/project.rb b/app/models/project.rb index 74b89aad499..e73a856c289 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -714,6 +714,8 @@ class Project < ActiveRecord::Base end def change_head(branch) + # Cached divergent commit counts are based on repository head + repository.expire_branch_cache gitlab_shell.update_repository_head(self.path_with_namespace, branch) reload_default_branch end diff --git a/app/models/repository.rb b/app/models/repository.rb index c9b36bd8170..9b270bc9d18 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -146,10 +146,27 @@ class Repository def size cache.fetch(:size) { raw_repository.size } end + + def diverging_commit_counts(branch) + branch_cache_key = ('diverging_commit_counts_' + branch.name).to_sym + cache.fetch(branch_cache_key) do + number_commits_behind = commits_between(branch.name, root_ref).size + number_commits_ahead = commits_between(root_ref, branch.name).size + + { behind: number_commits_behind, ahead: number_commits_ahead } + end + end def cache_keys - %i(size branch_names tag_names commit_count - readme version contribution_guide changelog license) + %i(size branch_names tag_names commit_count readme + contribution_guide changelog license) + end + + def branch_cache_keys + branches.map do + |branch| + ('diverging_commit_counts_' + branch.name).to_sym + end end def build_cache @@ -158,12 +175,28 @@ class Repository send(key) end end + + branches.each do |branch| + unless cache.exist?(('diverging_commit_counts_' + branch.name).to_sym) + send(:diverging_commit_counts, branch) + end + end end def expire_cache cache_keys.each do |key| cache.expire(key) end + + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + end + end + + def expire_branch_cache + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + end end def rebuild_cache @@ -171,6 +204,11 @@ class Repository cache.expire(key) send(key) end + + branches.each do |branch| + cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + send(:diverging_commit_counts, branch) + end end def lookup_cache diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index cc0ec9483d2..9ddb10a1c74 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -1,4 +1,7 @@ - commit = @repository.commit(branch.target) +- bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0 +- number_commits_behind = @repository.diverging_commit_counts(branch)[:behind] +- number_commits_ahead = @repository.diverging_commit_counts(branch)[:ahead] %li(class="js-branch-#{branch.name}") %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do @@ -29,6 +32,17 @@ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do = icon("trash-o") + - if branch.name != @repository.root_ref + .divergence-graph{ :title => "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } + .graph-side + .bar.bar-behind{ :style => "width: #{number_commits_behind * bar_graph_width_factor}%" } + %span.count.count-behind= number_commits_behind + .graph-separator + .graph-side + .bar.bar-ahead{ :style => "width: #{number_commits_ahead * bar_graph_width_factor}%" } + %span.count.count-ahead= number_commits_ahead + + - if commit = render 'projects/branches/commit', commit: commit, project: @project - else -- cgit v1.2.1 From e0c64fac68b4b3acc48300956146b85e03b426ce Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Wed, 11 Nov 2015 16:29:29 -0600 Subject: Refactor for style issues --- app/controllers/projects/branches_controller.rb | 3 +-- app/models/repository.rb | 24 ++++++++++-------------- app/views/projects/branches/_branch.html.haml | 11 ++++++----- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index c3cd7642dd2..87884420952 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -10,8 +10,7 @@ class Projects::BranchesController < Projects::ApplicationController @branches = @repository.branches_sorted_by(@sort) @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) - @max_commits = @branches.reduce(0) do - |memo, branch| + @max_commits = @branches.reduce(0) do |memo, branch| diverging_commit_counts = repository.diverging_commit_counts(branch) [memo, diverging_commit_counts[:behind], diverging_commit_counts[:ahead]].max end diff --git a/app/models/repository.rb b/app/models/repository.rb index 9b270bc9d18..0e2d4ea1fb8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -148,8 +148,7 @@ class Repository end def diverging_commit_counts(branch) - branch_cache_key = ('diverging_commit_counts_' + branch.name).to_sym - cache.fetch(branch_cache_key) do + cache.fetch(:"diverging_commit_counts_#{branch.name}") do number_commits_behind = commits_between(branch.name, root_ref).size number_commits_ahead = commits_between(root_ref, branch.name).size @@ -158,14 +157,13 @@ class Repository end def cache_keys - %i(size branch_names tag_names commit_count readme - contribution_guide changelog license) + %i(size branch_names tag_names commit_count + readme version contribution_guide changelog license) end def branch_cache_keys - branches.map do - |branch| - ('diverging_commit_counts_' + branch.name).to_sym + branches.map do |branch| + :"diverging_commit_counts_#{branch.name}" end end @@ -177,7 +175,7 @@ class Repository end branches.each do |branch| - unless cache.exist?(('diverging_commit_counts_' + branch.name).to_sym) + unless cache.exist?(:"diverging_commit_counts_#{branch.name}") send(:diverging_commit_counts, branch) end end @@ -188,14 +186,12 @@ class Repository cache.expire(key) end - branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) - end + expire_branch_cache end def expire_branch_cache branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) + cache.expire(:"diverging_commit_counts_#{branch.name}") end end @@ -206,8 +202,8 @@ class Repository end branches.each do |branch| - cache.expire(('diverging_commit_counts_' + branch.name).to_sym) - send(:diverging_commit_counts, branch) + cache.expire(:"diverging_commit_counts_#{branch.name}") + diverging_commit_counts(branch) end end diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 9ddb10a1c74..a4202d1120d 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -1,7 +1,8 @@ - commit = @repository.commit(branch.target) - bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0 -- number_commits_behind = @repository.diverging_commit_counts(branch)[:behind] -- number_commits_ahead = @repository.diverging_commit_counts(branch)[:ahead] +- diverging_commit_counts = @repository.diverging_commit_counts(branch) +- number_commits_behind = diverging_commit_counts[:behind] +- number_commits_ahead = diverging_commit_counts[:ahead] %li(class="js-branch-#{branch.name}") %div = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do @@ -33,13 +34,13 @@ = icon("trash-o") - if branch.name != @repository.root_ref - .divergence-graph{ :title => "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } + .divergence-graph{ title: "#{number_commits_ahead} commits ahead, #{number_commits_behind} commits behind #{@repository.root_ref}" } .graph-side - .bar.bar-behind{ :style => "width: #{number_commits_behind * bar_graph_width_factor}%" } + .bar.bar-behind{ style: "width: #{number_commits_behind * bar_graph_width_factor}%" } %span.count.count-behind= number_commits_behind .graph-separator .graph-side - .bar.bar-ahead{ :style => "width: #{number_commits_ahead * bar_graph_width_factor}%" } + .bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" } %span.count.count-ahead= number_commits_ahead -- cgit v1.2.1 From 839aae0e473e85042f76391b44eaeb099235a813 Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Thu, 19 Nov 2015 15:16:54 +0100 Subject: Added housekeeping status and moved path check to gitlab-shell --- app/controllers/projects_controller.rb | 9 +++++++-- app/services/projects/housekeeping_service.rb | 6 ++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index c3efdffe563..27b723fae6a 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -8,7 +8,7 @@ class ProjectsController < ApplicationController before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists? # Authorize - before_action :authorize_admin_project!, only: [:edit, :update] + before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping] before_action :event_filter, only: [:show, :activity] layout :determine_layout @@ -172,9 +172,14 @@ class ProjectsController < ApplicationController end def housekeeping - ::Projects::HousekeepingService.new(@project).execute + status = ::Projects::HousekeepingService.new(@project).execute respond_to do |format| + if status + flash[:notice] = "Housekeeping finished successfully." + else + flash[:alert] = "Housekeeping failed." + end format.html { redirect_to project_path(@project) } end end diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb index 48875ac3449..bea91b5f180 100644 --- a/app/services/projects/housekeeping_service.rb +++ b/app/services/projects/housekeeping_service.rb @@ -3,7 +3,7 @@ # Used for git housekeeping # # Ex. -# Projects::HousekeepingService.new(project, user).execute +# Projects::HousekeepingService.new(project).execute # module Projects class HousekeepingService < BaseService @@ -14,9 +14,7 @@ module Projects end def execute - if gitlab_shell.exists?(@project.path_with_namespace + '.git') - gitlab_shell.gc(@project.path_with_namespace) - end + gitlab_shell.gc(@project.path_with_namespace) end end end -- cgit v1.2.1 From d4690af8bc283c402e49cb8b3056c7de9d57e886 Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Thu, 19 Nov 2015 16:04:07 +0100 Subject: Use GitlabShellWorker.perform_async for housekeeping --- app/controllers/projects_controller.rb | 8 ++------ app/services/projects/housekeeping_service.rb | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 27b723fae6a..ecaf4476246 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -172,14 +172,10 @@ class ProjectsController < ApplicationController end def housekeeping - status = ::Projects::HousekeepingService.new(@project).execute + ::Projects::HousekeepingService.new(@project).execute respond_to do |format| - if status - flash[:notice] = "Housekeeping finished successfully." - else - flash[:alert] = "Housekeeping failed." - end + flash[:notice] = "Housekeeping successfully started." format.html { redirect_to project_path(@project) } end end diff --git a/app/services/projects/housekeeping_service.rb b/app/services/projects/housekeeping_service.rb index bea91b5f180..0db85ac2142 100644 --- a/app/services/projects/housekeeping_service.rb +++ b/app/services/projects/housekeeping_service.rb @@ -14,7 +14,7 @@ module Projects end def execute - gitlab_shell.gc(@project.path_with_namespace) + GitlabShellWorker.perform_async(:gc, @project.path_with_namespace) end end end -- cgit v1.2.1 From c12514fc2d3996e7cfc3553d8e2bac04d0c5afec Mon Sep 17 00:00:00 2001 From: The rugged tests are fragile Date: Tue, 15 Dec 2015 15:05:57 +0800 Subject: Ignore config/sidekiq.yml [ci skip] --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f5b6427ca03..91ea81bfc4e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ config/initializers/smtp_settings.rb config/resque.yml config/unicorn.rb config/secrets.yml +config/sidekiq.yml coverage/* db/*.sqlite3 db/*.sqlite3-journal -- cgit v1.2.1 From 2e8ec7e7204b2876218db34439584204b1062265 Mon Sep 17 00:00:00 2001 From: Jeff Stubler Date: Tue, 15 Dec 2015 16:23:52 -0600 Subject: Fix diverging commit count calculation Rugged seemed to stop accepting branch names as valid refs, throwing `Rugged::ReferenceError` exceptions. Now the branch target rather than branch name is used, and the default branch's hash is also calculated, and these work properly. This used to work and might be something worth re-investigating in the future. --- app/models/repository.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 4186ef295c9..77e5bd975ec 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -157,9 +157,12 @@ class Repository end def diverging_commit_counts(branch) + root_ref_hash = raw_repository.rev_parse_target(root_ref).oid cache.fetch(:"diverging_commit_counts_#{branch.name}") do - number_commits_behind = commits_between(branch.name, root_ref).size - number_commits_ahead = commits_between(root_ref, branch.name).size + # Rugged seems to throw a `ReferenceError` when given branch_names rather + # than SHA-1 hashes + number_commits_behind = commits_between(branch.target, root_ref_hash).size + number_commits_ahead = commits_between(root_ref_hash, branch.target).size { behind: number_commits_behind, ahead: number_commits_ahead } end -- cgit v1.2.1 From b45ee2c314e2c26f4574f2e973dfa40204860c66 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Wed, 16 Dec 2015 10:08:05 -0400 Subject: better support for referencing and closing issues in asana_service.rb --- app/models/project_services/asana_service.rb | 28 +++++++++++++++------- spec/models/project_services/asana_service_spec.rb | 21 ++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index e6e16058d41..bbc508e8f8e 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -98,17 +98,29 @@ automatically inspected. Leave blank to include all branches.' task_list = [] close_list = [] - message.split("\n").each do |line| - # look for a task ID or a full Asana url - task_list.concat(line.scan(/#(\d+)/)) - task_list.concat(line.scan(/https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)/)) - # look for a word starting with 'fix' followed by a task ID - close_list.concat(line.scan(/(fix\w*)\W*#(\d+)/i)) + # matches either: + # - #1234 + # - https://app.asana.com/0/0/1234 + # optionally preceded with: + # - fix/ed/es/ing + # - close/s/d + # - closing + issue_finder = /(fix\w*|clos[ei]\w*+)?\W*(?:https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)|#(\d+))/i + + message.scan(issue_finder).each do |tuple| + # tuple will be + # [ 'fix', 'id_from_url', 'id_from_pound' ] + taskid = tuple[2] || tuple[1] + task_list.push(taskid) + + if tuple[0] + close_list.push(taskid) + end end # post commit to every taskid found task_list.each do |taskid| - task = Asana::Task.find(taskid[0]) + task = Asana::Task.find(taskid) if task task.create_story(text: push_msg + ' ' + message) @@ -117,7 +129,7 @@ automatically inspected. Leave blank to include all branches.' # close all tasks that had 'fix(ed/es/ing) #:id' in them close_list.each do |taskid| - task = Asana::Task.find(taskid.last) + task = Asana::Task.find(taskid) if task task.modify(completed: true) diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index 64bb92fba95..e368b03206e 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -62,5 +62,26 @@ describe AsanaService, models: true do @asana.check_commit('fix #456789', 'pushed') end + + it 'should be able to close via url' do + expect(Asana::Task).to receive(:find).with('42').twice + + @asana.check_commit('closes https://app.asana.com/19292/956299/42', 'pushed') + end + + it 'should allow multiple matches per line' do + expect(Asana::Task).to receive(:find).with('123').twice + expect(Asana::Task).to receive(:find).with('456').twice + expect(Asana::Task).to receive(:find).with('789').once + + expect(Asana::Task).to receive(:find).with('42').once + expect(Asana::Task).to receive(:find).with('12').twice + + message = <<-EOF + minor bigfix, refactoring, fixed #123 and Closes #456 work on #789 + ref https://app.asana.com/19292/956299/42 and closing https://app.asana.com/19292/956299/12 + EOF + @asana.check_commit(message, 'pushed') + end end end -- cgit v1.2.1 From 331154ffdf899a82c67487a70436ce49e494256b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Dec 2015 14:38:23 +0100 Subject: Escape reference link text --- lib/banzai/filter/abstract_reference_filter.rb | 17 ++++++++++------- lib/banzai/filter/reference_filter.rb | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index bdaa4721b4b..6b200dc2017 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -110,13 +110,7 @@ module Banzai url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) - text = link_text - unless text - text = object.reference_link_text(context[:project]) - - extras = object_link_text_extras(object, matches) - text += " (#{extras.join(", ")})" if extras.any? - end + text = link_text || escape_once(object_link_text(object, matches)) %( Date: Thu, 24 Dec 2015 14:43:07 +0100 Subject: Render milestone links as references --- app/models/milestone.rb | 18 ++++++ lib/banzai/filter/abstract_reference_filter.rb | 38 ++++++----- lib/banzai/filter/milestone_reference_filter.rb | 24 +++++++ lib/banzai/pipeline/gfm_pipeline.rb | 1 + lib/gitlab/reference_extractor.rb | 2 +- spec/features/markdown_spec.rb | 1 + spec/fixtures/markdown.md.erb | 8 +++ .../filter/milestone_reference_filter_spec.rb | 73 ++++++++++++++++++++++ spec/support/markdown_feature.rb | 8 +++ spec/support/matchers/markdown_matchers.rb | 9 +++ 10 files changed, 164 insertions(+), 18 deletions(-) create mode 100644 lib/banzai/filter/milestone_reference_filter.rb create mode 100644 spec/lib/banzai/filter/milestone_reference_filter_spec.rb diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d8c7536cd31..e47b6440746 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -22,6 +22,7 @@ class Milestone < ActiveRecord::Base include InternalId include Sortable + include Referable include StripAttribute belongs_to :project @@ -61,6 +62,23 @@ class Milestone < ActiveRecord::Base end end + def self.reference_pattern + nil + end + + def self.link_reference_pattern + super("milestones", /(?\d+)/) + end + + def to_reference(from_project = nil) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_milestone_url(self.project.namespace, self.project, self) + end + + def reference_link_text(from_project = nil) + %Q{ }.html_safe + self.title + end + def expired? if due_date due_date.past? diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 6b200dc2017..36d8c12e2b3 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -60,27 +60,31 @@ module Banzai end def call - # `#123` - replace_text_nodes_matching(object_class.reference_pattern) do |content| - object_link_filter(content, object_class.reference_pattern) - end + if object_class.reference_pattern + # `#123` + replace_text_nodes_matching(object_class.reference_pattern) do |content| + object_link_filter(content, object_class.reference_pattern) + end - # `[Issue](#123)`, which is turned into - # `Issue` - replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| - object_link_filter(link, object_class.reference_pattern, link_text: text) + # `[Issue](#123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.reference_pattern) do |link, text| + object_link_filter(link, object_class.reference_pattern, link_text: text) + end end - # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into - # `http://gitlab.example.com/namespace/project/issues/123` - replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| - object_link_filter(text, object_class.link_reference_pattern) - end + if object_class.link_reference_pattern + # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into + # `http://gitlab.example.com/namespace/project/issues/123` + replace_link_nodes_with_text(object_class.link_reference_pattern) do |text| + object_link_filter(text, object_class.link_reference_pattern) + end - # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into - # `Issue` - replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text| - object_link_filter(link, object_class.link_reference_pattern, link_text: text) + # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into + # `Issue` + replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text| + object_link_filter(link, object_class.link_reference_pattern, link_text: text) + end end end diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb new file mode 100644 index 00000000000..f99202af5e8 --- /dev/null +++ b/lib/banzai/filter/milestone_reference_filter.rb @@ -0,0 +1,24 @@ +require 'banzai' + +module Banzai + module Filter + # HTML filter that replaces milestone references with links. + # + # This filter supports cross-project references. + class MilestoneReferenceFilter < AbstractReferenceFilter + def self.object_class + Milestone + end + + def find_object(project, id) + project.milestones.find_by(iid: id) + end + + def url_for_object(issue, project) + h = Gitlab::Application.routes.url_helpers + h.namespace_project_milestone_url(project.namespace, project, milestone, + only_path: context[:only_path]) + end + end + end +end diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb index 38750b55ec7..838155e8831 100644 --- a/lib/banzai/pipeline/gfm_pipeline.rb +++ b/lib/banzai/pipeline/gfm_pipeline.rb @@ -22,6 +22,7 @@ module Banzai Filter::CommitRangeReferenceFilter, Filter::CommitReferenceFilter, Filter::LabelReferenceFilter, + Filter::MilestoneReferenceFilter, Filter::TaskListFilter ] diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb index 0a70d21b1ce..c87068051ab 100644 --- a/lib/gitlab/reference_extractor.rb +++ b/lib/gitlab/reference_extractor.rb @@ -18,7 +18,7 @@ module Gitlab super(text, context.merge(project: project)) end - %i(user label merge_request snippet commit commit_range).each do |type| + %i(user label milestone merge_request snippet commit commit_range).each do |type| define_method("#{type}s") do @references[type] ||= references(type, project: project, current_user: current_user) end diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index fdd8cf07b12..e836d81c40b 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -212,6 +212,7 @@ describe 'GitLab Markdown', feature: true do expect(doc).to reference_commit_ranges expect(doc).to reference_commits expect(doc).to reference_labels + expect(doc).to reference_milestones end end diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index e8dfc5c0eb1..302b750aee5 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -214,6 +214,14 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Ignored in links: [Link to <%= simple_label.to_reference %>](#label-link) - Link to label by reference: [Label](<%= label.to_reference %>) +#### MilestoneReferenceFilter + +- Milestone: <%= milestone.to_reference %> +- Milestone in another project: <%= xmilestone.to_reference(project) %> +- Ignored in code: `<%= milestone.to_reference %>` +- Ignored in links: [Link to <%= milestone.to_reference %>](#milestone-link) +- Link to milestone by URL: [Milestone](<%= urls.namespace_project_milestone_url(milestone.project.namespace, milestone.project, milestone) %>) + ### Task Lists - [ ] Incomplete task 1 diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb new file mode 100644 index 00000000000..c53e780d354 --- /dev/null +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe Banzai::Filter::MilestoneReferenceFilter, lib: true do + include FilterSpecHelper + + let(:project) { create(:project, :public) } + let(:milestone) { create(:milestone, project: project) } + + it 'requires project context' do + expect { described_class.call('') }.to raise_error(ArgumentError, /:project/) + end + + %w(pre code a style).each do |elem| + it "ignores valid references contained inside '#{elem}' element" do + exp = act = "<#{elem}>milestone #{milestone.to_reference}" + expect(reference_filter(act).to_html).to eq exp + end + end + + context 'internal reference' do + let(:reference) { milestone.to_reference } + + it 'links to a valid reference' do + doc = reference_filter("See #{reference}") + + expect(doc.css('a').first.attr('href')).to eq urls. + namespace_project_milestone_url(project.namespace, project, milestone) + end + + it 'links with adjacent text' do + doc = reference_filter("milestone (#{reference}.)") + expect(doc.to_html).to match(/\(<\/i> #{Regexp.escape(milestone.title)}<\/a>\.\)/) + end + + it 'includes a title attribute' do + doc = reference_filter("milestone #{reference}") + expect(doc.css('a').first.attr('title')).to eq "Milestone: #{milestone.title}" + end + + it 'escapes the title attribute' do + milestone.update_attribute(:title, %{">whatever Date: Thu, 24 Dec 2015 14:43:26 +0100 Subject: Link to milestone in "Milestone changed" system note --- app/services/system_note_service.rb | 4 ++-- app/views/shared/issuable/_sidebar.html.haml | 3 +-- spec/services/system_note_service_spec.rb | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 98a71cbf1ad..955a28b5a7e 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -41,7 +41,7 @@ class SystemNoteService # # Returns the created Note object def self.change_assignee(noteable, project, author, assignee) - body = assignee.nil? ? 'Assignee removed' : "Reassigned to @#{assignee.username}" + body = assignee.nil? ? 'Assignee removed' : "Reassigned to #{assignee.to_reference}" create_note(noteable: noteable, project: project, author: author, note: body) end @@ -103,7 +103,7 @@ class SystemNoteService # Returns the created Note object def self.change_milestone(noteable, project, author, milestone) body = 'Milestone ' - body += milestone.nil? ? 'removed' : "changed to #{milestone.title}" + body += milestone.nil? ? 'removed' : "changed to #{milestone.to_reference(project)}" create_note(noteable: noteable, project: project, author: author, note: body) end diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 79c5cc7f40a..9d65a621e53 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -28,8 +28,7 @@ %span.back-to-milestone = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do %strong - = icon('clock-o') - = issuable.milestone.title + = issuable.milestone.reference_link_text - else .light None .selectbox diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index c9f828ae2f7..d3364a71022 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -171,7 +171,7 @@ describe SystemNoteService, services: true do context 'when milestone added' do it 'sets the note text' do - expect(subject.note).to eq "Milestone changed to #{milestone.title}" + expect(subject.note).to eq "Milestone changed to #{milestone.to_reference}" end end -- cgit v1.2.1 From 88c35095a8dafd722de63422fdfefd3db65532fe Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Dec 2015 15:34:47 +0100 Subject: Add changelog item --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 0f9ae1e3b52..30cd589fddd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,7 @@ v 8.4.0 (unreleased) - Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu) - Implement new UI for group page - Add project permissions to all project API endpoints (Stan Hu) + - Link to milestone in "Milestone changed" system note v 8.3.0 - Add CAS support (tduehr) -- cgit v1.2.1 From 0ca74f7aad0d5b63955c86397fffdd9e670c56f3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Dec 2015 15:40:59 +0100 Subject: Use `to_reference` in system notes. --- app/services/system_note_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index 955a28b5a7e..1083bcec054 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -66,7 +66,7 @@ class SystemNoteService def self.change_label(noteable, project, author, added_labels, removed_labels) labels_count = added_labels.count + removed_labels.count - references = ->(label) { "~#{label.id}" } + references = ->(label) { label.to_reference(:id) } added_labels = added_labels.map(&references).join(' ') removed_labels = removed_labels.map(&references).join(' ') -- cgit v1.2.1 From f4e7a8893d71bcbc92a5ecd16c95593f17d1ddf5 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 24 Dec 2015 19:05:57 +0100 Subject: Add builds API (listing, showing trace) --- lib/api/api.rb | 2 ++ lib/api/builds.rb | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/entities.rb | 13 +++++++++ 3 files changed, 95 insertions(+) create mode 100644 lib/api/builds.rb diff --git a/lib/api/api.rb b/lib/api/api.rb index 7834262d612..266b5f48f8f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -54,5 +54,7 @@ module API mount Keys mount Tags mount Triggers + + mount Builds end end diff --git a/lib/api/builds.rb b/lib/api/builds.rb new file mode 100644 index 00000000000..ce4892b5eeb --- /dev/null +++ b/lib/api/builds.rb @@ -0,0 +1,80 @@ +module API + # Projects builds API + class Builds < Grape::API + before { authenticate! } + + resource :projects do + # Get a project repository commits + # + # Parameters: + # id (required) - The ID of a project + # scope (optional) - The scope of builds to show (one of: all, finished, running) + # page (optional) - The page number for pagination (default: 1) + # per_page (ooptional) - The value of items per page to show (default 30) + # Example Request: + # GET /projects/:id/builds/all + get ':id/builds' do + all_builds = user_project.builds + builds = all_builds.order('created_at DESC') + builds = + case params[:scope] + when 'all' + builds + when 'finished' + builds.finished + when 'running' + builds.running + when 'pending' + builds.pending + when 'success' + builds.success + when 'failed' + builds.failed + else + builds.running_or_pending.reverse_order + end + + page = (params[:page] || 1).to_i + per_page = (params[:per_page] || 30).to_i + + present builds.page(page).per(per_page), with: Entities::Build + end + + # Get a specific build of a project + # + # Parameters: + # id (required) - The ID of a project + # build_id (required) - The ID of a build + # Example Request: + # GET /projects/:id/builds/:build_id + get ':id/builds/:build_id' do + present get_build(params[:build_id]), with: Entities::Build + end + + # Get a trace of a specific build of a project + # + # Parameters: + # id (required) - The ID of a project + # build_id (required) - The ID of a build + # Example Request: + # GET /projects/:id/build/:build_id/trace + get ':id/builds/:build_id/trace' do + trace = get_build(params[:build_id]).trace + trace = + unless trace.nil? + trace.split("\n") + else + [] + end + + present trace + end + end + + helpers do + def get_build(id) + user_project.builds.where(id: id).first + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index f8511ac5f5c..0bf50490eac 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -366,5 +366,18 @@ module API class TriggerRequest < Grape::Entity expose :id, :variables end + + class Build < Grape::Entity + expose :id + expose :status + expose :stage + expose :name + expose :ref + expose :commit + expose :runner + expose :created_at + expose :started_at + expose :finished_at + end end end -- cgit v1.2.1 From b5fef34f1e3beb60e7184cfb3420976bfa367137 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 24 Dec 2015 19:18:01 +0100 Subject: Fix example request url --- lib/api/builds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index ce4892b5eeb..0ddb9e98de6 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -12,7 +12,7 @@ module API # page (optional) - The page number for pagination (default: 1) # per_page (ooptional) - The value of items per page to show (default 30) # Example Request: - # GET /projects/:id/builds/all + # GET /projects/:id/builds get ':id/builds' do all_builds = user_project.builds builds = all_builds.order('created_at DESC') -- cgit v1.2.1 From 32543f3bd94b1b66dd949b0fb1f57bff3732eb45 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 24 Dec 2015 21:19:03 +0100 Subject: More escaping! --- lib/banzai/filter/abstract_reference_filter.rb | 8 ++++---- lib/banzai/filter/reference_filter.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 36d8c12e2b3..b99ccd98624 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -102,7 +102,7 @@ module Banzai project = project_from_ref(project_ref) if project && object = find_object(project, id) - title = escape_once(object_link_title(object)) + title = object_link_title(object) klass = reference_class(object_sym) data = data_attribute( @@ -114,11 +114,11 @@ module Banzai url = matches[:url] if matches.names.include?("url") url ||= url_for_object(object, project) - text = link_text || escape_once(object_link_text(object, matches)) + text = link_text || object_link_text(object, matches) %(#{text}) + title="#{escape_once(title)}" + class="#{klass}">#{escape_once(text)}) else match end diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index c183702516a..a22a7a7afd3 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -44,7 +44,7 @@ module Banzai # Returns a String def data_attribute(attributes = {}) attributes[:reference_filter] = self.class.name.demodulize - attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ") + attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ") end def escape_once(html) -- cgit v1.2.1 From bd21e3d7319dce4600881f7f8677b28f3f55cc5e Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Fri, 25 Dec 2015 16:41:02 +0100 Subject: Add Open Graph data for group, project and commit. --- app/helpers/page_layout_helper.rb | 2 ++ app/views/groups/show.html.haml | 2 ++ app/views/layouts/_head.html.haml | 10 +++++----- app/views/projects/commit/show.html.haml | 4 +++- app/views/projects/show.html.haml | 2 ++ 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 791cb9e50bd..b84644d6996 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -53,6 +53,8 @@ module PageLayoutHelper @project.avatar_url || default elsif @user avatar_icon(@user) + elsif @group + @group.avatar_url || default else default end diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c2c7c581b3e..8179cdfac80 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,3 +1,5 @@ +- page_description @group.description + - unless can?(current_user, :read_group, @group) - @disable_search_panel = true diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 2e0bd2007a3..2e9a34a8807 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,13 +1,11 @@ +- site_name = "GitLab" %head{prefix: "og: http://ogp.me/ns#"} %meta{charset: "utf-8"} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} - %meta{name: 'referrer', content: 'origin-when-cross-origin'} - - %meta{name: "description", content: page_description} -# Open Graph - http://ogp.me/ %meta{property: 'og:type', content: "object"} - %meta{property: 'og:site_name', content: "GitLab"} + %meta{property: 'og:site_name', content: site_name} %meta{property: 'og:title', content: page_title} %meta{property: 'og:description', content: page_description} %meta{property: 'og:image', content: page_image} @@ -20,8 +18,9 @@ %meta{property: 'twitter:image', content: page_image} = page_card_meta_tags - - page_title "GitLab" + - page_title site_name %title= page_title + %meta{name: "description", content: page_description} = favicon_link_tag 'favicon.ico' @@ -34,6 +33,7 @@ = include_gon + %meta{name: 'referrer', content: 'origin-when-cross-origin'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} %meta{name: 'theme-color', content: '#474D57'} diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 069b8b1f169..58aa45e8d2c 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,4 +1,6 @@ -- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" +- page_title "#{@commit.title} (#{@commit.short_id})", "Commits" +- page_description @commit.description + = render "projects/commits/header_title" = render "commit_box" - if @ci_commit diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 7466a098e24..74ce005eaa2 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,3 +1,5 @@ +- page_description @project.description + = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity") -- cgit v1.2.1 From 17ee4e6cc633199d2134b8c75dce2418898c8aec Mon Sep 17 00:00:00 2001 From: Igor Matsko Date: Thu, 17 Dec 2015 15:15:29 +0300 Subject: Fix user autocomplete uri --- app/assets/javascripts/users_select.js.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 12abf806bfa..9467011799f 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -117,5 +117,5 @@ class @UsersSelect callback(users) buildUrl: (url) -> - url = gon.relative_url_root + url if gon.relative_url_root? + url = gon.relative_url_root.replace(/\/$/, '') + url if gon.relative_url_root? return url -- cgit v1.2.1 From a7756a4b51b0127caa19d1fb20953cb27c4c62a8 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 27 Dec 2015 19:49:48 -0500 Subject: Add specs for page_image using a Group's avatar --- spec/helpers/page_layout_helper_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index fd7107779f6..60c4eb21814 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -96,6 +96,22 @@ describe PageLayoutHelper do helper.page_image end end + + context 'with @group' do + it 'uses Group avatar if available' do + group = double(avatar_url: 'http://example.com/uploads/avatar.png') + helper.instance_variable_set(:@group, group) + + expect(helper.page_image).to eq group.avatar_url + end + + it 'falls back to the default' do + group = double(avatar_url: nil) + helper.instance_variable_set(:@group, group) + + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end + end end describe 'page_card_attributes' do -- cgit v1.2.1 From dcca64a5230bbfd53ef5db8403d132deac4667f2 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sun, 27 Dec 2015 19:58:44 -0500 Subject: Use `assign` instead of `instance_variable_set` --- spec/helpers/page_layout_helper_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 60c4eb21814..300dccf50ec 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -45,14 +45,14 @@ describe PageLayoutHelper do describe 'page_description_default' do it 'uses Project description when available' do project = double(description: 'Project Description') - helper.instance_variable_set(:@project, project) + assign(:project, project) expect(helper.page_description_default).to eq 'Project Description' end it 'uses brand_title when Project description is nil' do project = double(description: nil) - helper.instance_variable_set(:@project, project) + assign(:project, project) expect(helper).to receive(:brand_title).and_return('Brand Title') expect(helper.page_description_default).to eq 'Brand Title' @@ -73,14 +73,14 @@ describe PageLayoutHelper do context 'with @project' do it 'uses Project avatar if available' do project = double(avatar_url: 'http://example.com/uploads/avatar.png') - helper.instance_variable_set(:@project, project) + assign(:project, project) expect(helper.page_image).to eq project.avatar_url end it 'falls back to the default' do project = double(avatar_url: nil) - helper.instance_variable_set(:@project, project) + assign(:project, project) expect(helper.page_image).to end_with 'assets/gitlab_logo.png' end @@ -89,7 +89,7 @@ describe PageLayoutHelper do context 'with @user' do it 'delegates to avatar_icon helper' do user = double('User') - helper.instance_variable_set(:@user, user) + assign(:user, user) expect(helper).to receive(:avatar_icon).with(user) @@ -100,14 +100,14 @@ describe PageLayoutHelper do context 'with @group' do it 'uses Group avatar if available' do group = double(avatar_url: 'http://example.com/uploads/avatar.png') - helper.instance_variable_set(:@group, group) + assign(:group, group) expect(helper.page_image).to eq group.avatar_url end it 'falls back to the default' do group = double(avatar_url: nil) - helper.instance_variable_set(:@group, group) + assign(:group, group) expect(helper.page_image).to end_with 'assets/gitlab_logo.png' end -- cgit v1.2.1 From 83d42c1518274dc0af0f49fda3a10e846569cbcc Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Fri, 25 Dec 2015 18:13:55 +0200 Subject: Revert upvotes and downvotes params to MR API --- app/models/concerns/issuable.rb | 6 ++---- app/models/note.rb | 2 -- doc/api/merge_requests.md | 17 +++++------------ doc/api/notes.md | 3 +-- lib/api/entities.rb | 1 - spec/models/concerns/issuable_spec.rb | 14 ++++++++++++++ 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 919833f6df5..18a00f95b48 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -95,14 +95,12 @@ module Issuable opened? || reopened? end - # Deprecated. Still exists to preserve API compatibility. def downvotes - 0 + notes.awards.where(note: "thumbsdown").count end - # Deprecated. Still exists to preserve API compatibility. def upvotes - 0 + notes.awards.where(note: "thumbsup").count end def subscribed?(user) diff --git a/app/models/note.rb b/app/models/note.rb index 1222d99cf1f..1ca86b2592f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -346,12 +346,10 @@ class Note < ActiveRecord::Base read_attribute(:system) end - # Deprecated. Still exists to preserve API compatibility. def downvote? false end - # Deprecated. Still exists to preserve API compatibility. def upvote? false end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 366a1f8abec..8bc0a67067a 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -4,8 +4,7 @@ Get all merge requests for this project. The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). -The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. With GitLab 8.2 the return fields `upvotes` and -`downvotes` are deprecated and always return `0`. +The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. ``` GET /projects/:id/merge_requests @@ -58,7 +57,7 @@ Parameters: ## Get single MR -Shows information about a single merge request. With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and always return `0`. +Shows information about a single merge request. ``` GET /projects/:id/merge_request/:merge_request_id @@ -141,8 +140,6 @@ Parameters: ## Get single MR changes Shows information about the merge request including its files and changes. -With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and -always return `0`. ``` GET /projects/:id/merge_request/:merge_request_id/changes @@ -213,9 +210,7 @@ Parameters: ## Create MR -Creates a new merge request. With GitLab 8.2 the return fields `upvotes` and ` -downvotes` are deprecated and always return `0`. - +Creates a new merge request. ``` POST /projects/:id/merge_requests ``` @@ -266,8 +261,7 @@ If an error occurs, an error number and a message explaining the reason is retur ## Update MR -Updates an existing merge request. You can change the target branch, title, or even close the MR. With GitLab 8.2 the return fields `upvotes` and `downvotes` -are deprecated and always return `0`. +Updates an existing merge request. You can change the target branch, title, or even close the MR. ``` PUT /projects/:id/merge_request/:merge_request_id @@ -318,8 +312,7 @@ If an error occurs, an error number and a message explaining the reason is retur ## Accept MR -Merge changes submitted with MR using this API. With GitLab 8.2 the return -fields `upvotes` and `downvotes` are deprecated and always return `0`. +Merge changes submitted with MR using this API. If merge success you get `200 OK`. diff --git a/doc/api/notes.md b/doc/api/notes.md index 4d7ef288df8..d4d63e825ab 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -6,8 +6,7 @@ Notes are comments on snippets, issues or merge requests. ### List project issue notes -Gets a list of all notes for a single issue. With GitLab 8.2 the return fields -`upvote` and `downvote` are deprecated and always return `false`. +Gets a list of all notes for a single issue. ``` GET /projects/:id/issues/:issue_id/notes diff --git a/lib/api/entities.rb b/lib/api/entities.rb index f8511ac5f5c..26e7c956e8f 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -166,7 +166,6 @@ module API class MergeRequest < ProjectEntity expose :target_branch, :source_branch - # deprecated, always returns 0 expose :upvotes, :downvotes expose :author, :assignee, using: Entities::UserBasic expose :source_project_id, :target_project_id diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index f9d3c56750f..021d62cdf0c 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -99,4 +99,18 @@ describe Issue, "Issuable" do to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' }) end end + + describe "votes" do + before do + author = create :user + project = create :empty_project + issue.notes.awards.create!(note: "thumbsup", author: author, project: project) + issue.notes.awards.create!(note: "thumbsdown", author: author, project: project) + end + + it "returns correct values" do + expect(issue.upvotes).to eq(1) + expect(issue.downvotes).to eq(1) + end + end end -- cgit v1.2.1 From f39959d00a1358ba7d73ebeaccb827738c8151ba Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 13:09:51 +0100 Subject: Add some fixes to builds API --- lib/api/builds.rb | 27 +++++++++------------------ lib/api/entities.rb | 20 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 0ddb9e98de6..863be0d5e40 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -15,23 +15,15 @@ module API # GET /projects/:id/builds get ':id/builds' do all_builds = user_project.builds - builds = all_builds.order('created_at DESC') + builds = all_builds.order('id DESC') builds = case params[:scope] - when 'all' - builds when 'finished' builds.finished when 'running' builds.running - when 'pending' - builds.pending - when 'success' - builds.success - when 'failed' - builds.failed else - builds.running_or_pending.reverse_order + builds end page = (params[:page] || 1).to_i @@ -59,15 +51,14 @@ module API # Example Request: # GET /projects/:id/build/:build_id/trace get ':id/builds/:build_id/trace' do - trace = get_build(params[:build_id]).trace - trace = - unless trace.nil? - trace.split("\n") - else - [] - end + build = get_build(params[:build_id]) + + header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" + content_type 'text/plain' + env['api.format'] = :binary - present trace + trace = build.trace + body trace end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0bf50490eac..76b5d14f202 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -367,14 +367,30 @@ module API expose :id, :variables end + class CiCommit < Grape::Entity + expose :id + expose :ref + expose :sha + expose :committed_at + end + + class CiRunner < Grape::Entity + expose :id + expose :token + expose :description + expose :active + expose :is_shared + expose :name + end + class Build < Grape::Entity expose :id expose :status expose :stage expose :name expose :ref - expose :commit - expose :runner + expose :commit, with: CiCommit + expose :runner, with: CiRunner expose :created_at expose :started_at expose :finished_at -- cgit v1.2.1 From f4cff4dcd0b11d4597efe454731838fbf5803516 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 13:33:03 +0100 Subject: Modify build pagination to use 'paginate' helper --- lib/api/builds.rb | 5 +---- lib/api/helpers.rb | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 863be0d5e40..f219b0f5241 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -26,10 +26,7 @@ module API builds end - page = (params[:page] || 1).to_i - per_page = (params[:per_page] || 30).to_i - - present builds.page(page).per(per_page), with: Entities::Build + present paginate(builds), with: Entities::Build end # Get a specific build of a project diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index a4df810e755..8fb5cd6ab63 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -97,8 +97,10 @@ module API end def paginate(relation) - per_page = params[:per_page].to_i - paginated = relation.page(params[:page]).per(per_page) + page = (params[:page] || 1).to_i + per_page = (params[:per_page] || 30).to_i + + paginated = relation.page(page).per(per_page) add_pagination_headers(paginated, per_page) paginated -- cgit v1.2.1 From 42592201d9ce57817d439c5899b63776872a4175 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 28 Dec 2015 15:28:39 +0100 Subject: Hotfix for builds trace data integrity Issue #4246 --- app/models/ci/build.rb | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 7b89fe069ea..e251b1dcd97 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -194,8 +194,11 @@ module Ci end def raw_trace - if File.exist?(path_to_trace) + if File.file?(path_to_trace) File.read(path_to_trace) + elsif File.file?(old_path_to_trace) + # Temporary fix for build trace data integrity + File.read(old_path_to_trace) else # backward compatibility read_attribute :trace @@ -231,6 +234,24 @@ module Ci "#{dir_to_trace}/#{id}.log" end + ## + # Deprecated + # + def old_dir_to_trace + File.join( + Settings.gitlab_ci.builds_path, + created_at.utc.strftime("%Y_%m"), + project.ci_id.to_s + ) + end + + ## + # Deprecated + # + def old_path_to_trace + "#{old_dir_to_trace}/#{id}.log" + end + def token project.runners_token end -- cgit v1.2.1 From d398e78ea09c1f88800cd4c99f92dd8e5d1d0d39 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 15:49:13 +0100 Subject: Add endpoint for getting builds for a specific commit --- lib/api/builds.rb | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index f219b0f5241..7488863cdcf 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -4,7 +4,7 @@ module API before { authenticate! } resource :projects do - # Get a project repository commits + # Get a project builds # # Parameters: # id (required) - The ID of a project @@ -14,18 +14,21 @@ module API # Example Request: # GET /projects/:id/builds get ':id/builds' do - all_builds = user_project.builds - builds = all_builds.order('id DESC') - builds = - case params[:scope] - when 'finished' - builds.finished - when 'running' - builds.running - else - builds - end + builds = user_project.builds.order('id DESC') + builds = filter_builds(builds, params[:scope]) + present paginate(builds), with: Entities::Build + end + # GET builds for a specific commit of a project + # + # Parameters: + # id (required) - The ID of a project + # sha (required) - The SHA id of a commit + # Example Request: + # GET /projects/:id/builds/commit/:sha + get ':id/builds/commit/:sha' do + builds = user_project.ci_commits.find_by_sha(params[:sha]).builds.order('id DESC') + builds = filter_builds(builds, params[:scope]) present paginate(builds), with: Entities::Build end @@ -63,6 +66,17 @@ module API def get_build(id) user_project.builds.where(id: id).first end + + def filter_builds(builds, scope) + case scope + when 'finished' + builds.finished + when 'running' + builds.running + else + builds + end + end end end end -- cgit v1.2.1 From 141b8b67ff4cbe67778ff6815a51f49834e290b9 Mon Sep 17 00:00:00 2001 From: Michi302 Date: Mon, 28 Dec 2015 15:50:44 +0100 Subject: Make single user API endpoint return Entities::User instead of Entities::UserBasic --- doc/api/users.md | 7 +++++++ lib/api/users.rb | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/api/users.md b/doc/api/users.md index 66d2fd52526..773fe36d277 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -123,6 +123,13 @@ Parameters: "name": "John Smith", "state": "active", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", + "created_at": "2012-05-23T08:00:58Z", + "is_admin": false, + "bio": null, + "skype": "", + "linkedin": "", + "twitter": "", + "website_url": "" } ``` diff --git a/lib/api/users.rb b/lib/api/users.rb index 3400f0713ef..0d7813428e2 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -39,7 +39,7 @@ module API if current_user.is_admin? present @user, with: Entities::UserFull else - present @user, with: Entities::UserBasic + present @user, with: Entities::User end end -- cgit v1.2.1 From e7d0746d9319119c459581615ae4205139bee444 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 16:38:02 +0100 Subject: Add 'not_found' notifications in build/trace details --- lib/api/builds.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 7488863cdcf..5ac24d0367a 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -19,7 +19,7 @@ module API present paginate(builds), with: Entities::Build end - # GET builds for a specific commit of a project + # Get builds for a specific commit of a project # # Parameters: # id (required) - The ID of a project @@ -40,7 +40,10 @@ module API # Example Request: # GET /projects/:id/builds/:build_id get ':id/builds/:build_id' do - present get_build(params[:build_id]), with: Entities::Build + build = get_build(params[:build_id]) + return not_found!(build) unless build + + present build, with: Entities::Build end # Get a trace of a specific build of a project @@ -52,6 +55,7 @@ module API # GET /projects/:id/build/:build_id/trace get ':id/builds/:build_id/trace' do build = get_build(params[:build_id]) + return not_found!(build) unless build header 'Content-Disposition', "infile; filename=\"#{build.id}.log\"" content_type 'text/plain' -- cgit v1.2.1 From 8d4555037a844d21dbf56c2995cff30782af920b Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 16:38:29 +0100 Subject: Add cancel/retry endpoints to build API --- lib/api/builds.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 5ac24d0367a..16e4549d280 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -64,6 +64,42 @@ module API trace = build.trace body trace end + + # cancel a specific build of a project + # + # parameters: + # id (required) - the id of a project + # build_id (required) - the id of a build + # example request: + # post /projects/:id/build/:build_id/cancel + post ':id/builds/:build_id/cancel' do + authorize_manage_builds! + + build = get_build(params[:build_id]) + return not_found!(build) unless build + + build.cancel + + present build, with: Entities::Build + end + + # cancel a specific build of a project + # + # parameters: + # id (required) - the id of a project + # build_id (required) - the id of a build + # example request: + # post /projects/:id/build/:build_id/retry + post ':id/builds/:build_id/retry' do + authorize_manage_builds! + + build = get_build(params[:build_id]) + return not_found!(build) unless build && build.retryable? + + build = Ci::Build.retry(build) + + present build, with: Entities::Build + end end helpers do @@ -81,6 +117,10 @@ module API builds end end + + def authorize_manage_builds! + authorize! :manage_builds, user_project + end end end end -- cgit v1.2.1 From b2fbeb377d8ba17352d40817b0b444196cb97636 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 28 Dec 2015 17:01:02 +0100 Subject: Remove changes in 'paginate' helper --- lib/api/helpers.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8fb5cd6ab63..a4df810e755 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -97,10 +97,8 @@ module API end def paginate(relation) - page = (params[:page] || 1).to_i - per_page = (params[:per_page] || 30).to_i - - paginated = relation.page(page).per(per_page) + per_page = params[:per_page].to_i + paginated = relation.page(params[:page]).per(per_page) add_pagination_headers(paginated, per_page) paginated -- cgit v1.2.1 From 4465e2eca0162a27142a401e6a46aa78add9177f Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Mon, 28 Dec 2015 17:08:15 +0100 Subject: Fix spelling mistake, thanks Connor. --- app/views/groups/edit.html.haml | 2 +- spec/lib/ci/gitlab_ci_yaml_processor_spec.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 8daac585960..1dea77c2e96 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -31,7 +31,7 @@ .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) + %span.descr Make this group public (even if there are no public projects inside this group) .form-actions = f.submit 'Save group', class: "btn btn-save" diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index c90133fbf03..d15100fc6d8 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' module Ci describe GitlabCiYamlProcessor, lib: true do let(:path) { 'path' } - + describe "#builds_for_ref" do let(:type) { 'test' } @@ -29,7 +29,7 @@ module Ci when: "on_success" }) end - + describe :only do it "does not return builds if only has another branch" do config = YAML.dump({ @@ -517,7 +517,7 @@ module Ci end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra") end - it "returns errors if there is no any jobs defined" do + it "returns errors if there are no jobs defined" do config = YAML.dump({ before_script: ["bundle update"] }) expect do GitlabCiYamlProcessor.new(config, path) -- cgit v1.2.1 From eb162c2b2741bfd1ea8dd1b49e12027f7ed03594 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 23 Dec 2015 22:01:38 -0500 Subject: adds settings menu to the top for editting and leaving projects --- app/assets/stylesheets/pages/projects.scss | 7 +++++++ app/views/projects/_home_panel.html.haml | 21 +++++++++++++++++---- app/views/projects/show.html.haml | 6 +----- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index cff3edb7ed2..be6ef43e49c 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -26,6 +26,13 @@ } .project-home-panel { + + .cover-controls { + .project-settings-dropdown { + margin-left: 10px; + } + } + .project-identicon-holder { margin-bottom: 16px; diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index e92115b9b98..c3f710fa3fb 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -18,13 +18,26 @@ = visibility_level_label(@project.visibility_level) .cover-controls - - if can?(current_user, :admin_project, @project) - = link_to edit_project_path(@project), class: 'btn btn-gray' do - = icon('pencil') - if current_user -   = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do = icon('rss') + - access = user_max_access_in_project(current_user.id, @project) + - can_edit = can?(current_user, :admin_project, @project) + - if access || can_edit + %span.dropdown.project-settings-dropdown + %a.dropdown-new.btn.btn-gray.notifications-btn#notifications-button{href: '#', 'data-toggle' => 'dropdown'} + = icon('cog') + = icon('angle-down') + %ul.dropdown-menu.dropdown-menu-right + - if can_edit + %li + = link_to edit_project_path(@project) do + Edit Project + - if access + %li + = link_to leave_namespace_project_project_members_path(@project.namespace, @project), + data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do + Leave project .project-repo-buttons .split-one.count-buttons diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 7466a098e24..5d04776f7f4 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -75,8 +75,4 @@ - if access .prepend-top-20.project-footer .gray-content-block.footer-block.center - You have #{access} access to this project. - - if @project.project_member_by_id(current_user) - = link_to leave_namespace_project_project_members_path(@project.namespace, @project), - data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project', class: 'cred' do - Leave this project + You have #{access} access to this project. \ No newline at end of file -- cgit v1.2.1 From 8ee6abaaf472d7d4562d3c718ad1a68ceb71631f Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 28 Dec 2015 10:36:35 -0500 Subject: fixes tests failing --- app/views/projects/_home_panel.html.haml | 4 ++-- spec/features/projects_spec.rb | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index c3f710fa3fb..0f61e623396 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -25,7 +25,7 @@ - can_edit = can?(current_user, :admin_project, @project) - if access || can_edit %span.dropdown.project-settings-dropdown - %a.dropdown-new.btn.btn-gray.notifications-btn#notifications-button{href: '#', 'data-toggle' => 'dropdown'} + %a.dropdown-new.btn.btn-gray#project-settings-button{href: '#', 'data-toggle' => 'dropdown'} = icon('cog') = icon('angle-down') %ul.dropdown-menu.dropdown-menu-right @@ -37,7 +37,7 @@ %li = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: leave_project_message(@project) }, method: :delete, title: 'Leave project' do - Leave project + Leave Project .project-repo-buttons .split-one.count-buttons diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 74b148f5d17..d4a92072a94 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -81,7 +81,10 @@ feature 'Project', feature: true do end it { expect(page).to have_content('You have Master access to this project.') } - it { expect(page).to have_link('Leave this project') } + it { + find('#project-settings').click + expect(page).to have_link('Leave Project') + } end def remove_with_confirm(button_text, confirm_with) -- cgit v1.2.1 From a3469d914aaf28a1184247cbe72e5197ce7ca006 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Mon, 28 Dec 2015 18:21:34 -0200 Subject: reCAPTCHA is configurable through Admin Settings, no reload needed. --- .../admin/application_settings_controller.rb | 3 ++ app/controllers/registrations_controller.rb | 2 +- app/controllers/sessions_controller.rb | 5 +++ app/models/application_setting.rb | 28 ++++++++----- .../admin/application_settings/_form.html.haml | 22 ++++++++++ app/views/devise/shared/_signup_box.html.haml | 2 +- config/gitlab.yml.example | 6 --- config/initializers/1_settings.rb | 6 --- config/initializers/recaptcha.rb | 6 --- ...175719_add_recaptcha_to_application_settings.rb | 9 +++++ db/schema.rb | 5 ++- doc/integration/recaptcha.md | 47 ++++------------------ lib/gitlab/recaptcha.rb | 14 +++++++ 13 files changed, 84 insertions(+), 71 deletions(-) delete mode 100644 config/initializers/recaptcha.rb create mode 100644 db/migrate/20151228175719_add_recaptcha_to_application_settings.rb create mode 100644 lib/gitlab/recaptcha.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 3c332adf1fa..005db13fb9b 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -75,6 +75,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_pool_size, :metrics_timeout, :metrics_method_call_threshold, + :recaptcha_enabled, + :recaptcha_site_key, + :recaptcha_private_key, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index ee1006dea49..485aaf45b01 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -7,7 +7,7 @@ class RegistrationsController < Devise::RegistrationsController end def create - if !Gitlab.config.recaptcha.enabled || verify_recaptcha + if Gitlab::Recaptcha.load_configurations! && verify_recaptcha super else flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code." diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index da4b35d322b..825f85199be 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -5,6 +5,7 @@ class SessionsController < Devise::SessionsController prepend_before_action :authenticate_with_two_factor, only: [:create] prepend_before_action :store_redirect_path, only: [:new] before_action :auto_sign_in_with_provider, only: [:new] + before_action :load_recaptcha def new if Gitlab.config.ldap.enabled @@ -108,4 +109,8 @@ class SessionsController < Devise::SessionsController AuditEventService.new(user, user, options). for_authentication.security_event end + + def load_recaptcha + Gitlab::Recaptcha.load_configurations! + end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 7c107da116c..be69d317d73 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -44,24 +44,32 @@ class ApplicationSetting < ActiveRecord::Base attr_accessor :restricted_signup_domains_raw validates :session_expire_delay, - presence: true, - numericality: { only_integer: true, greater_than_or_equal_to: 0 } + presence: true, + numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :home_page_url, - allow_blank: true, - url: true, - if: :home_page_url_column_exist + allow_blank: true, + url: true, + if: :home_page_url_column_exist validates :after_sign_out_path, - allow_blank: true, - url: true + allow_blank: true, + url: true validates :admin_notification_email, - allow_blank: true, - email: true + allow_blank: true, + email: true validates :two_factor_grace_period, - numericality: { greater_than_or_equal_to: 0 } + numericality: { greater_than_or_equal_to: 0 } + + validates :recaptcha_site_key, + presence: true, + if: :recaptcha_enabled + + validates :recaptcha_private_key, + presence: true, + if: :recaptcha_enabled validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 3cada08c2ba..6b240ffc97b 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -209,5 +209,27 @@ A method call is only tracked when it takes longer to complete than the given amount of milliseconds. + %fieldset + %legend Spam and Anti-bot Protection + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :recaptcha_enabled do + = f.check_box :recaptcha_enabled + Enable reCAPTCHA + %span.help-block#recaptcha_help_block Helps preventing bots from creating accounts + + .form-group + = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :recaptcha_site_key, class: 'form-control' + .help-block + Generate site and private keys here: + %a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha + .form-group + = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :recaptcha_private_key, class: 'form-control' + .form-actions = f.submit 'Save', class: 'btn btn-primary' diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index 49fab016bfa..cb93ff2465e 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -19,7 +19,7 @@ .form-group.append-bottom-20#password-strength = f.password_field :password, class: "form-control bottom", value: user[:password], id: "user_password_sign_up", placeholder: "Password", required: true %div - - if Gitlab.config.recaptcha.enabled + - if current_application_settings.recaptcha_enabled = recaptcha_tags %div = f.submit "Sign up", class: "btn-create btn" diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 84f0dfb64c8..2d9f730c183 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -346,12 +346,6 @@ production: &base # cas3: # session_duration: 28800 - # reCAPTCHA settings. See: http://www.google.com/recaptcha - recaptcha: - enabled: false - public_key: 'YOUR_PUBLIC_KEY' - private_key: 'YOUR_PRIVATE_KEY' - # Shared file storage settings shared: # path: /mnt/gitlab # Default: shared diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 045bab739ea..dea59f4fec8 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -131,12 +131,6 @@ Settings.omniauth.cas3['session_duration'] ||= 8.hours Settings.omniauth['session_tickets'] ||= Settingslogic.new({}) Settings.omniauth.session_tickets['cas3'] = 'ticket' -# ReCAPTCHA settings -Settings['recaptcha'] ||= Settingslogic.new({}) -Settings.recaptcha['enabled'] = false if Settings.recaptcha['enabled'].nil? -Settings.recaptcha['public_key'] ||= Settings.recaptcha['public_key'] -Settings.recaptcha['private_key'] ||= Settings.recaptcha['private_key'] - Settings['shared'] ||= Settingslogic.new({}) Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root) diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb deleted file mode 100644 index 7509e327ae1..00000000000 --- a/config/initializers/recaptcha.rb +++ /dev/null @@ -1,6 +0,0 @@ -if Gitlab.config.recaptcha.enabled - Recaptcha.configure do |config| - config.public_key = Gitlab.config.recaptcha['public_key'] - config.private_key = Gitlab.config.recaptcha['private_key'] - end -end diff --git a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb new file mode 100644 index 00000000000..259fd0248d2 --- /dev/null +++ b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb @@ -0,0 +1,9 @@ +class AddRecaptchaToApplicationSettings < ActiveRecord::Migration + def change + change_table :application_settings do |t| + t.boolean :recaptcha_enabled, default: false + t.string :recaptcha_site_key + t.string :recaptcha_private_key + end + end +end diff --git a/db/schema.rb b/db/schema.rb index dc9ba36d0c7..ac6bd905eea 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: 20151228150906) do +ActiveRecord::Schema.define(version: 20151228175719) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -60,6 +60,9 @@ ActiveRecord::Schema.define(version: 20151228150906) do t.integer "metrics_pool_size", default: 16 t.integer "metrics_timeout", default: 10 t.integer "metrics_method_call_threshold", default: 10 + t.boolean "recaptcha_enabled", default: false + t.string "recaptcha_site_key" + t.string "recaptcha_private_key" end create_table "audit_events", force: :cascade do |t| diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md index 7e6f7e7e30a..a301d1a613c 100644 --- a/doc/integration/recaptcha.md +++ b/doc/integration/recaptcha.md @@ -6,51 +6,18 @@ to confirm that a real user, not a bot, is attempting to create an account. ## Configuration -To use reCAPTCHA, first you must create a public and private key. +To use reCAPTCHA, first you must create a site and private key. 1. Go to the URL: https://www.google.com/recaptcha/admin -1. Fill out the form necessary to obtain reCAPTCHA keys. +2. Fill out the form necessary to obtain reCAPTCHA keys. -1. On your GitLab server, open the configuration file. +3. Login to your GitLab server, with administrator credentials. - For omnibus package: +4. Go to Applications Settings on Admin Area (`admin/application_settings`) - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` +5. Fill all recaptcha fields with keys from previous steps - For installations from source: +6. Check the `Enable reCAPTCHA` checkbox - ```sh - cd /home/git/gitlab - - sudo -u git -H editor config/gitlab.yml - ``` - -1. Enable reCAPTCHA and add the settings: - - For omnibus package: - - ```ruby - gitlab_rails['recaptcha_enabled'] = true - gitlab_rails['recaptcha_public_key'] = 'YOUR_PUBLIC_KEY' - gitlab_rails['recaptcha_private_key'] = 'YOUR_PUBLIC_KEY' - ``` - - For installation from source: - - ``` - recaptcha: - enabled: true - public_key: 'YOUR_PUBLIC_KEY' - private_key: 'YOUR_PRIVATE_KEY' - ``` - -1. Change 'YOUR_PUBLIC_KEY' to the public key from step 2. - -1. Change 'YOUR_PRIVATE_KEY' to the private key from step 2. - -1. Save the configuration file. - -1. Restart GitLab. +7. Save the configuration. diff --git a/lib/gitlab/recaptcha.rb b/lib/gitlab/recaptcha.rb new file mode 100644 index 00000000000..70e7f25d518 --- /dev/null +++ b/lib/gitlab/recaptcha.rb @@ -0,0 +1,14 @@ +module Gitlab + module Recaptcha + def self.load_configurations! + if current_application_settings.recaptcha_enabled + ::Recaptcha.configure do |config| + config.public_key = current_application_settings.recaptcha_site_key + config.private_key = current_application_settings.recaptcha_private_key + end + + true + end + end + end +end -- cgit v1.2.1 From d985d08a19d945ad4615b319c9346ec88abf2f97 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 28 Dec 2015 17:37:03 -0500 Subject: adds proper `it` for multi line. adds `find` with correct id for button. --- spec/features/projects_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index d4a92072a94..4836b3b9b14 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -81,10 +81,10 @@ feature 'Project', feature: true do end it { expect(page).to have_content('You have Master access to this project.') } - it { - find('#project-settings').click + it 'click project-settings and find leave project' do + find('#project-settings-button').click expect(page).to have_link('Leave Project') - } + end end def remove_with_confirm(button_text, confirm_with) -- cgit v1.2.1 From 41c74cec09af146ed7fdb4778dd72fe578eb1407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Kahlh=C3=B6fer?= Date: Wed, 2 Dec 2015 07:56:34 +0000 Subject: Downcased user or email search for avatar_icon. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rubén Dávila --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0b00b9a0702..f7f7a1a02d3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -72,7 +72,7 @@ module ApplicationHelper if user_or_email.is_a?(User) user = user_or_email else - user = User.find_by(email: user_or_email) + user = User.find_by(email: user_or_email.downcase) end if user -- cgit v1.2.1 From e619d0b615a394a08ca1d0be59f0028c8e390b88 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 28 Dec 2015 16:59:59 -0800 Subject: When reCAPTCHA is disabled, allow registrations to go through without a code --- app/controllers/registrations_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 485aaf45b01..c48175a4c5a 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -7,7 +7,7 @@ class RegistrationsController < Devise::RegistrationsController end def create - if Gitlab::Recaptcha.load_configurations! && verify_recaptcha + if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha super else flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code." -- cgit v1.2.1 From d3807328d8ed9be2915f67708b093269bf0b1b9f Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Tue, 29 Dec 2015 10:11:20 +0200 Subject: note votes methids implementation --- app/models/note.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/note.rb b/app/models/note.rb index 1ca86b2592f..3d5b663c99f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -347,11 +347,11 @@ class Note < ActiveRecord::Base end def downvote? - false + is_award && note == "thumbsdown" end def upvote? - false + is_award && note == "thumbsup" end def editable? -- cgit v1.2.1 From 504696453bb7d89278bf021393274e475b84ebbd Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 29 Dec 2015 08:52:06 +0100 Subject: Add hotfix that allows to access build artifacts created before 8.3 This is a temporary hotfix that allows to access build artifacts created before 8.3. See #5257. This needs to be changed after migrating CI build files. Note that `ArtifactUploader` uses `artifacts_path` to create a storage directory before and after parsisting `Ci::Build` instance, before and after moving a file to store (save and fetch a file). --- app/models/ci/build.rb | 37 ++++++++++++++++++++++++++++++++++--- app/uploaders/artifact_uploader.rb | 8 ++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e251b1dcd97..3e67b2771c1 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -196,7 +196,7 @@ module Ci def raw_trace if File.file?(path_to_trace) File.read(path_to_trace) - elsif File.file?(old_path_to_trace) + elsif project.ci_id && File.file?(old_path_to_trace) # Temporary fix for build trace data integrity File.read(old_path_to_trace) else @@ -215,8 +215,8 @@ module Ci end def trace=(trace) - unless Dir.exists? dir_to_trace - FileUtils.mkdir_p dir_to_trace + unless Dir.exists?(dir_to_trace) + FileUtils.mkdir_p(dir_to_trace) end File.write(path_to_trace, trace) @@ -237,6 +237,9 @@ module Ci ## # Deprecated # + # This is a hotfix for CI build data integrity, see #4246 + # Should be removed in 8.4, after CI files migration has been done. + # def old_dir_to_trace File.join( Settings.gitlab_ci.builds_path, @@ -248,10 +251,38 @@ module Ci ## # Deprecated # + # This is a hotfix for CI build data integrity, see #4246 + # Should be removed in 8.4, after CI files migration has been done. + # def old_path_to_trace "#{old_dir_to_trace}/#{id}.log" end + ## + # Deprecated + # + # This contains a hotfix for CI build data integrity, see #4246 + # + # This method is used by `ArtifactUploader` to create a store_dir. + # Warning: Uploader uses it after AND before file has been stored. + # + # This method returns old path to artifacts only if it already exists. + # + def artifacts_path + old = File.join(created_at.utc.strftime('%Y_%m'), + project.ci_id.to_s, + id.to_s) + + old_store = File.join(ArtifactUploader.artifacts_path, old) + return old if project.ci_id && File.directory?(old_store) + + File.join( + created_at.utc.strftime('%Y_%m'), + project.id.to_s, + id.to_s + ) + end + def token project.runners_token end diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb index 1dccc39e7e2..1b0ae6c0056 100644 --- a/app/uploaders/artifact_uploader.rb +++ b/app/uploaders/artifact_uploader.rb @@ -20,16 +20,12 @@ class ArtifactUploader < CarrierWave::Uploader::Base @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) + File.join(self.class.artifacts_path, @build.artifacts_path) end def cache_dir - File.join(ArtifactUploader.artifacts_cache_path, artifacts_path) + File.join(self.class.artifacts_cache_path, @build.artifacts_path) end def file_storage? -- cgit v1.2.1 From dddb54f0b47f35ff6e42c1823c0f1385bb6ae30a Mon Sep 17 00:00:00 2001 From: Sytse Sijbrandij Date: Tue, 29 Dec 2015 12:58:32 +0100 Subject: Fix link, filename and crosslink to omnibus docs. --- doc/README.md | 2 +- doc/administration/enviroment_variables.md | 45 -------------------------- doc/administration/environment_variables.md | 50 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 46 deletions(-) delete mode 100644 doc/administration/enviroment_variables.md create mode 100644 doc/administration/environment_variables.md diff --git a/doc/README.md b/doc/README.md index d82ff8b908b..4162cde6dc1 100644 --- a/doc/README.md +++ b/doc/README.md @@ -56,7 +56,7 @@ - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Log system](logs/logs.md) Log system. -- [Environmental Variables](administration/environmental_variables.md) to configure GitLab. +- [Environment Variables](administration/environment_variables.md) to configure GitLab. - [Operations](operations/README.md) Keeping GitLab up and running - [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects. - [Security](security/README.md) Learn what you can do to further secure your GitLab instance. diff --git a/doc/administration/enviroment_variables.md b/doc/administration/enviroment_variables.md deleted file mode 100644 index d7f5cb7c21f..00000000000 --- a/doc/administration/enviroment_variables.md +++ /dev/null @@ -1,45 +0,0 @@ -# Environment Variables - -## Introduction - -Commonly people configure GitLab via the gitlab.rb configuration file in the Omnibus package. - -But if you prefer to use environment variables we allow that too. - -## Supported environment variables - -Variable | Type | Explanation ---- | --- | --- -GITLAB_ROOT_PASSWORD | string | sets the password for the `root` user on installation -GITLAB_HOST | url | hostname of the GitLab server includes http or https -RAILS_ENV | production/development/staging/test | Rails environment -DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5 - -## Complete database variables - -As explained in the [Heroku documentation](https://devcenter.heroku.com/articles/rails-database-connection-behavior) the DATABASE_URL doesn't let you set: - -- adapter -- database -- username -- password -- host -- port - -To do so please `cp config/database.yml.env config/database.yml` and use the following variables: - -Variable | Default ---- | --- -GITLAB_DATABASE_ADAPTER | postgresql -GITLAB_DATABASE_ENCODING | unicode -GITLAB_DATABASE_DATABASE | gitlab_#{ENV['RAILS_ENV'] -GITLAB_DATABASE_POOL | 10 -GITLAB_DATABASE_USERNAME | root -GITLAB_DATABASE_PASSWORD | -GITLAB_DATABASE_HOST | localhost -GITLAB_DATABASE_PORT | 5432 - -## Other variables - -We welcome merge requests to make more settings configurable via variables. -Please stick to the naming scheme "GITLAB_#{name 1_settings.rb in upper case}". diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md new file mode 100644 index 00000000000..5043e69db8d --- /dev/null +++ b/doc/administration/environment_variables.md @@ -0,0 +1,50 @@ +# Environment Variables + +## Introduction + +Commonly people configure GitLab via the gitlab.rb configuration file in the Omnibus package. + +But if you prefer to use environment variables we allow that too. + +## Supported environment variables + +Variable | Type | Explanation +--- | --- | --- +GITLAB_ROOT_PASSWORD | string | sets the password for the `root` user on installation +GITLAB_HOST | url | hostname of the GitLab server includes http or https +RAILS_ENV | production/development/staging/test | Rails environment +DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5 + +## Complete database variables + +As explained in the [Heroku documentation](https://devcenter.heroku.com/articles/rails-database-connection-behavior) the DATABASE_URL doesn't let you set: + +- adapter +- database +- username +- password +- host +- port + +To do so please `cp config/database.yml.env config/database.yml` and use the following variables: + +Variable | Default +--- | --- +GITLAB_DATABASE_ADAPTER | postgresql +GITLAB_DATABASE_ENCODING | unicode +GITLAB_DATABASE_DATABASE | gitlab_#{ENV['RAILS_ENV'] +GITLAB_DATABASE_POOL | 10 +GITLAB_DATABASE_USERNAME | root +GITLAB_DATABASE_PASSWORD | +GITLAB_DATABASE_HOST | localhost +GITLAB_DATABASE_PORT | 5432 + +## Adding more variables + +We welcome merge requests to make more settings configurable via variables. +Please stick to the naming scheme "GITLAB_#{name 1_settings.rb in upper case}". + +## Omnibus configuration + +It's possible to preconfigure the GitLab image by adding the environment variable: `GITLAB_OMNIBUS_CONFIG` to docker run command. +For more information see the ['preconfigure-docker-container' section in the Omnibus documentation](http://doc.gitlab.com/omnibus/docker/#preconfigure-docker-container). -- cgit v1.2.1 From 03478e6d5b98a723fbb349dac2c8495f75909a08 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 29 Dec 2015 13:40:08 +0100 Subject: Strip newlines from obfuscated SQL Newlines aren't really needed and they may mess with InfluxDB's line protocol. --- lib/gitlab/metrics/obfuscated_sql.rb | 2 +- spec/lib/gitlab/metrics/obfuscated_sql_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/metrics/obfuscated_sql.rb b/lib/gitlab/metrics/obfuscated_sql.rb index 481aca56efb..2e932fb3049 100644 --- a/lib/gitlab/metrics/obfuscated_sql.rb +++ b/lib/gitlab/metrics/obfuscated_sql.rb @@ -40,7 +40,7 @@ module Gitlab sql = sql.delete('"') end - sql + sql.gsub("\n", ' ') end end end diff --git a/spec/lib/gitlab/metrics/obfuscated_sql_spec.rb b/spec/lib/gitlab/metrics/obfuscated_sql_spec.rb index 0f01ee588c9..2b681c9fe34 100644 --- a/spec/lib/gitlab/metrics/obfuscated_sql_spec.rb +++ b/spec/lib/gitlab/metrics/obfuscated_sql_spec.rb @@ -2,6 +2,12 @@ require 'spec_helper' describe Gitlab::Metrics::ObfuscatedSQL do describe '#to_s' do + it 'replaces newlines with a space' do + sql = described_class.new("SELECT x\nFROM y") + + expect(sql.to_s).to eq('SELECT x FROM y') + end + describe 'using single values' do it 'replaces a single integer' do sql = described_class.new('SELECT x FROM y WHERE a = 10') -- cgit v1.2.1 From 620e7bb3d60c3685b494b26e256b793a47621da4 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 29 Dec 2015 13:40:42 +0100 Subject: Write to InfluxDB directly via UDP This removes the need for Sidekiq and any overhead/problems introduced by TCP. There are a few things to take into account: 1. When writing data to InfluxDB you may still get an error if the server becomes unavailable during the write. Because of this we're catching all exceptions and just ignore them (for now). 2. Writing via UDP apparently requires the timestamp to be in nanoseconds. Without this data either isn't written properly. 3. Due to the restrictions on UDP buffer sizes we're writing metrics one by one, instead of writing all of them at once. --- Procfile | 2 +- .../admin/application_settings_controller.rb | 2 +- .../admin/application_settings/_form.html.haml | 10 +-- app/workers/metrics_worker.rb | 33 --------- .../20151229102248_influxdb_udp_port_setting.rb | 5 ++ ...51229112614_influxdb_remote_database_setting.rb | 5 ++ db/schema.rb | 82 +++++++++++----------- lib/gitlab/metrics.rb | 38 +++++++++- lib/gitlab/metrics/metric.rb | 2 +- lib/gitlab/metrics/obfuscated_sql.rb | 2 +- lib/gitlab/metrics/sampler.rb | 2 +- lib/gitlab/metrics/sidekiq_middleware.rb | 7 -- lib/gitlab/metrics/transaction.rb | 2 +- spec/lib/gitlab/metrics/sampler_spec.rb | 2 +- spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb | 8 --- spec/lib/gitlab/metrics/transaction_spec.rb | 2 +- spec/lib/gitlab/metrics_spec.rb | 48 +++++++++++++ spec/workers/metrics_worker_spec.rb | 52 -------------- 18 files changed, 149 insertions(+), 155 deletions(-) delete mode 100644 app/workers/metrics_worker.rb create mode 100644 db/migrate/20151229102248_influxdb_udp_port_setting.rb create mode 100644 db/migrate/20151229112614_influxdb_remote_database_setting.rb delete mode 100644 spec/workers/metrics_worker_spec.rb diff --git a/Procfile b/Procfile index bbafdd33a2d..9cfdee7040f 100644 --- a/Procfile +++ b/Procfile @@ -3,5 +3,5 @@ # lib/support/init.d, which call scripts in bin/ . # web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} -worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -q metrics +worker: bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default # mail_room: bundle exec mail_room -q -c config/mail_room.yml diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 005db13fb9b..10e736fd362 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -69,7 +69,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :max_artifacts_size, :metrics_enabled, :metrics_host, - :metrics_database, + :metrics_port, :metrics_username, :metrics_password, :metrics_pool_size, diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 6b240ffc97b..214e0209bb7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -171,12 +171,14 @@ .col-sm-10 = f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com' .form-group - = f.label :metrics_database, 'InfluxDB database', class: 'control-label col-sm-2' + = f.label :metrics_port, 'InfluxDB port', class: 'control-label col-sm-2' .col-sm-10 - = f.text_field :metrics_database, class: 'form-control', placeholder: 'gitlab' + = f.text_field :metrics_port, class: 'form-control', placeholder: '8089' .help-block - The name of the InfluxDB database to store data in. Users will have to - create this database manually, GitLab does not do so automatically. + The UDP port to use for connecting to InfluxDB. InfluxDB requires that + your server configuration specifies a database to store data in when + sending messages to this port, without it metrics data will not be + saved. .form-group = f.label :metrics_username, 'InfluxDB username', class: 'control-label col-sm-2' .col-sm-10 diff --git a/app/workers/metrics_worker.rb b/app/workers/metrics_worker.rb deleted file mode 100644 index b15dc819c5c..00000000000 --- a/app/workers/metrics_worker.rb +++ /dev/null @@ -1,33 +0,0 @@ -class MetricsWorker - include Sidekiq::Worker - - sidekiq_options queue: :metrics - - def perform(metrics) - prepared = prepare_metrics(metrics) - - Gitlab::Metrics.pool.with do |connection| - connection.write_points(prepared) - end - end - - def prepare_metrics(metrics) - metrics.map do |hash| - new_hash = hash.symbolize_keys - - new_hash[:tags].each do |key, value| - if value.blank? - new_hash[:tags].delete(key) - else - new_hash[:tags][key] = escape_value(value) - end - end - - new_hash - end - end - - def escape_value(value) - value.to_s.gsub('=', '\\=') - end -end diff --git a/db/migrate/20151229102248_influxdb_udp_port_setting.rb b/db/migrate/20151229102248_influxdb_udp_port_setting.rb new file mode 100644 index 00000000000..ae0499f936d --- /dev/null +++ b/db/migrate/20151229102248_influxdb_udp_port_setting.rb @@ -0,0 +1,5 @@ +class InfluxdbUdpPortSetting < ActiveRecord::Migration + def change + add_column :application_settings, :metrics_port, :integer, default: 8089 + end +end diff --git a/db/migrate/20151229112614_influxdb_remote_database_setting.rb b/db/migrate/20151229112614_influxdb_remote_database_setting.rb new file mode 100644 index 00000000000..f0e1ee1e7a7 --- /dev/null +++ b/db/migrate/20151229112614_influxdb_remote_database_setting.rb @@ -0,0 +1,5 @@ +class InfluxdbRemoteDatabaseSetting < ActiveRecord::Migration + def change + remove_column :application_settings, :metrics_database + end +end diff --git a/db/schema.rb b/db/schema.rb index ac6bd905eea..df7f72d5ad4 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: 20151228175719) do +ActiveRecord::Schema.define(version: 20151229112614) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -33,36 +33,36 @@ ActiveRecord::Schema.define(version: 20151228175719) do t.datetime "created_at" t.datetime "updated_at" t.string "home_page_url" - t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true + t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true t.text "restricted_visibility_levels" - t.boolean "version_check_enabled", default: true - t.integer "max_attachment_size", default: 10, null: false + t.boolean "version_check_enabled", default: true + t.integer "max_attachment_size", default: 10, null: false t.integer "default_project_visibility" t.integer "default_snippet_visibility" t.text "restricted_signup_domains" - t.boolean "user_oauth_applications", default: true + t.boolean "user_oauth_applications", default: true t.string "after_sign_out_path" - t.integer "session_expire_delay", default: 10080, null: false + t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" 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 + t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" - t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 - t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" - t.string "metrics_database", default: "gitlab" + t.boolean "require_two_factor_authentication", default: false + t.integer "two_factor_grace_period", default: 48 + t.boolean "metrics_enabled", default: false + t.string "metrics_host", default: "localhost" t.string "metrics_username" t.string "metrics_password" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 t.boolean "recaptcha_enabled", default: false t.string "recaptcha_site_key" t.string "recaptcha_private_key" + t.integer "metrics_port", default: 8089 end create_table "audit_events", force: :cascade do |t| @@ -796,12 +796,12 @@ ActiveRecord::Schema.define(version: 20151228175719) do add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" @@ -809,22 +809,22 @@ ActiveRecord::Schema.define(version: 20151228175719) do t.datetime "created_at" t.datetime "updated_at" t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", default: "", null: false + t.string "linkedin", default: "", null: false + t.string "twitter", default: "", null: false t.string "authentication_token" - t.integer "theme_id", default: 1, null: false + t.integer "theme_id", default: 1, null: false t.string "bio" - t.integer "failed_attempts", default: 0 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" t.datetime "last_credential_check_at" @@ -833,23 +833,23 @@ ActiveRecord::Schema.define(version: 20151228175719) do t.datetime "confirmed_at" t.datetime "confirmation_sent_at" t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", default: "", null: false t.string "notification_email" - t.boolean "hide_no_password", default: false - t.boolean "password_automatically_set", default: false + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false t.string "location" t.string "encrypted_otp_secret" t.string "encrypted_otp_secret_iv" t.string "encrypted_otp_secret_salt" - t.boolean "otp_required_for_login", default: false, null: false + t.boolean "otp_required_for_login", default: false, null: false t.text "otp_backup_codes" - t.string "public_email", default: "", null: false - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.string "public_email", default: "", null: false + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 t.integer "consumed_timestep" - t.integer "layout", default: 0 - t.boolean "hide_project_limit", default: false + t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false t.string "unlock_token" t.datetime "otp_grace_period_started_at" end diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index 9470633b065..0869d007ca5 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -66,6 +66,39 @@ module Gitlab end end + def self.submit_metrics(metrics) + prepared = prepare_metrics(metrics) + + pool.with do |connection| + prepared.each do |metric| + begin + connection.write_points([metric]) + rescue StandardError + end + end + end + end + + def self.prepare_metrics(metrics) + metrics.map do |hash| + new_hash = hash.symbolize_keys + + new_hash[:tags].each do |key, value| + if value.blank? + new_hash[:tags].delete(key) + else + new_hash[:tags][key] = escape_value(value) + end + end + + new_hash + end + end + + def self.escape_value(value) + value.to_s.gsub('=', '\\=') + end + @hostname = Socket.gethostname # When enabled this should be set before being used as the usual pattern @@ -73,11 +106,12 @@ module Gitlab if enabled? @pool = ConnectionPool.new(size: pool_size, timeout: timeout) do host = settings[:metrics_host] - db = settings[:metrics_database] user = settings[:metrics_username] pw = settings[:metrics_password] + port = settings[:metrics_port] - InfluxDB::Client.new(db, host: host, username: user, password: pw) + InfluxDB::Client. + new(udp: { host: host, port: port }, username: user, password: pw) end end end diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index f592f4e571f..79241f56874 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -26,7 +26,7 @@ module Gitlab process_type: Sidekiq.server? ? 'sidekiq' : 'rails' ), values: @values, - timestamp: @created_at.to_i + timestamp: @created_at.to_i * 1_000_000_000 } end end diff --git a/lib/gitlab/metrics/obfuscated_sql.rb b/lib/gitlab/metrics/obfuscated_sql.rb index 2e932fb3049..fe97d7a0534 100644 --- a/lib/gitlab/metrics/obfuscated_sql.rb +++ b/lib/gitlab/metrics/obfuscated_sql.rb @@ -40,7 +40,7 @@ module Gitlab sql = sql.delete('"') end - sql.gsub("\n", ' ') + sql.tr("\n", ' ') end end end diff --git a/lib/gitlab/metrics/sampler.rb b/lib/gitlab/metrics/sampler.rb index 828ee1f8c62..998578e1c0a 100644 --- a/lib/gitlab/metrics/sampler.rb +++ b/lib/gitlab/metrics/sampler.rb @@ -46,7 +46,7 @@ module Gitlab end def flush - MetricsWorker.perform_async(@metrics.map(&:to_hash)) + Metrics.submit_metrics(@metrics.map(&:to_hash)) end def sample_memory_usage diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index ec10707d1fb..ad441decfa2 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -5,13 +5,6 @@ module Gitlab # This middleware is intended to be used as a server-side middleware. class SidekiqMiddleware def call(worker, message, queue) - # We don't want to track the MetricsWorker itself as otherwise we'll end - # up in an infinite loop. - if worker.class == MetricsWorker - yield - return - end - trans = Transaction.new begin diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 568f9d6ae0c..a61dbd989e7 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -59,7 +59,7 @@ module Gitlab end def submit - MetricsWorker.perform_async(@metrics.map(&:to_hash)) + Metrics.submit_metrics(@metrics.map(&:to_hash)) end end end diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb index 69376c0b79b..51a941c48cd 100644 --- a/spec/lib/gitlab/metrics/sampler_spec.rb +++ b/spec/lib/gitlab/metrics/sampler_spec.rb @@ -38,7 +38,7 @@ describe Gitlab::Metrics::Sampler do describe '#flush' do it 'schedules the metrics using Sidekiq' do - expect(MetricsWorker).to receive(:perform_async). + expect(Gitlab::Metrics).to receive(:submit_metrics). with([an_instance_of(Hash)]) sampler.sample_memory_usage diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb index 05214efc565..5882e7d81c7 100644 --- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb @@ -11,14 +11,6 @@ describe Gitlab::Metrics::SidekiqMiddleware do middleware.call(worker, 'test', :test) { nil } end - - it 'does not track jobs of the MetricsWorker' do - worker = MetricsWorker.new - - expect(Gitlab::Metrics::Transaction).to_not receive(:new) - - middleware.call(worker, 'test', :test) { nil } - end end describe '#tag_worker' do diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 5f17ff8ee75..6862fc9e2d1 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -68,7 +68,7 @@ describe Gitlab::Metrics::Transaction do it 'submits the metrics to Sidekiq' do transaction.track_self - expect(MetricsWorker).to receive(:perform_async). + expect(Gitlab::Metrics).to receive(:submit_metrics). with([an_instance_of(Hash)]) transaction.submit diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index 944642909aa..6c0682cac4d 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -33,4 +33,52 @@ describe Gitlab::Metrics do expect(file).to eq('spec/lib/gitlab/metrics_spec.rb') end end + + describe '#submit_metrics' do + it 'prepares and writes the metrics to InfluxDB' do + connection = double(:connection) + pool = double(:pool) + + expect(pool).to receive(:with).and_yield(connection) + expect(connection).to receive(:write_points).with(an_instance_of(Array)) + expect(Gitlab::Metrics).to receive(:pool).and_return(pool) + + described_class.submit_metrics([{ 'series' => 'kittens', 'tags' => {} }]) + end + end + + describe '#prepare_metrics' do + it 'returns a Hash with the keys as Symbols' do + metrics = described_class. + prepare_metrics([{ 'values' => {}, 'tags' => {} }]) + + expect(metrics).to eq([{ values: {}, tags: {} }]) + end + + it 'escapes tag values' do + metrics = described_class.prepare_metrics([ + { 'values' => {}, 'tags' => { 'foo' => 'bar=' } } + ]) + + expect(metrics).to eq([{ values: {}, tags: { 'foo' => 'bar\\=' } }]) + end + + it 'drops empty tags' do + metrics = described_class.prepare_metrics([ + { 'values' => {}, 'tags' => { 'cats' => '', 'dogs' => nil } } + ]) + + expect(metrics).to eq([{ values: {}, tags: {} }]) + end + end + + describe '#escape_value' do + it 'escapes an equals sign' do + expect(described_class.escape_value('foo=')).to eq('foo\\=') + end + + it 'casts values to Strings' do + expect(described_class.escape_value(10)).to eq('10') + end + end end diff --git a/spec/workers/metrics_worker_spec.rb b/spec/workers/metrics_worker_spec.rb deleted file mode 100644 index 18260ea0c24..00000000000 --- a/spec/workers/metrics_worker_spec.rb +++ /dev/null @@ -1,52 +0,0 @@ -require 'spec_helper' - -describe MetricsWorker do - let(:worker) { described_class.new } - - describe '#perform' do - it 'prepares and writes the metrics to InfluxDB' do - connection = double(:connection) - pool = double(:pool) - - expect(pool).to receive(:with).and_yield(connection) - expect(connection).to receive(:write_points).with(an_instance_of(Array)) - expect(Gitlab::Metrics).to receive(:pool).and_return(pool) - - worker.perform([{ 'series' => 'kittens', 'tags' => {} }]) - end - end - - describe '#prepare_metrics' do - it 'returns a Hash with the keys as Symbols' do - metrics = worker.prepare_metrics([{ 'values' => {}, 'tags' => {} }]) - - expect(metrics).to eq([{ values: {}, tags: {} }]) - end - - it 'escapes tag values' do - metrics = worker.prepare_metrics([ - { 'values' => {}, 'tags' => { 'foo' => 'bar=' } } - ]) - - expect(metrics).to eq([{ values: {}, tags: { 'foo' => 'bar\\=' } }]) - end - - it 'drops empty tags' do - metrics = worker.prepare_metrics([ - { 'values' => {}, 'tags' => { 'cats' => '', 'dogs' => nil } } - ]) - - expect(metrics).to eq([{ values: {}, tags: {} }]) - end - end - - describe '#escape_value' do - it 'escapes an equals sign' do - expect(worker.escape_value('foo=')).to eq('foo\\=') - end - - it 'casts values to Strings' do - expect(worker.escape_value(10)).to eq('10') - end - end -end -- cgit v1.2.1 From 701e5de9108faa65a118e8822560d39fd8ea9996 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Tue, 29 Dec 2015 15:49:12 +0100 Subject: Use Gitlab::CurrentSettings for InfluxDB This ensures we can still start up even when not connecting to a database. --- lib/gitlab/metrics.rb | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index 0869d007ca5..2d266ccfe9e 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -1,36 +1,21 @@ module Gitlab module Metrics + extend Gitlab::CurrentSettings + RAILS_ROOT = Rails.root.to_s METRICS_ROOT = Rails.root.join('lib', 'gitlab', 'metrics').to_s PATH_REGEX = /^#{RAILS_ROOT}\/?/ - # Returns the current settings, ensuring we _always_ have a default set of - # metrics settings (even during tests, when the migrations are lacking, - # etc). This ensures the application is able to boot up even when the - # migrations have not been executed. - def self.settings - if ApplicationSetting.table_exists? and curr = ApplicationSetting.current - curr - else - { - metrics_pool_size: 16, - metrics_timeout: 10, - metrics_enabled: false, - metrics_method_call_threshold: 10 - } - end - end - def self.pool_size - settings[:metrics_pool_size] + current_application_settings[:metrics_pool_size] || 16 end def self.timeout - settings[:metrics_timeout] + current_application_settings[:metrics_timeout] || 10 end def self.enabled? - settings[:metrics_enabled] + current_application_settings[:metrics_enabled] || false end def self.mri? @@ -41,7 +26,8 @@ module Gitlab # This is memoized since this method is called for every instrumented # method. Loading data from an external cache on every method call slows # things down too much. - @method_call_threshold ||= settings[:metrics_method_call_threshold] + @method_call_threshold ||= + (current_application_settings[:metrics_method_call_threshold] || 10) end def self.pool @@ -105,10 +91,10 @@ module Gitlab # "@foo ||= bar" is _not_ thread-safe. if enabled? @pool = ConnectionPool.new(size: pool_size, timeout: timeout) do - host = settings[:metrics_host] - user = settings[:metrics_username] - pw = settings[:metrics_password] - port = settings[:metrics_port] + host = current_application_settings[:metrics_host] + user = current_application_settings[:metrics_username] + pw = current_application_settings[:metrics_password] + port = current_application_settings[:metrics_port] InfluxDB::Client. new(udp: { host: host, port: port }, username: user, password: pw) -- cgit v1.2.1 From 8e74460534cdb0081ca1ac3e52892a4ebad05fe3 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Tue, 29 Dec 2015 12:53:21 -0500 Subject: adds ajax to bottom discussion buttons. Now submits issue and closes via ajax. --- app/assets/javascripts/issue.js.coffee | 12 +++++++++++- app/assets/javascripts/notes.js.coffee | 16 ++-------------- app/views/projects/issues/_discussion.html.haml | 6 ++---- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index c256ec8f41b..0d26c58a81d 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -16,12 +16,16 @@ class @Issue $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList initIssueBtnEventListeners: -> + _this = @ issueFailMessage = 'Unable to update this issue at this time.' $('a.btn-close, a.btn-reopen').on 'click', (e) -> e.preventDefault() e.stopImmediatePropagation() $this = $(this) isClose = $this.hasClass('btn-close') + shouldSubmit = $this.hasClass('btn-comment') + if shouldSubmit + _this.submitNoteForm($this.closest('form')) $this.prop('disabled', true) url = $this.attr('href') $.ajax @@ -32,12 +36,13 @@ class @Issue new Flash(issueFailMessage, 'alert') success: (data, textStatus, jqXHR) -> if data.saved - $this.addClass('hidden') if isClose + $('a.btn-close').addClass('hidden') $('a.btn-reopen').removeClass('hidden') $('div.status-box-closed').removeClass('hidden') $('div.status-box-open').addClass('hidden') else + $('a.btn-reopen').addClass('hidden') $('a.btn-close').removeClass('hidden') $('div.status-box-closed').addClass('hidden') $('div.status-box-open').removeClass('hidden') @@ -45,6 +50,11 @@ class @Issue new Flash(issueFailMessage, 'alert') $this.prop('disabled', false) + submitNoteForm: (form) => + noteText = form.find("textarea.js-note-text").val() + if noteText.trim().length > 0 + form.submit() + disableTaskList: -> $('.detail-page-description .js-task-list-container').taskList('disable') $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container' diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 9e5204bfeeb..ba0de896201 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -19,6 +19,8 @@ class @Notes @cleanBinding() @addBinding() @initTaskList() + # for updating the comment disscussion buttons once that issue #5534 is approved. + # @updateTargetButtons({target:$("#note_note")}) addBinding: -> # add note to UI after creation @@ -33,8 +35,6 @@ class @Notes $(document).on "click", ".note-edit-cancel", @cancelEdit # Reopen and close actions for Issue/MR combined with note form submit - $(document).on "click", ".js-note-target-reopen", @targetReopen - $(document).on "click", ".js-note-target-close", @targetClose $(document).on "click", ".js-comment-button", @updateCloseButton $(document).on "keyup", ".js-note-text", @updateTargetButtons @@ -512,17 +512,6 @@ class @Notes visibilityChange: => @refresh() - targetReopen: (e) => - @submitNoteForm($(e.target).parents('form')) - - targetClose: (e) => - @submitNoteForm($(e.target).parents('form')) - - submitNoteForm: (form) => - noteText = form.find(".js-note-text").val() - if noteText.trim().length > 0 - form.submit() - updateCloseButton: (e) => textarea = $(e.target) form = textarea.parents('form') @@ -531,7 +520,6 @@ class @Notes updateTargetButtons: (e) => textarea = $(e.target) form = textarea.parents('form') - if textarea.val().trim().length > 0 form.find('.js-note-target-reopen').text('Comment & reopen') form.find('.js-note-target-close').text('Comment & close') diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index dc434cf38c4..673020a4e30 100644 --- a/app/views/projects/issues/_discussion.html.haml +++ b/app/views/projects/issues/_discussion.html.haml @@ -1,9 +1,7 @@ - content_for :note_actions do - if can?(current_user, :update_issue, @issue) - - if @issue.closed? - = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-reopen js-note-target-reopen', title: 'Reopen Issue' - - else - = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-close js-note-target-close', title: 'Close Issue' + = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen Issue' + = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close Issue' #notes = render 'projects/notes/notes_with_form' -- cgit v1.2.1 From 2cd2c54bd1c83f8545b5f903d7717c4848453f0c Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Tue, 29 Dec 2015 15:37:48 -0400 Subject: Update Asana field descriptions --- app/models/project_services/asana_service.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index bbc508e8f8e..a16adf10432 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -53,14 +53,12 @@ http://developer.asana.com/documentation/#api_keys' { type: 'text', name: 'api_key', - placeholder: 'User API token. User must have access to task, -all comments will be attributed to this user.' + placeholder: 'User Personal Access Token. User must have access to task, all comments will be attributed to this user.' }, { type: 'text', name: 'restrict_to_branch', - placeholder: 'Comma-separated list of branches which will be -automatically inspected. Leave blank to include all branches.' + placeholder: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.' } ] end -- cgit v1.2.1 From 12ec1e3c407a4b72f670483a558a9cb95449c838 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Tue, 29 Dec 2015 15:39:58 -0400 Subject: Update Asana service to work with Personal Access Token, lessen number of requests to Asana API --- app/models/project_services/asana_service.rb | 52 +++++++++++----------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index a16adf10432..111a60431b1 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -67,35 +67,34 @@ http://developer.asana.com/documentation/#api_keys' %w(push) end + def client + @_client ||= begin + Asana::Client.new do |c| + c.authentication :access_token, api_key + end + end + end + def execute(data) return unless supported_events.include?(data[:object_kind]) - Asana.configure do |client| - client.api_key = api_key - end - - user = data[:user_name] + # check the branch restriction is poplulated and branch is not included branch = Gitlab::Git.ref_name(data[:ref]) - branch_restriction = restrict_to_branch.to_s - - # check the branch restriction is poplulated and branch is not included if branch_restriction.length > 0 && branch_restriction.index(branch).nil? return end + user = data[:user_name] project_name = project.name_with_namespace - push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name + push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} )" data[:commits].each do |commit| - check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg) + check_commit(commit[:message], push_msg) end end def check_commit(message, push_msg) - task_list = [] - close_list = [] - # matches either: # - #1234 # - https://app.asana.com/0/0/1234 @@ -109,28 +108,19 @@ http://developer.asana.com/documentation/#api_keys' # tuple will be # [ 'fix', 'id_from_url', 'id_from_pound' ] taskid = tuple[2] || tuple[1] - task_list.push(taskid) - - if tuple[0] - close_list.push(taskid) - end - end - - # post commit to every taskid found - task_list.each do |taskid| - task = Asana::Task.find(taskid) - if task - task.create_story(text: push_msg + ' ' + message) + begin + task = Asana::Task.find_by_id(client, taskid) + rescue Exception => e + puts e.message + puts e.backtrace.inspect + next end - end - # close all tasks that had 'fix(ed/es/ing) #:id' in them - close_list.each do |taskid| - task = Asana::Task.find(taskid) + task.add_comment(text: "#{push_msg} #{message}") - if task - task.modify(completed: true) + if tuple[0] + task.update(completed: true) end end end -- cgit v1.2.1 From 4c1f1c2c91a1a43f0ee38fc0f5e8c92dea996f04 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Tue, 29 Dec 2015 15:40:50 -0400 Subject: Update Asana specs --- spec/models/project_services/asana_service_spec.rb | 40 +++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index e368b03206e..306d18171be 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -52,30 +52,54 @@ describe AsanaService, models: true do end it 'should call Asana service to created a story' do - expect(Asana::Task).to receive(:find).with('123456').once + d1 = double('Asana::Task', add_comment: true) + expect(d1).to receive(:add_comment) + expect(Asana::Task).to receive(:find_by_id).with(anything, '123456').once.and_return(d1) @asana.check_commit('related to #123456', 'pushed') end it 'should call Asana service to created a story and close a task' do - expect(Asana::Task).to receive(:find).with('456789').twice + d1 = double('Asana::Task', add_comment: true) + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) + expect(Asana::Task).to receive(:find_by_id).with(anything, '456789').once.and_return(d1) @asana.check_commit('fix #456789', 'pushed') end it 'should be able to close via url' do - expect(Asana::Task).to receive(:find).with('42').twice + d1 = double('Asana::Task', add_comment: true) + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) + expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d1) @asana.check_commit('closes https://app.asana.com/19292/956299/42', 'pushed') end it 'should allow multiple matches per line' do - expect(Asana::Task).to receive(:find).with('123').twice - expect(Asana::Task).to receive(:find).with('456').twice - expect(Asana::Task).to receive(:find).with('789').once + d1 = double('Asana::Task', add_comment: true) + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) + expect(Asana::Task).to receive(:find_by_id).with(anything, '123').once.and_return(d1) - expect(Asana::Task).to receive(:find).with('42').once - expect(Asana::Task).to receive(:find).with('12').twice + d2 = double('Asana::Task', add_comment: true) + expect(d2).to receive(:add_comment) + expect(d2).to receive(:update).with(completed: true) + expect(Asana::Task).to receive(:find_by_id).with(anything, '456').once.and_return(d2) + + d3 = double('Asana::Task', add_comment: true) + expect(d3).to receive(:add_comment) + expect(Asana::Task).to receive(:find_by_id).with(anything, '789').once.and_return(d3) + + d4 = double('Asana::Task', add_comment: true) + expect(d4).to receive(:add_comment) + expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d4) + + d5 = double('Asana::Task', add_comment: true) + expect(d5).to receive(:add_comment) + expect(d5).to receive(:update).with(completed: true) + expect(Asana::Task).to receive(:find_by_id).with(anything, '12').once.and_return(d5) message = <<-EOF minor bigfix, refactoring, fixed #123 and Closes #456 work on #789 -- cgit v1.2.1 From e8080fc8fb216fb6da7d3f056e8b4417f807d09e Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Tue, 29 Dec 2015 16:00:36 -0400 Subject: Fix error in Asana service --- app/models/project_services/asana_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 111a60431b1..80c56b9097b 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -87,9 +87,9 @@ http://developer.asana.com/documentation/#api_keys' user = data[:user_name] project_name = project.name_with_namespace - push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} )" data[:commits].each do |commit| + push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} )" check_commit(commit[:message], push_msg) end end -- cgit v1.2.1 From a80f2b792cd2bdd6626bfe712750a58b863cc037 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 29 Dec 2015 16:29:45 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 57f0b9f30d5..5d0726a52ce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,6 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.4.0 (unreleased) - - Add support for Google reCAPTCHA in user registration to prevent spammers (Stan Hu) - Implement new UI for group page - Implement search inside emoji picker - Add API support for looking up a user by username (Stan Hu) @@ -10,12 +9,13 @@ v 8.4.0 (unreleased) - Expose Git's version in the admin area - Add "Frequently used" category to emoji picker - Add CAS support (tduehr) - - Add link to merge request on build detail page. + - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages + - Enable "Add key" button when user fills in a proper key (Stan Hu) -v 8.3.2 (unreleased) +v 8.3.2 - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) - - Enable "Add key" button when user fills in a proper key + - Add support for Google reCAPTCHA in user registration v 8.3.1 - Fix Error 500 when global milestones have slashes (Stan Hu) -- cgit v1.2.1 From 59533d47dd901a1b4a7e4eda382a0a25ce9a1eef Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 28 Dec 2015 18:10:46 -0800 Subject: Fix project transfer e-mail sending incorrect paths in e-mail notification The introduction of ActiveJob and `deliver_now` in 7f214cee7 caused a race condition where the mailer would be invoked before the project was committed to the database, causing the transfer e-mail notification to show the old path instead of the new one. Closes #4670 --- CHANGELOG | 3 +++ app/models/project.rb | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 5d0726a52ce..425beb17743 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,9 @@ v 8.4.0 (unreleased) - Revert back upvote and downvote button to the issue and MR pages - Enable "Add key" button when user fills in a proper key (Stan Hu) +v 8.3.3 (unreleased) + - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) + v 8.3.2 - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) - Add support for Google reCAPTCHA in user registration diff --git a/app/models/project.rb b/app/models/project.rb index 75f85310d5f..017471995ec 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -555,7 +555,9 @@ class Project < ActiveRecord::Base end def send_move_instructions(old_path_with_namespace) - NotificationService.new.project_was_moved(self, old_path_with_namespace) + # New project path needs to be committed to the DB or notification will + # retrieve stale information + run_after_commit { NotificationService.new.project_was_moved(self, old_path_with_namespace) } end def owner -- cgit v1.2.1 From d2601211a0c7a44666501dea82a8488b08f8faa7 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 29 Dec 2015 23:12:36 +0100 Subject: Add specs for build listings in API --- lib/api/builds.rb | 9 ++++--- spec/requests/api/builds_spec.rb | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 spec/requests/api/builds_spec.rb diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 16e4549d280..a519224d2b8 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -27,7 +27,10 @@ module API # Example Request: # GET /projects/:id/builds/commit/:sha get ':id/builds/commit/:sha' do - builds = user_project.ci_commits.find_by_sha(params[:sha]).builds.order('id DESC') + commit = user_project.ci_commits.find_by_sha(params[:sha]) + return not_found! unless commit + + builds = commit.builds.order('id DESC') builds = filter_builds(builds, params[:scope]) present paginate(builds), with: Entities::Build end @@ -65,7 +68,7 @@ module API body trace end - # cancel a specific build of a project + # Cancel a specific build of a project # # parameters: # id (required) - the id of a project @@ -83,7 +86,7 @@ module API present build, with: Entities::Build end - # cancel a specific build of a project + # Retry a specific build of a project # # parameters: # id (required) - the id of a project diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb new file mode 100644 index 00000000000..81c176c9fb0 --- /dev/null +++ b/spec/requests/api/builds_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(: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/builds ' do + context 'authorized user' do + it 'should return project builds' do + get api("/projects/#{project.id}/builds", user) + + puts json_response + expect(response.status).to eq(200) + expect(json_response).to be_an Array + end + end + + context 'unauthorized user' do + it 'should not return project builds' do + get api("/projects/#{project.id}/builds") + + expect(response.status).to eq(401) + end + end + end + + describe 'GET /projects/:id/builds/commit/:sha' do + context 'authorized user' do + it 'should return project builds for specific commit' do + project.ensure_ci_commit(project.repository.commit.sha) + get api("/projects/#{project.id}/builds/commit/#{project.ci_commits.first.sha}", user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + end + end + + context 'unauthorized user' do + it 'should not return project builds' do + project.ensure_ci_commit(project.repository.commit.sha) + get api("/projects/#{project.id}/builds/commit/#{project.ci_commits.first.sha}") + + expect(response.status).to eq(401) + end + end + end +end -- cgit v1.2.1 From 8662f4737b8d5eeebbe8e5e527f6e2148fea2ddf Mon Sep 17 00:00:00 2001 From: Nathan Lowe Date: Tue, 29 Dec 2015 20:58:18 -0500 Subject: docs: raketasks: Add documentation on uploading to mounted shares --- doc/raketasks/backup_restore.md | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 093450a6de3..cdd6652b7b0 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -153,6 +153,49 @@ with the name of your bucket: } ``` +### Uploading to locally mounted shares + +You may also send backups to a mounted share (`NFS` / `CIFS` / `SMB` / etc.) by +using the [`Local`](https://github.com/fog/fog-local#usage) storage provider. +The directory pointed to by the `local_root` key **must** be owned by the `git` +user **when mounted** (mounting with the `uid=` of the `git` user for `CIFS` and +`SMB`) or the user that you are executing the backup tasks under (for omnibus +packages, this is the `git` user). + +The `backup_upload_remote_directory` **must** be set in addition to the +`local_root` key. This is the sub directory inside the mounted directory that +backups will be copied to, and will be created if it does not exist. If the +directory that you want to copy the tarballs to is the root of your mounted +directory, just use `.` instead. + +For omnibus packages: + +```ruby +gitlab_rails['backup_upload_connection'] = { + :provider => 'Local', + :local_root => '/mnt/backups' +} + +# The directory inside the mounted folder to copy backups to +# Use '.' to store them in the root directory +gitlab_rails['backup_upload_remote_directory'] = 'gitlab_backups' +``` + +For installations from source: + +```yaml + backup: + # snip + upload: + # Fog storage connection settings, see http://fog.io/storage/ . + connection: + provider: Local + local_root: '/mnt/backups' + # The directory inside the mounted folder to copy backups to + # Use '.' to store them in the root directory + remote_directory: 'gitlab_backups' +``` + ## Backup archive permissions The backup archives created by GitLab (123456_gitlab_backup.tar) will have owner/group git:git and 0600 permissions by default. -- cgit v1.2.1 From 6eb273a11cf7521448f4bcb5f9376df24c2f9f3f Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Wed, 30 Dec 2015 00:50:44 -0400 Subject: Restore colon in Asana comment --- app/models/project_services/asana_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 80c56b9097b..183ce2df787 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -89,7 +89,7 @@ http://developer.asana.com/documentation/#api_keys' project_name = project.name_with_namespace data[:commits].each do |commit| - push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} )" + push_msg = "#{user} pushed to branch #{branch} of #{project_name} ( #{commit[:url]} ):" check_commit(commit[:message], push_msg) end end -- cgit v1.2.1 From 7b98d0e1a24645df802ad65911c290ad057d1422 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Wed, 30 Dec 2015 00:51:05 -0400 Subject: Update Asana service documentation --- app/models/project_services/asana_service.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 183ce2df787..ab5772356f1 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -40,8 +40,8 @@ get the commit comment added to it. You can also close a task with a message containing: `fix #123456`. -You can find your Api Keys here: -http://developer.asana.com/documentation/#api_keys' +You can create a Personal Access Token here: +http://app.asana.com/-/account_api' end def to_param -- cgit v1.2.1 From 9e7a88f089323964088945829523b798ea6b78b5 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Wed, 30 Dec 2015 00:52:56 -0400 Subject: Better handling of errors in Asana service [ci skip] --- app/models/project_services/asana_service.rb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index ab5772356f1..cb4f6ddb3a5 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -111,17 +111,16 @@ http://app.asana.com/-/account_api' begin task = Asana::Task.find_by_id(client, taskid) - rescue Exception => e - puts e.message - puts e.backtrace.inspect + task.add_comment(text: "#{push_msg} #{message}") + + if tuple[0] + task.update(completed: true) + end + rescue => e + Rails.logger.error(e.message) + Rails.logger.error(e.backtrace.join("\n")) next end - - task.add_comment(text: "#{push_msg} #{message}") - - if tuple[0] - task.update(completed: true) - end end end end -- cgit v1.2.1 From 5a997a02a2cd71877d2af7b72fb67e86722882b2 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 30 Dec 2015 06:29:56 -0500 Subject: removes commented out code. --- app/assets/javascripts/notes.js.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index ba0de896201..8ba00ecbbab 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -19,8 +19,6 @@ class @Notes @cleanBinding() @addBinding() @initTaskList() - # for updating the comment disscussion buttons once that issue #5534 is approved. - # @updateTargetButtons({target:$("#note_note")}) addBinding: -> # add note to UI after creation -- cgit v1.2.1 From a6fd7b74ee21c830ac65a1e77696dcc8284dce7c Mon Sep 17 00:00:00 2001 From: Jeroen van Baarsen Date: Wed, 30 Dec 2015 13:42:57 +0100 Subject: We don't use whenever anymore. Lets remove the schedule file **Why is this needed?** Not really needed, but having code that is not used around is confusing, so lets remove it Signed-off-by: Jeroen van Baarsen --- config/schedule.rb | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 config/schedule.rb diff --git a/config/schedule.rb b/config/schedule.rb deleted file mode 100644 index 8122f7cc69c..00000000000 --- a/config/schedule.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Use this file to easily define all of your cron jobs. -# -# If you make changes to this file, please create also an issue on -# https://gitlab.com/gitlab-org/omnibus-gitlab/issues . This is necessary -# because the omnibus packages manage cron jobs using Chef instead of Whenever. -every 1.hour do - rake "ci:schedule_builds" -end -- cgit v1.2.1 From 593d87ea54eec4d60cf7eeb404af82d9e015b066 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 30 Dec 2015 15:12:07 +0100 Subject: Add specs for build details/traces features in builds API --- spec/factories/ci/builds.rb | 6 ++++++ spec/requests/api/builds_spec.rb | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index f76e826f138..4551ee57d78 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -30,6 +30,7 @@ FactoryGirl.define do name 'test' ref 'master' tag false + created_at 'Di 29. Okt 09:50:00 CET 2013' started_at 'Di 29. Okt 09:51:28 CET 2013' finished_at 'Di 29. Okt 09:53:28 CET 2013' commands 'ls -a' @@ -54,5 +55,10 @@ FactoryGirl.define do factory :ci_build_tag do tag true end + + factory :ci_build_with_trace do + id 999 + trace 'BUILD TRACE' + end end end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 81c176c9fb0..c68ea0898b8 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -8,6 +8,9 @@ describe API::API, api: true do 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) } + let(:commit) { create(:ci_commit, project: project)} + let(:build) { create(:ci_build, commit: commit) } + let(:build_with_trace) { create(:ci_build_with_trace, commit: commit) } describe 'GET /projects/:id/builds ' do context 'authorized user' do @@ -32,7 +35,7 @@ describe API::API, api: true do describe 'GET /projects/:id/builds/commit/:sha' do context 'authorized user' do it 'should return project builds for specific commit' do - project.ensure_ci_commit(project.repository.commit.sha) + project.ensure_ci_commit(commit.sha) get api("/projects/#{project.id}/builds/commit/#{project.ci_commits.first.sha}", user) expect(response.status).to eq(200) @@ -42,11 +45,44 @@ describe API::API, api: true do context 'unauthorized user' do it 'should not return project builds' do - project.ensure_ci_commit(project.repository.commit.sha) + project.ensure_ci_commit(commit.sha) get api("/projects/#{project.id}/builds/commit/#{project.ci_commits.first.sha}") expect(response.status).to eq(401) end end end + + describe 'GET /projects/:id/builds/:build_id(/trace)?' do + context 'authorized user' do + it 'should return specific build data' do + get api("/projects/#{project.id}/builds/#{build.id}", user) + + expect(response.status).to eq(200) + expect(json_response['name']).to eq('test') + expect(json_response['commit']['sha']).to eq(commit.sha) + end + + it 'should return specific build trace' do + get api("/projects/#{project.id}/builds/#{build_with_trace.id}/trace", user) + + expect(response.status).to eq(200) + expect(response.body).to eq(build_with_trace.trace) + end + end + + context 'unauthorized user' do + it 'should not return specific build data' do + get api("/projects/#{project.id}/builds/#{build.id}") + + expect(response.status).to eq(401) + end + + it 'should not return specific build trace' do + get api("/projects/#{project.id}/builds/#{build_with_trace.id}/trace") + + expect(response.status).to eq(401) + end + end + end end -- cgit v1.2.1 From 82bfa8f40c9ee7d292e8c4f8543cca27ca2c3c25 Mon Sep 17 00:00:00 2001 From: Gabriel Mazetto Date: Wed, 30 Dec 2015 12:45:24 -0200 Subject: Added additional config environmental variables to help Debian packaging * GITLAB_EMAIL_FROM * GITLAB_EMAIL_DISPLAY_NAME * GITLAB_EMAIL_REPLY_TO --- config/initializers/1_settings.rb | 6 +++--- doc/README.md | 2 +- doc/administration/enviroment_variables.md | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index dea59f4fec8..4fbd84ee890 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -152,9 +152,9 @@ Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil? -Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}" -Settings.gitlab['email_display_name'] ||= "GitLab" -Settings.gitlab['email_reply_to'] ||= "noreply@#{Settings.gitlab.host}" +Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}" +Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab' +Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}" Settings.gitlab['base_url'] ||= Settings.send(:build_base_gitlab_url) Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['user'] ||= 'git' diff --git a/doc/README.md b/doc/README.md index d82ff8b908b..8a297f8267f 100644 --- a/doc/README.md +++ b/doc/README.md @@ -56,7 +56,7 @@ - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. - [Log system](logs/logs.md) Log system. -- [Environmental Variables](administration/environmental_variables.md) to configure GitLab. +- [Environmental Variables](administration/environment_variables.md) to configure GitLab. - [Operations](operations/README.md) Keeping GitLab up and running - [Raketasks](raketasks/README.md) Backups, maintenance, automatic web hook setup and the importing of projects. - [Security](security/README.md) Learn what you can do to further secure your GitLab instance. diff --git a/doc/administration/enviroment_variables.md b/doc/administration/enviroment_variables.md index d7f5cb7c21f..7d8f9d29eef 100644 --- a/doc/administration/enviroment_variables.md +++ b/doc/administration/enviroment_variables.md @@ -9,11 +9,14 @@ But if you prefer to use environment variables we allow that too. ## Supported environment variables Variable | Type | Explanation ---- | --- | --- +-------- | ---- | ----------- GITLAB_ROOT_PASSWORD | string | sets the password for the `root` user on installation GITLAB_HOST | url | hostname of the GitLab server includes http or https -RAILS_ENV | production/development/staging/test | Rails environment +RAILS_ENV | production / development / staging / test | Rails environment DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5 +GITLAB_EMAIL_FROM | email | Email address used in the "From" field in mails sent by GitLab +GITLAB_EMAIL_DISPLAY_NAME | string | Name used in the "From" field in mails sent by GitLab +GITLAB_EMAIL_REPLY_TO | email | Email address used in the "Reply-To" field in mails sent by GitLab ## Complete database variables -- cgit v1.2.1 From a17bf380cb4c90696349f268ca4a8c2fedc1f545 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Wed, 30 Dec 2015 16:37:47 +0100 Subject: Add cancel/retry features to builds API --- spec/factories/ci/builds.rb | 4 +++ spec/requests/api/builds_spec.rb | 62 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 4551ee57d78..ce68457f86b 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -60,5 +60,9 @@ FactoryGirl.define do id 999 trace 'BUILD TRACE' end + + factory :ci_build_canceled do + status 'canceled' + end end end diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index c68ea0898b8..d4af7639d4b 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -7,10 +7,11 @@ describe API::API, api: true do 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) } + let!(:reporter) { create(:project_member, user: user2, project: project, access_level: ProjectMember::REPORTER) } let(:commit) { create(:ci_commit, project: project)} let(:build) { create(:ci_build, commit: commit) } let(:build_with_trace) { create(:ci_build_with_trace, commit: commit) } + let(:build_canceled) { create(:ci_build_canceled, commit: commit) } describe 'GET /projects/:id/builds ' do context 'authorized user' do @@ -85,4 +86,63 @@ describe API::API, api: true do end end end + + describe 'GET /projects/:id/builds/:build_id/cancel' do + context 'authorized user' do + context 'user with :manage_builds persmission' do + it 'should cancel running or pending build' do + post api("/projects/#{project.id}/builds/#{build.id}/cancel", user) + + expect(response.status).to eq(201) + expect(project.builds.first.status).to eq('canceled') + end + end + + context 'user without :manage_builds permission' do + it 'should not cancel build' do + post api("/projects/#{project.id}/builds/#{build.id}/cancel", user2) + + expect(response.status).to eq(403) + end + end + end + + context 'unauthorized user' do + it 'should not cancel build' do + post api("/projects/#{project.id}/builds/#{build.id}/cancel") + + expect(response.status).to eq(401) + end + end + end + + describe 'GET /projects/:id/builds/:build_id/retry' do + context 'authorized user' do + context 'user with :manage_builds persmission' do + it 'should retry non-running build' do + post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user) + + expect(response.status).to eq(201) + expect(project.builds.first.status).to eq('canceled') + expect(json_response['status']).to eq('pending') + end + end + + context 'user without :manage_builds permission' do + it 'should not retry build' do + post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user2) + + expect(response.status).to eq(403) + end + end + end + + context 'unauthorized user' do + it 'should not retry build' do + post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry") + + expect(response.status).to eq(401) + end + end + end end -- cgit v1.2.1 From ff98c631c1004247656677568989e5ed68fc88f3 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 30 Dec 2015 14:26:54 -0200 Subject: Make sure that is no pending migrations in Gitlab::CurrentSettings --- lib/gitlab/current_settings.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 46a4ef0e31f..7a86c09158e 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -38,7 +38,9 @@ module Gitlab true end - use_db && ActiveRecord::Base.connection.active? && ActiveRecord::Base.connection.table_exists?('application_settings') + use_db && ActiveRecord::Base.connection.active? && + !ActiveRecord::Migrator.needs_migration? && + ActiveRecord::Base.connection.table_exists?('application_settings') end end end -- cgit v1.2.1 From d9d2e8a3e8de62beff685457e4e305f93122b206 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 29 Dec 2015 21:58:16 -0500 Subject: Support a single directory traversal in RelativeLinkFilter Prior, if we were viewing a blob at `https://example.com/namespace/project/blob/master/doc/some-file.md` and it contained a relative link such as `[README](../README.md)`, the resulting link when viewing the blob would be: `https://example.com/namespace/project/blob/README.md` which omits the `master` ref, resulting in a 404. --- lib/banzai/filter/relative_link_filter.rb | 2 +- spec/lib/banzai/filter/relative_link_filter_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb index 5a081125f21..66f166939e4 100644 --- a/lib/banzai/filter/relative_link_filter.rb +++ b/lib/banzai/filter/relative_link_filter.rb @@ -91,7 +91,7 @@ module Banzai parts = request_path.split('/') parts.pop if path_type(request_path) != 'tree' - while parts.length > 1 && path.start_with?('../') + while path.start_with?('../') parts.pop path.sub!('../', '') end diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb index 0b3e5ecbc9f..0e6685f0ffb 100644 --- a/spec/lib/banzai/filter/relative_link_filter_spec.rb +++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb @@ -92,6 +92,14 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do to eq "/#{project_path}/blob/#{ref}/doc/api/README.md" end + it 'rebuilds relative URL for a file in the repository root' do + relative_link = link('../README.md') + doc = filter(relative_link, requested_path: 'doc/some-file.md') + + expect(doc.at_css('a')['href']). + to eq "/#{project_path}/blob/#{ref}/README.md" + end + it 'rebuilds relative URL for a file in the repo with an anchor' do doc = filter(link('README.md#section')) expect(doc.at_css('a')['href']). -- cgit v1.2.1 From c479dbae225728234848467c5451f48f27462a05 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 29 Dec 2015 20:42:26 -0500 Subject: Add js-requires-input and js-quick-submit to abuse report form --- app/views/abuse_reports/new.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml index cffd7684008..3e5cdd2ce4a 100644 --- a/app/views/abuse_reports/new.html.haml +++ b/app/views/abuse_reports/new.html.haml @@ -2,7 +2,7 @@ %h3.page-title Report abuse %p Please use this form to report users who create spam issues, comments or behave inappropriately. %hr -= form_for @abuse_report, html: { class: 'form-horizontal'} do |f| += form_for @abuse_report, html: { class: 'form-horizontal js-requires-input'} do |f| = f.hidden_field :user_id - if @abuse_report.errors.any? .alert.alert-danger @@ -16,7 +16,7 @@ .form-group = f.label :message, class: 'control-label' .col-sm-10 - = f.text_area :message, class: "form-control", rows: 2, required: true + = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true .help-block Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment. -- cgit v1.2.1 From 5c6b9e8c57a1a8d117cafe0c11eda1950d66159e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Dec 2015 16:51:47 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 425beb17743..712563e5f06 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,10 +11,10 @@ v 8.4.0 (unreleased) - Add CAS support (tduehr) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages - - Enable "Add key" button when user fills in a proper key (Stan Hu) v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) + - Enable "Add key" button when user fills in a proper key (Stan Hu) v 8.3.2 - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) -- cgit v1.2.1 From 1be406d741a3096f831fbb85b09a24749d8bcfcb Mon Sep 17 00:00:00 2001 From: Zeger-Jan van de Weg Date: Thu, 17 Dec 2015 09:32:22 +0100 Subject: Swap Author and Assignee Selectors on issuable index view Closes #4039 --- CHANGELOG | 2 ++ app/views/shared/issuable/_filter.html.haml | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 712563e5f06..fc8ae9f8eb7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,8 @@ v 8.4.0 (unreleased) - Add CAS support (tduehr) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages + - Enable "Add key" button when user fills in a proper key (Stan Hu) + - Swap position of Assignee and Author selector on Issueables (Zeger-Jan van de Weg) v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml index ac6c248ccf1..be06738eac9 100644 --- a/app/views/shared/issuable/_filter.html.haml +++ b/app/views/shared/issuable/_filter.html.haml @@ -29,14 +29,14 @@ = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" .issues-other-filters - .filter-item.inline - = users_select_tag(:assignee_id, selected: params[:assignee_id], - placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true) - .filter-item.inline = users_select_tag(:author_id, selected: params[:author_id], placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true) + .filter-item.inline + = users_select_tag(:assignee_id, selected: params[:assignee_id], + placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true) + .filter-item.inline.milestone-filter = select_tag('milestone_title', projects_milestones_options, class: 'select2 trigger-submit', include_blank: true, -- cgit v1.2.1 From efc679e4aa02954a8df1de6ed4359a09190f6221 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Dec 2015 17:03:51 -0500 Subject: Fix "I see current user as the first user" step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Why did this break? `¯\_(ツ)_/¯` --- features/steps/project/issues/issues.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index 4a7ff21d385..8e8c9c57452 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -59,15 +59,14 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'I click "author" dropdown' do - first('.ajax-users-select').click + first('#s2id_author_id').click end step 'I see current user as the first user' do - expect(page).to have_selector('.user-result', visible: true, count: 4) + expect(page).to have_selector('.user-result', visible: true, count: 3) users = page.all('.user-name') - expect(users[0].text).to eq 'Any Assignee' - expect(users[1].text).to eq 'Unassigned' - expect(users[2].text).to eq current_user.name + expect(users[0].text).to eq 'Any Author' + expect(users[1].text).to eq current_user.name end step 'I submit new issue "500 error on profile"' do -- cgit v1.2.1 From 00dc5f1b3ea738c602246bda11fc607042b3e76d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Dec 2015 18:22:06 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index fc8ae9f8eb7..353d5060eba 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,7 +11,6 @@ v 8.4.0 (unreleased) - Add CAS support (tduehr) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages - - Enable "Add key" button when user fills in a proper key (Stan Hu) - Swap position of Assignee and Author selector on Issueables (Zeger-Jan van de Weg) v 8.3.3 (unreleased) -- cgit v1.2.1 From c936e4e3c8282df17f2dc89b305233b7608003ca Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 30 Dec 2015 15:53:52 +0100 Subject: Removed various default metrics tags While it's useful to keep track of the different versions (Ruby, GitLab, etc) doing so for every point wastes disk space and possibly also RAM (which InfluxDB is all to eager to gobble up). If we want to see the performance differences between different GitLab versions simply looking at the performance since the last release date should suffice. --- lib/gitlab/metrics/metric.rb | 7 ++----- spec/lib/gitlab/metrics/metric_spec.rb | 3 --- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index 79241f56874..753008df99a 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -19,11 +19,8 @@ module Gitlab { series: @series, tags: @tags.merge( - hostname: Metrics.hostname, - ruby_engine: RUBY_ENGINE, - ruby_version: RUBY_VERSION, - gitlab_version: Gitlab::VERSION, - process_type: Sidekiq.server? ? 'sidekiq' : 'rails' + hostname: Metrics.hostname, + process_type: Sidekiq.server? ? 'sidekiq' : 'rails' ), values: @values, timestamp: @created_at.to_i * 1_000_000_000 diff --git a/spec/lib/gitlab/metrics/metric_spec.rb b/spec/lib/gitlab/metrics/metric_spec.rb index ec39bc9cce8..aa76315c79c 100644 --- a/spec/lib/gitlab/metrics/metric_spec.rb +++ b/spec/lib/gitlab/metrics/metric_spec.rb @@ -39,9 +39,6 @@ describe Gitlab::Metrics::Metric do expect(hash[:tags]).to be_an_instance_of(Hash) expect(hash[:tags][:hostname]).to be_an_instance_of(String) - expect(hash[:tags][:ruby_engine]).to be_an_instance_of(String) - expect(hash[:tags][:ruby_version]).to be_an_instance_of(String) - expect(hash[:tags][:gitlab_version]).to be_an_instance_of(String) expect(hash[:tags][:process_type]).to be_an_instance_of(String) end -- cgit v1.2.1 From 6d5202e2b6bcb563d6998ed530b6ecd9f4748156 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 31 Dec 2015 12:27:23 +0100 Subject: Updated allocations Gem to version 1.0.3 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c4cadbafa26..9769ae80a7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,7 +49,7 @@ GEM addressable (2.3.8) after_commit_queue (1.3.0) activerecord (>= 3.0) - allocations (1.0.1) + allocations (1.0.3) annotate (2.6.10) activerecord (>= 3.2, <= 4.3) rake (~> 10.4) -- cgit v1.2.1 From bf03acfa259169e8a060d2c3fc55c8042226557c Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 31 Dec 2015 15:58:52 +0200 Subject: spinach fix --- features/steps/project/issues/award_emoji.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb index 1404f34cfe0..2c2ed08655e 100644 --- a/features/steps/project/issues/award_emoji.rb +++ b/features/steps/project/issues/award_emoji.rb @@ -24,7 +24,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps page.within '.awards' do expect do page.find('.award.active').click - sleep 0.1 + sleep 0.3 end.to change{ page.all(".award").size }.from(3).to(2) end end -- cgit v1.2.1 From 3077cb52d904154b98ee3e9aced5b3aadae86941 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 30 Dec 2015 17:16:02 +0100 Subject: Use XPath for searching link nodes This is a tad faster than letting Nokogiri figure out whether it should evaluate the query as CSS or XPath and then actually evaluating it. --- lib/banzai/filter/reference_filter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 8ca05ace88c..7198a8b03e2 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -124,7 +124,7 @@ module Banzai def replace_link_nodes_with_text(pattern) return doc if project.nil? - doc.search('a').each do |node| + doc.xpath('descendant-or-self::a').each do |node| klass = node.attr('class') next if klass && klass.include?('gfm') @@ -162,7 +162,7 @@ module Banzai def replace_link_nodes_with_href(pattern) return doc if project.nil? - doc.search('a').each do |node| + doc.xpath('descendant-or-self::a').each do |node| klass = node.attr('class') next if klass && klass.include?('gfm') -- cgit v1.2.1 From d3951dfaa13b9aaf11695ef10fa63456ac75cc48 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 30 Dec 2015 17:16:59 +0100 Subject: Don't use delegate to delegate trivial methods Around 300 ms (in total) would be spent in these delegated methods due to the extra stuff ActiveSupport adds to the compiled methods. Because these delegations are so simple we can just manually define the methods, saving around 275 milliseconds. --- lib/banzai/filter/abstract_reference_filter.rb | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 63ad8910c0f..230387c8383 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -47,7 +47,17 @@ module Banzai { object_sym => LazyReference.new(object_class, node.attr(data_reference)) } end - delegate :object_class, :object_sym, :references_in, to: :class + def object_class + self.class.object_class + end + + def object_sym + self.class.object_sym + end + + def references_in(*args, &block) + self.class.references_in(*args, &block) + end def find_object(project, id) # Implement in child class -- cgit v1.2.1 From 054df415f94abe1e517a729e53cdb325d592d31b Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 30 Dec 2015 18:16:53 +0100 Subject: Optimize CSS expressions produced by Nokogiri Nokogiri produces inefficient XPath expressions when given CSS expressions such as "a.gfm". Luckily these expressions can be optimized quite easily while still achieving the same results. In the two cases where this optimization is applied the run time has been reduced from around 170 ms to around 15 ms. --- lib/banzai/filter/redactor_filter.rb | 2 +- lib/banzai/filter/reference_gatherer_filter.rb | 2 +- lib/banzai/querying.rb | 18 ++++++++++++++++++ spec/lib/banzai/querying_spec.rb | 13 +++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 lib/banzai/querying.rb create mode 100644 spec/lib/banzai/querying_spec.rb diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb index f01a32b5ae5..66f77902319 100644 --- a/lib/banzai/filter/redactor_filter.rb +++ b/lib/banzai/filter/redactor_filter.rb @@ -10,7 +10,7 @@ module Banzai # class RedactorFilter < HTML::Pipeline::Filter def call - doc.css('a.gfm').each do |node| + Querying.css(doc, 'a.gfm').each do |node| unless user_can_see_reference?(node) # The reference should be replaced by the original text, # which is not always the same as the rendered text. diff --git a/lib/banzai/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb index 12412ff7ea9..bef04112919 100644 --- a/lib/banzai/filter/reference_gatherer_filter.rb +++ b/lib/banzai/filter/reference_gatherer_filter.rb @@ -16,7 +16,7 @@ module Banzai end def call - doc.css('a.gfm').each do |node| + Querying.css(doc, 'a.gfm').each do |node| gather_references(node) end diff --git a/lib/banzai/querying.rb b/lib/banzai/querying.rb new file mode 100644 index 00000000000..1e1b51e683e --- /dev/null +++ b/lib/banzai/querying.rb @@ -0,0 +1,18 @@ +module Banzai + module Querying + # Searches a Nokogiri document using a CSS query, optionally optimizing it + # whenever possible. + # + # document - A document/element to search. + # query - The CSS query to use. + # + # Returns a Nokogiri::XML::NodeSet. + def self.css(document, query) + # When using "a.foo" Nokogiri compiles this to "//a[...]" but + # "descendant::a[...]" is quite a bit faster and achieves the same result. + xpath = Nokogiri::CSS.xpath_for(query)[0].gsub(%r{^//}, 'descendant::') + + document.xpath(xpath) + end + end +end diff --git a/spec/lib/banzai/querying_spec.rb b/spec/lib/banzai/querying_spec.rb new file mode 100644 index 00000000000..27da2a7439c --- /dev/null +++ b/spec/lib/banzai/querying_spec.rb @@ -0,0 +1,13 @@ +require 'spec_helper' + +describe Banzai::Querying do + describe '.css' do + it 'optimizes queries for elements with classes' do + document = double(:document) + + expect(document).to receive(:xpath).with(/^descendant::a/) + + described_class.css(document, 'a.gfm') + end + end +end -- cgit v1.2.1 From ea4777ff501e370a39ae30e76a955136afe3c1fa Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 15:19:13 +0100 Subject: Add features for list and show details of variables in API --- app/models/ci/variable.rb | 1 + lib/api/api.rb | 2 + lib/api/entities.rb | 4 ++ lib/api/variables.rb | 43 +++++++++++++++++++++ spec/factories/ci/variables.rb | 25 +++++++++++++ spec/requests/api/variables_spec.rb | 75 +++++++++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 lib/api/variables.rb create mode 100644 spec/factories/ci/variables.rb create mode 100644 spec/requests/api/variables_spec.rb diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 56759d3e50f..0e2712086ca 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -9,6 +9,7 @@ # encrypted_value :text # encrypted_value_salt :string(255) # encrypted_value_iv :string(255) +# gl_project_id :integer # module Ci diff --git a/lib/api/api.rb b/lib/api/api.rb index 7834262d612..a9e1913f0f2 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -54,5 +54,7 @@ module API mount Keys mount Tags mount Triggers + + mount Variables end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 26e7c956e8f..f71d072f269 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -365,5 +365,9 @@ module API class TriggerRequest < Grape::Entity expose :id, :variables end + + class Variable < Grape::Entity + expose :id, :key, :value + end end end diff --git a/lib/api/variables.rb b/lib/api/variables.rb new file mode 100644 index 00000000000..6517150f6f4 --- /dev/null +++ b/lib/api/variables.rb @@ -0,0 +1,43 @@ +module API + # Projects variables API + class Variables < Grape::API + before { authenticate! } + before { authorize_admin_project } + + resource :projects do + # Get project variables + # + # Parameters: + # id (required) - The ID of a project + # page (optional) - The page number for pagination + # per_page (optional) - The value of items per page to show + # Example Request: + # GET /projects/:id/variables + get ':id/variables' do + variables = user_project.variables + present paginate(variables), with: Entities::Variable + end + + # Get specifica bariable of a project + # + # Parameters: + # id (required) - The ID of a project + # variable_id (required) - The ID OR `key` of variable to show; if variable_id contains only digits it's treated + # as ID other ways it's treated as `key` + # Example Reuest: + # GET /projects/:id/variables/:variable_id + get ':id/variables/:variable_id' do + variable_id = params[:variable_id] + variables = user_project.variables + variables = + if variable_id.match(/^\d+$/) + variables.where(id: variable_id.to_i) + else + variables.where(key: variable_id) + end + + present variables.first, with: Entities::Variable + end + end + end +end diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb new file mode 100644 index 00000000000..c3dcb678da7 --- /dev/null +++ b/spec/factories/ci/variables.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: ci_variables +# +# id :integer not null, primary key +# project_id :integer not null +# key :string(255) +# value :text +# encrypted_value :text +# encrypted_value_salt :string(255) +# encrypted_value_iv :string(255) +# gl_project_id :integer +# + +# Read about factories at https://github.com/thoughtbot/factory_girl + +FactoryGirl.define do + factory :ci_variable, class: Ci::Variable do + id 1 + key 'TEST_VARIABLE_1' + value 'VALUE_1' + + project factory: :empty_project + end +end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb new file mode 100644 index 00000000000..8f66f5432b6 --- /dev/null +++ b/spec/requests/api/variables_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:project) { create(:project, creator_id: user.id) } + let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } + let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) } + let!(:variable) { create(:ci_variable, project: project) } + + describe 'GET /projects/:id/variables' do + context 'authorized user with proper permissions' do + it 'should return project variables' do + get api("/projects/#{project.id}/variables", user) + + expect(response.status).to eq(200) + expect(json_response).to be_a(Array) + end + end + + context 'authorized user with invalid permissions' do + it 'should not return project variables' do + get api("/projects/#{project.id}/variables", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not return project variables' do + get api("/projects/#{project.id}/variables") + + expect(response.status).to eq(401) + end + end + end + + describe 'GET /projects/:id/variables/:variable_id' do + context 'authorized user with proper permissions' do + it 'should return project variable details when ID is used as :variable_id' do + get api("/projects/#{project.id}/variables/1", user) + + expect(response.status).to eq(200) + expect(json_response['key']).to eq('TEST_VARIABLE_1') + expect(json_response['value']).to eq('VALUE_1') + end + + it 'should return project variable details when `key` is used as :variable_id' do + get api("/projects/#{project.id}/variables/TEST_VARIABLE_1", user) + + expect(response.status).to eq(200) + expect(json_response['id']).to eq(1) + expect(json_response['value']).to eq('VALUE_1') + end + end + + context 'authorized user with invalid permissions' do + it 'should not return project variable details' do + get api("/projects/#{project.id}/variables/1", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not return project variable details' do + get api("/projects/#{project.id}/variables/1") + + expect(response.status).to eq(401) + end + end + end +end -- cgit v1.2.1 From a692ce1c079703c4f3947e1d0a29547189e94d0f Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 16:25:49 +0100 Subject: Add update feature for variables API --- lib/api/variables.rb | 21 ++++++++++++++- spec/requests/api/variables_spec.rb | 52 +++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 6517150f6f4..6522ecba70c 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -24,7 +24,7 @@ module API # id (required) - The ID of a project # variable_id (required) - The ID OR `key` of variable to show; if variable_id contains only digits it's treated # as ID other ways it's treated as `key` - # Example Reuest: + # Example Request: # GET /projects/:id/variables/:variable_id get ':id/variables/:variable_id' do variable_id = params[:variable_id] @@ -38,6 +38,25 @@ module API present variables.first, with: Entities::Variable end + + # Update existing variable of a project + # + # Parameters: + # id (required) - The ID of a project + # variable_id (required) - The ID of a variable + # key (optional) - new value for `key` field of variable + # value (optional) - new value for `value` field of variable + # Example Request: + # PUT /projects/:id/variables/:variable_id + put ':id/variables/:variable_id' do + variable = user_project.variables.where(id: params[:variable_id].to_i).first + + variable.key = params[:key] + variable.value = params[:value] + variable.save! + + present variable, with: Entities::Variable + end end end end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index 8f66f5432b6..3f58277c4ae 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -40,25 +40,25 @@ describe API::API, api: true do describe 'GET /projects/:id/variables/:variable_id' do context 'authorized user with proper permissions' do it 'should return project variable details when ID is used as :variable_id' do - get api("/projects/#{project.id}/variables/1", user) + get api("/projects/#{project.id}/variables/#{variable.id}", user) expect(response.status).to eq(200) - expect(json_response['key']).to eq('TEST_VARIABLE_1') - expect(json_response['value']).to eq('VALUE_1') + expect(json_response['key']).to eq(variable.key) + expect(json_response['value']).to eq(variable.value) end it 'should return project variable details when `key` is used as :variable_id' do - get api("/projects/#{project.id}/variables/TEST_VARIABLE_1", user) + get api("/projects/#{project.id}/variables/#{variable.key}", user) expect(response.status).to eq(200) - expect(json_response['id']).to eq(1) - expect(json_response['value']).to eq('VALUE_1') + expect(json_response['id']).to eq(variable.id) + expect(json_response['value']).to eq(variable.value) end end context 'authorized user with invalid permissions' do it 'should not return project variable details' do - get api("/projects/#{project.id}/variables/1", user2) + get api("/projects/#{project.id}/variables/#{variable.id}", user2) expect(response.status).to eq(403) end @@ -66,7 +66,43 @@ describe API::API, api: true do context 'unauthorized user' do it 'should not return project variable details' do - get api("/projects/#{project.id}/variables/1") + get api("/projects/#{project.id}/variables/#{variable.id}") + + expect(response.status).to eq(401) + end + end + end + + describe 'PUT /projects/:id/variables/:variable_id' do + context 'authorized user with proper permissions' do + it 'should update variable data' do + initial_variable = project.variables.first + key_before = initial_variable.key + value_before = initial_variable.value + + put api("/projects/#{project.id}/variables/#{variable.id}", user), key: 'TEST_VARIABLE_1_UP', value: 'VALUE_1_UP' + + updated_variable = project.variables.first + + expect(response.status).to eq(200) + expect(key_before).to eq(variable.key) + expect(value_before).to eq(variable.value) + expect(updated_variable.key).to eq('TEST_VARIABLE_1_UP') + expect(updated_variable.value).to eq('VALUE_1_UP') + end + end + + context 'authorized user with invalid permissions' do + it 'should not update variable' do + put api("/projects/#{project.id}/variables/#{variable.id}", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not return project variable details' do + put api("/projects/#{project.id}/variables/#{variable.id}") expect(response.status).to eq(401) end -- cgit v1.2.1 From 0d014feb1d216e692882976f0d70c3227eaec4ca Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 16:56:03 +0100 Subject: Add delete feature to variables API --- lib/api/variables.rb | 12 ++++++++++++ spec/requests/api/variables_spec.rb | 29 ++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 6522ecba70c..c70c7cd9d7b 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -57,6 +57,18 @@ module API present variable, with: Entities::Variable end + + # Delete existing variable of a project + # + # Parameters: + # id (required) - The ID of a project + # variable_id (required) - The ID of a variable + # Exanoke Reqyest: + # DELETE /projects/:id/variables/:variable_id + delete ':id/variables/:variable_id' do + variable = user_project.variables.where(id: params[:variable_id].to_i).first + variable.destroy + end end end end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index 3f58277c4ae..385db2409bd 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -101,11 +101,38 @@ describe API::API, api: true do end context 'unauthorized user' do - it 'should not return project variable details' do + it 'should not update variable' do put api("/projects/#{project.id}/variables/#{variable.id}") expect(response.status).to eq(401) end end end + + describe 'DELETE /projects/:id/variables/:variable_id' do + context 'authorized user with proper permissions' do + it 'should delete variable' do + expect do + delete api("/projects/#{project.id}/variables/#{variable.id}", user) + end.to change{project.variables.count}.by(-1) + expect(response.status).to eq(200) + end + end + + context 'authorized user with invalid permissions' do + it 'should not delete variable' do + delete api("/projects/#{project.id}/variables/#{variable.id}", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not delete variable' do + delete api("/projects/#{project.id}/variables/#{variable.id}") + + expect(response.status).to eq(401) + end + end + end end -- cgit v1.2.1 From c5177dd5e2171b047a695802c979cf779522ba8a Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 17:03:11 +0100 Subject: Add missing 'not_found' checks in variables API --- lib/api/variables.rb | 7 +++++++ spec/requests/api/variables_spec.rb | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/api/variables.rb b/lib/api/variables.rb index c70c7cd9d7b..dac2ba679c7 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -36,6 +36,8 @@ module API variables.where(key: variable_id) end + return not_found!('Variable') if variables.empty? + present variables.first, with: Entities::Variable end @@ -51,6 +53,8 @@ module API put ':id/variables/:variable_id' do variable = user_project.variables.where(id: params[:variable_id].to_i).first + return not_found!('Variable') unless variable + variable.key = params[:key] variable.value = params[:value] variable.save! @@ -67,6 +71,9 @@ module API # DELETE /projects/:id/variables/:variable_id delete ':id/variables/:variable_id' do variable = user_project.variables.where(id: params[:variable_id].to_i).first + + return not_found!('Variable') unless variable + variable.destroy end end diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index 385db2409bd..b35ee2d32d1 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -54,6 +54,12 @@ describe API::API, api: true do expect(json_response['id']).to eq(variable.id) expect(json_response['value']).to eq(variable.value) end + + it 'should responde with 404 Not Found if requesting non-existing variable' do + get api("/projects/#{project.id}/variables/9999", user) + + expect(response.status).to eq(404) + end end context 'authorized user with invalid permissions' do @@ -90,6 +96,12 @@ describe API::API, api: true do expect(updated_variable.key).to eq('TEST_VARIABLE_1_UP') expect(updated_variable.value).to eq('VALUE_1_UP') end + + it 'should responde with 404 Not Found if requesting non-existing variable' do + put api("/projects/#{project.id}/variables/9999", user) + + expect(response.status).to eq(404) + end end context 'authorized user with invalid permissions' do @@ -117,6 +129,12 @@ describe API::API, api: true do end.to change{project.variables.count}.by(-1) expect(response.status).to eq(200) end + + it 'should responde with 404 Not Found if requesting non-existing variable' do + delete api("/projects/#{project.id}/variables/9999", user) + + expect(response.status).to eq(404) + end end context 'authorized user with invalid permissions' do -- cgit v1.2.1 From a6c60127e3e2966b2f29fa6e6e79503b130c2b02 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 31 Dec 2015 17:14:02 +0100 Subject: Removed tracking of raw SQL queries This particular setup had 3 problems: 1. Storing SQL queries as tags is very inefficient as InfluxDB ends up indexing every query (and they can get pretty large). Storing these as values instead means we can't always display the SQL as easily. 2. We already instrument ActiveRecord query methods, thus we already have timing information about database queries. 3. SQL obfuscation is difficult to get right and I'd rather not expose sensitive data by accident. --- config/initializers/metrics.rb | 1 - lib/gitlab/metrics/obfuscated_sql.rb | 47 ----------- lib/gitlab/metrics/subscribers/active_record.rb | 48 ----------- spec/lib/gitlab/metrics/obfuscated_sql_spec.rb | 93 ---------------------- .../metrics/subscribers/active_record_spec.rb | 32 -------- 5 files changed, 221 deletions(-) delete mode 100644 lib/gitlab/metrics/obfuscated_sql.rb delete mode 100644 lib/gitlab/metrics/subscribers/active_record.rb delete mode 100644 spec/lib/gitlab/metrics/obfuscated_sql_spec.rb delete mode 100644 spec/lib/gitlab/metrics/subscribers/active_record_spec.rb diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index 2e4908192a1..94c535dc562 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -7,7 +7,6 @@ if Gitlab::Metrics.enabled? # These are manually require'd so the classes are registered properly with # ActiveSupport. require 'gitlab/metrics/subscribers/action_view' - require 'gitlab/metrics/subscribers/active_record' Gitlab::Application.configure do |config| config.middleware.use(Gitlab::Metrics::RackMiddleware) diff --git a/lib/gitlab/metrics/obfuscated_sql.rb b/lib/gitlab/metrics/obfuscated_sql.rb deleted file mode 100644 index fe97d7a0534..00000000000 --- a/lib/gitlab/metrics/obfuscated_sql.rb +++ /dev/null @@ -1,47 +0,0 @@ -module Gitlab - module Metrics - # Class for producing SQL queries with sensitive data stripped out. - class ObfuscatedSQL - REPLACEMENT = / - \d+(\.\d+)? # integers, floats - | '.+?' # single quoted strings - | \/.+?(? Date: Thu, 31 Dec 2015 17:47:07 +0100 Subject: Cache InfluxDB settings after the first use This ensures we don't need to load anything from either PostgreSQL or the Rails cache whenever creating new InfluxDB connections. --- lib/gitlab/metrics.rb | 32 ++++++++++++++++++-------------- spec/lib/gitlab/metrics_spec.rb | 12 +++--------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index 2d266ccfe9e..c5b98a0115e 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -6,16 +6,21 @@ module Gitlab METRICS_ROOT = Rails.root.join('lib', 'gitlab', 'metrics').to_s PATH_REGEX = /^#{RAILS_ROOT}\/?/ - def self.pool_size - current_application_settings[:metrics_pool_size] || 16 - end - - def self.timeout - current_application_settings[:metrics_timeout] || 10 + def self.settings + @settings ||= { + enabled: current_application_settings[:metrics_enabled], + pool_size: current_application_settings[:metrics_pool_size], + timeout: current_application_settings[:metrics_timeout], + method_call_threshold: current_application_settings[:metrics_method_call_threshold], + host: current_application_settings[:metrics_host], + username: current_application_settings[:metrics_username], + password: current_application_settings[:metrics_password], + port: current_application_settings[:metrics_port] + } end def self.enabled? - current_application_settings[:metrics_enabled] || false + settings[:enabled] || false end def self.mri? @@ -26,8 +31,7 @@ module Gitlab # This is memoized since this method is called for every instrumented # method. Loading data from an external cache on every method call slows # things down too much. - @method_call_threshold ||= - (current_application_settings[:metrics_method_call_threshold] || 10) + @method_call_threshold ||= settings[:method_call_threshold] end def self.pool @@ -90,11 +94,11 @@ module Gitlab # When enabled this should be set before being used as the usual pattern # "@foo ||= bar" is _not_ thread-safe. if enabled? - @pool = ConnectionPool.new(size: pool_size, timeout: timeout) do - host = current_application_settings[:metrics_host] - user = current_application_settings[:metrics_username] - pw = current_application_settings[:metrics_password] - port = current_application_settings[:metrics_port] + @pool = ConnectionPool.new(size: settings[:pool_size], timeout: settings[:timeout]) do + host = settings[:host] + user = settings[:username] + pw = settings[:password] + port = settings[:port] InfluxDB::Client. new(udp: { host: host, port: port }, username: user, password: pw) diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index 6c0682cac4d..c2924708f44 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -1,15 +1,9 @@ require 'spec_helper' describe Gitlab::Metrics do - describe '.pool_size' do - it 'returns a Fixnum' do - expect(described_class.pool_size).to be_an_instance_of(Fixnum) - end - end - - describe '.timeout' do - it 'returns a Fixnum' do - expect(described_class.timeout).to be_an_instance_of(Fixnum) + describe '.settings' do + it 'returns a Hash' do + expect(described_class.settings).to be_an_instance_of(Hash) end end -- cgit v1.2.1 From bd9f86bb8abb4759a0c72f94fb0492b1ff8619b5 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 31 Dec 2015 17:51:12 +0100 Subject: Use separate series for Rails/Sidekiq transactions This removes the need for tagging all metrics with a "process_type" tag. --- lib/gitlab/metrics/metric.rb | 3 +-- lib/gitlab/metrics/rack_middleware.rb | 4 +++- lib/gitlab/metrics/sidekiq_middleware.rb | 4 +++- lib/gitlab/metrics/transaction.rb | 7 +++---- spec/lib/gitlab/metrics/instrumentation_spec.rb | 2 +- spec/lib/gitlab/metrics/metric_spec.rb | 1 - spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb | 2 +- spec/lib/gitlab/metrics/subscribers/action_view_spec.rb | 2 +- spec/lib/gitlab/metrics/transaction_spec.rb | 4 ++-- 9 files changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index 753008df99a..8319e628a40 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -19,8 +19,7 @@ module Gitlab { series: @series, tags: @tags.merge( - hostname: Metrics.hostname, - process_type: Sidekiq.server? ? 'sidekiq' : 'rails' + hostname: Metrics.hostname ), values: @values, timestamp: @created_at.to_i * 1_000_000_000 diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index 5c0587c4c51..bb9e4fcb918 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -4,6 +4,8 @@ module Gitlab class RackMiddleware CONTROLLER_KEY = 'action_controller.instance' + SERIES = 'rails_transactions' + def initialize(app) @app = app end @@ -30,7 +32,7 @@ module Gitlab end def transaction_from_env(env) - trans = Transaction.new + trans = Transaction.new(SERIES) trans.add_tag(:request_method, env['REQUEST_METHOD']) trans.add_tag(:request_uri, env['REQUEST_URI']) diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index ad441decfa2..6e804dd2562 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -4,8 +4,10 @@ module Gitlab # # This middleware is intended to be used as a server-side middleware. class SidekiqMiddleware + SERIES = 'sidekiq_transactions' + def call(worker, message, queue) - trans = Transaction.new + trans = Transaction.new(SERIES) begin trans.run { yield } diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index a61dbd989e7..43a7dab5323 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -4,8 +4,6 @@ module Gitlab class Transaction THREAD_KEY = :_gitlab_metrics_transaction - SERIES = 'transactions' - attr_reader :uuid, :tags def self.current @@ -13,7 +11,8 @@ module Gitlab end # name - The name of this transaction as a String. - def initialize + def initialize(series) + @series = series @metrics = [] @uuid = SecureRandom.uuid @@ -55,7 +54,7 @@ module Gitlab end def track_self - add_metric(SERIES, { duration: duration }, @tags) + add_metric(@series, { duration: duration }, @tags) end def submit diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index a7eab9d11cc..a9003d8796b 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Instrumentation do - let(:transaction) { Gitlab::Metrics::Transaction.new } + let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } before do @dummy = Class.new do diff --git a/spec/lib/gitlab/metrics/metric_spec.rb b/spec/lib/gitlab/metrics/metric_spec.rb index aa76315c79c..9b942855140 100644 --- a/spec/lib/gitlab/metrics/metric_spec.rb +++ b/spec/lib/gitlab/metrics/metric_spec.rb @@ -39,7 +39,6 @@ describe Gitlab::Metrics::Metric do expect(hash[:tags]).to be_an_instance_of(Hash) expect(hash[:tags][:hostname]).to be_an_instance_of(String) - expect(hash[:tags][:process_type]).to be_an_instance_of(String) end it 'includes the values' do diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb index 5882e7d81c7..5fda6de52f4 100644 --- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::Metrics::SidekiqMiddleware do describe '#tag_worker' do it 'adds the worker class and action to the transaction' do - trans = Gitlab::Metrics::Transaction.new + trans = Gitlab::Metrics::Transaction.new('rspec') worker = double(:worker, class: double(:class, name: 'TestWorker')) expect(trans).to receive(:add_tag).with(:action, 'TestWorker#perform') diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb index c6cd584663f..bca76ca5a69 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Subscribers::ActionView do - let(:transaction) { Gitlab::Metrics::Transaction.new } + let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } let(:subscriber) { described_class.new } diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 6862fc9e2d1..345163bfbea 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Transaction do - let(:transaction) { described_class.new } + let(:transaction) { described_class.new('rspec') } describe '#duration' do it 'returns the duration of a transaction in seconds' do @@ -58,7 +58,7 @@ describe Gitlab::Metrics::Transaction do describe '#track_self' do it 'adds a metric for the transaction itself' do expect(transaction).to receive(:add_metric). - with(described_class::SERIES, { duration: transaction.duration }, {}) + with('rspec', { duration: transaction.duration }, {}) transaction.track_self end -- cgit v1.2.1 From cafc784ee1d5d0a0279077272af8ee435bb110e4 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 31 Dec 2015 17:55:10 +0100 Subject: Removed tracking of hostnames for metrics This isn't hugely useful and mostly wastes InfluxDB space. We can re-add this whenever needed (but only once we really need it). --- config/initializers/metrics.rb | 1 - lib/gitlab/metrics.rb | 6 ------ lib/gitlab/metrics/metric.rb | 6 ++---- spec/lib/gitlab/metrics/metric_spec.rb | 2 -- spec/lib/gitlab/metrics_spec.rb | 6 ------ 5 files changed, 2 insertions(+), 19 deletions(-) diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index 94c535dc562..ebb20be7283 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -1,6 +1,5 @@ if Gitlab::Metrics.enabled? require 'influxdb' - require 'socket' require 'connection_pool' require 'method_source' diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index c5b98a0115e..ee88ab34d6c 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -38,10 +38,6 @@ module Gitlab @pool end - def self.hostname - @hostname - end - # Returns a relative path and line number based on the last application call # frame. def self.last_relative_application_frame @@ -89,8 +85,6 @@ module Gitlab value.to_s.gsub('=', '\\=') end - @hostname = Socket.gethostname - # When enabled this should be set before being used as the usual pattern # "@foo ||= bar" is _not_ thread-safe. if enabled? diff --git a/lib/gitlab/metrics/metric.rb b/lib/gitlab/metrics/metric.rb index 8319e628a40..7ea9555cc8c 100644 --- a/lib/gitlab/metrics/metric.rb +++ b/lib/gitlab/metrics/metric.rb @@ -17,10 +17,8 @@ module Gitlab # Returns a Hash in a format that can be directly written to InfluxDB. def to_hash { - series: @series, - tags: @tags.merge( - hostname: Metrics.hostname - ), + series: @series, + tags: @tags, values: @values, timestamp: @created_at.to_i * 1_000_000_000 } diff --git a/spec/lib/gitlab/metrics/metric_spec.rb b/spec/lib/gitlab/metrics/metric_spec.rb index 9b942855140..f718d536130 100644 --- a/spec/lib/gitlab/metrics/metric_spec.rb +++ b/spec/lib/gitlab/metrics/metric_spec.rb @@ -37,8 +37,6 @@ describe Gitlab::Metrics::Metric do it 'includes the tags' do expect(hash[:tags]).to be_an_instance_of(Hash) - - expect(hash[:tags][:hostname]).to be_an_instance_of(String) end it 'includes the values' do diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index c2924708f44..c2782f95c8e 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -13,12 +13,6 @@ describe Gitlab::Metrics do end end - describe '.hostname' do - it 'returns a String containing the hostname' do - expect(described_class.hostname).to eq(Socket.gethostname) - end - end - describe '.last_relative_application_frame' do it 'returns an Array containing a file path and line number' do file, line = described_class.last_relative_application_frame -- cgit v1.2.1 From d33cc4e53070e6afb576911aa4d76dc80eba78b7 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 31 Dec 2015 14:21:31 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 353d5060eba..ea403642e32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,12 +6,12 @@ v 8.4.0 (unreleased) - Add API support for looking up a user by username (Stan Hu) - Add project permissions to all project API endpoints (Stan Hu) - Only allow group/project members to mention `@all` - - Expose Git's version in the admin area + - Expose Git's version in the admin area (Trey Davis) - Add "Frequently used" category to emoji picker - Add CAS support (tduehr) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages - - Swap position of Assignee and Author selector on Issueables (Zeger-Jan van de Weg) + - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) -- cgit v1.2.1 From 937567b767e6d7b34dcaa1d9c83fc75464638683 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 22:30:07 +0100 Subject: Add create feature to variables API --- lib/api/variables.rb | 20 +++++++++++++++++++ spec/factories/ci/variables.rb | 2 +- spec/requests/api/variables_spec.rb | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/lib/api/variables.rb b/lib/api/variables.rb index dac2ba679c7..fc63ac2f56a 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -41,6 +41,24 @@ module API present variables.first, with: Entities::Variable end + # Create a new variable in project + # + # Parameters: + # id (required) - The ID of a project + # key (required) - The key of variable being created + # value (required) - The value of variable being created + # Example Request: + # POST /projects/:id/variables + post ':id/variables' do + required_attributes! [:key, :value] + + variable = user_project.variables.create(key: params[:key], value: params[:value]) + return render_validation_error!(variable) unless variable.valid? + variable.save! + + present variable, with: Entities::Variable + end + # Update existing variable of a project # # Parameters: @@ -75,6 +93,8 @@ module API return not_found!('Variable') unless variable variable.destroy + + present variable, with: Entities::Variable end end end diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb index c3dcb678da7..a19b9fc72f2 100644 --- a/spec/factories/ci/variables.rb +++ b/spec/factories/ci/variables.rb @@ -16,7 +16,7 @@ FactoryGirl.define do factory :ci_variable, class: Ci::Variable do - id 1 + id 10 key 'TEST_VARIABLE_1' value 'VALUE_1' diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index b35ee2d32d1..bf0dd77473a 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -79,6 +79,44 @@ describe API::API, api: true do end end + describe 'POST /projects/:id/variables' do + context 'authorized user with proper permissions' do + it 'should create variable' do + expect do + post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2' + end.to change{project.variables.count}.by(1) + + expect(response.status).to eq(201) + expect(json_response['key']).to eq('TEST_VARIABLE_2') + expect(json_response['value']).to eq('VALUE_2') + end + + it 'should not allow to duplicate variable key' do + expect do + post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_1', value: 'VALUE_2' + end.to change{project.variables.count}.by(0) + + expect(response.status).to eq(400) + end + end + + context 'authorized user with invalid permissions' do + it 'should not create variable' do + post api("/projects/#{project.id}/variables", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthorized user' do + it 'should not create variable' do + post api("/projects/#{project.id}/variables") + + expect(response.status).to eq(401) + end + end + end + describe 'PUT /projects/:id/variables/:variable_id' do context 'authorized user with proper permissions' do it 'should update variable data' do -- cgit v1.2.1 From 16bd4df083135e2e4a263b2e1bdd71b78a875ef7 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 22:59:06 +0100 Subject: Fix a typo in method description --- lib/api/variables.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/api/variables.rb b/lib/api/variables.rb index fc63ac2f56a..b8bbcb6ce3b 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -18,7 +18,7 @@ module API present paginate(variables), with: Entities::Variable end - # Get specifica bariable of a project + # Get specifica variable of a project # # Parameters: # id (required) - The ID of a project -- cgit v1.2.1 From 628297fe5692fc241c93ff34cece71132bfb9aed Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 31 Dec 2015 23:01:28 +0100 Subject: Remove incorrect 'default' values from method description --- lib/api/builds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/api/builds.rb b/lib/api/builds.rb index a519224d2b8..92bf849824c 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -9,8 +9,8 @@ module API # Parameters: # id (required) - The ID of a project # scope (optional) - The scope of builds to show (one of: all, finished, running) - # page (optional) - The page number for pagination (default: 1) - # per_page (ooptional) - The value of items per page to show (default 30) + # page (optional) - The page number for pagination + # per_page (ooptional) - The value of items per page to show # Example Request: # GET /projects/:id/builds get ':id/builds' do -- cgit v1.2.1 From 90029a5caaef1fd9d41a8ac02a7e9840ce3ac7b5 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Thu, 31 Dec 2015 18:27:34 -0400 Subject: Actually test the posted comment in Asana service --- spec/models/project_services/asana_service_spec.rb | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index 306d18171be..a7b32ac07a9 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -40,6 +40,20 @@ describe AsanaService, models: true do let(:user) { create(:user) } let(:project) { create(:project) } + def create_data_for_commits(*messages) + data = { + object_kind: 'push', + ref: 'master', + user_name: user.name, + commits: messages.map do |m| + { + message: m, + url: 'https://gitlab.com/', + } + end + } + end + before do @asana = AsanaService.new allow(@asana).to receive_messages( @@ -51,12 +65,15 @@ describe AsanaService, models: true do ) end - it 'should call Asana service to created a story' do - d1 = double('Asana::Task', add_comment: true) - expect(d1).to receive(:add_comment) + it 'should call Asana service to create a story' do + data = create_data_for_commits('Message from commit. related to #123456') + expected_message = "#{data[:user_name]} pushed to branch #{data[:ref]} of #{project.name_with_namespace} ( #{data[:commits][0][:url]} ): #{data[:commits][0][:message]}" + + d1 = double('Asana::Task') + expect(d1).to receive(:add_comment).with(text: expected_message) expect(Asana::Task).to receive(:find_by_id).with(anything, '123456').once.and_return(d1) - @asana.check_commit('related to #123456', 'pushed') + @asana.execute(data) end it 'should call Asana service to created a story and close a task' do -- cgit v1.2.1 From 571df5f44bfec89b21bdce0f91f9acfdda6d7660 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Thu, 31 Dec 2015 18:29:00 -0400 Subject: Use `execute` in Asana specs --- spec/models/project_services/asana_service_spec.rb | 33 ++++++++++++---------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index a7b32ac07a9..0db48c75d1d 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -76,53 +76,56 @@ describe AsanaService, models: true do @asana.execute(data) end - it 'should call Asana service to created a story and close a task' do - d1 = double('Asana::Task', add_comment: true) + it 'should call Asana service to create a story and close a task' do + data = create_data_for_commits('fix #456789') + d1 = double('Asana::Task') expect(d1).to receive(:add_comment) expect(d1).to receive(:update).with(completed: true) expect(Asana::Task).to receive(:find_by_id).with(anything, '456789').once.and_return(d1) - @asana.check_commit('fix #456789', 'pushed') + @asana.execute(data) end it 'should be able to close via url' do - d1 = double('Asana::Task', add_comment: true) + data = create_data_for_commits('closes https://app.asana.com/19292/956299/42') + d1 = double('Asana::Task') expect(d1).to receive(:add_comment) expect(d1).to receive(:update).with(completed: true) expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d1) - @asana.check_commit('closes https://app.asana.com/19292/956299/42', 'pushed') + @asana.execute(data) end it 'should allow multiple matches per line' do - d1 = double('Asana::Task', add_comment: true) + message = <<-EOF + minor bigfix, refactoring, fixed #123 and Closes #456 work on #789 + ref https://app.asana.com/19292/956299/42 and closing https://app.asana.com/19292/956299/12 + EOF + data = create_data_for_commits(message) + d1 = double('Asana::Task') expect(d1).to receive(:add_comment) expect(d1).to receive(:update).with(completed: true) expect(Asana::Task).to receive(:find_by_id).with(anything, '123').once.and_return(d1) - d2 = double('Asana::Task', add_comment: true) + d2 = double('Asana::Task') expect(d2).to receive(:add_comment) expect(d2).to receive(:update).with(completed: true) expect(Asana::Task).to receive(:find_by_id).with(anything, '456').once.and_return(d2) - d3 = double('Asana::Task', add_comment: true) + d3 = double('Asana::Task') expect(d3).to receive(:add_comment) expect(Asana::Task).to receive(:find_by_id).with(anything, '789').once.and_return(d3) - d4 = double('Asana::Task', add_comment: true) + d4 = double('Asana::Task') expect(d4).to receive(:add_comment) expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d4) - d5 = double('Asana::Task', add_comment: true) + d5 = double('Asana::Task') expect(d5).to receive(:add_comment) expect(d5).to receive(:update).with(completed: true) expect(Asana::Task).to receive(:find_by_id).with(anything, '12').once.and_return(d5) - message = <<-EOF - minor bigfix, refactoring, fixed #123 and Closes #456 work on #789 - ref https://app.asana.com/19292/956299/42 and closing https://app.asana.com/19292/956299/12 - EOF - @asana.check_commit(message, 'pushed') + @asana.execute(data) end end end -- cgit v1.2.1 From bf249550d03a21d4aca3847b6d32b6d71de3956b Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Dec 2015 22:03:43 -0500 Subject: number_with_delimiter most of the things --- app/views/admin/builds/index.html.haml | 6 +++--- app/views/admin/dashboard/index.html.haml | 22 +++++++++++----------- app/views/admin/groups/index.html.haml | 2 +- app/views/admin/users/index.html.haml | 14 +++++++------- app/views/layouts/nav/_admin.html.haml | 6 +++--- app/views/layouts/nav/_dashboard.html.haml | 4 ++-- app/views/layouts/nav/_group.html.haml | 4 ++-- app/views/layouts/nav/_profile.html.haml | 4 ++-- app/views/layouts/nav/_project.html.haml | 6 +++--- app/views/projects/issues/_issues.html.haml | 2 +- .../merge_requests/_merge_requests.html.haml | 2 +- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index 55da06a7fe9..52c36af6225 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -8,17 +8,17 @@ %li{class: ('active' if @scope.nil?)} = link_to admin_builds_path do Running - %span.badge.js-running-count= @all_builds.running_or_pending.count(:id) + %span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id)) %li{class: ('active' if @scope == 'finished')} = link_to admin_builds_path(scope: :finished) do Finished - %span.badge.js-running-count= @all_builds.finished.count(:id) + %span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id)) %li{class: ('active' if @scope == 'all')} = link_to admin_builds_path(scope: :all) do All - %span.badge.js-totalbuilds-count= @all_builds.count(:id) + %span.badge.js-totalbuilds-count= number_with_delimiter(@all_builds.count(:id)) .gray-content-block #{(@scope || 'running').capitalize} builds diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 531247e9148..cc389c3ae08 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -6,35 +6,35 @@ %p Forks %span.light.pull-right - = ForkedProjectLink.count + = number_with_delimiter(ForkedProjectLink.count) %p Issues %span.light.pull-right - = Issue.count + = number_with_delimiter(Issue.count) %p Merge Requests %span.light.pull-right - = MergeRequest.count + = number_with_delimiter(MergeRequest.count) %p Notes %span.light.pull-right - = Note.count + = number_with_delimiter(Note.count) %p Snippets %span.light.pull-right - = Snippet.count + = number_with_delimiter(Snippet.count) %p SSH Keys %span.light.pull-right - = Key.count + = number_with_delimiter(Key.count) %p Milestones %span.light.pull-right - = Milestone.count + = number_with_delimiter(Milestone.count) %p Active Users %span.light.pull-right - = User.active.count + = number_with_delimiter(User.active.count) .col-md-4 %h4 Features @@ -99,7 +99,7 @@ %h4 Projects .data = link_to admin_namespaces_projects_path do - %h1= Project.count + %h1= number_with_delimiter(Project.count) %hr = link_to('New Project', new_project_path, class: "btn btn-new") .col-sm-4 @@ -107,7 +107,7 @@ %h4 Users .data = link_to admin_users_path do - %h1= User.count + %h1= number_with_delimiter(User.count) %hr = link_to 'New User', new_admin_user_path, class: "btn btn-new" .col-sm-4 @@ -115,7 +115,7 @@ %h4 Groups .data = link_to admin_groups_path do - %h1= Group.count + %h1= number_with_delimiter(Group.count) %hr = link_to 'New Group', new_admin_group_path, class: "btn btn-new" diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml index 5ce7cdf2f8d..3940210e19b 100644 --- a/app/views/admin/groups/index.html.haml +++ b/app/views/admin/groups/index.html.haml @@ -1,6 +1,6 @@ - page_title "Groups" %h3.page-title - Groups (#{@groups.total_count}) + Groups (#{number_with_delimiter(@groups.total_count)}) = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" %p.light diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index bc08458312c..a92c9c152b9 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -8,27 +8,27 @@ %li{class: "#{'active' unless params[:filter]}"} = link_to admin_users_path do Active - %small.pull-right= User.active.count + %small.pull-right= number_with_delimiter(User.active.count) %li{class: "#{'active' if params[:filter] == "admins"}"} = link_to admin_users_path(filter: "admins") do Admins - %small.pull-right= User.admins.count + %small.pull-right= number_with_delimiter(User.admins.count) %li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"} = link_to admin_users_path(filter: 'two_factor_enabled') do 2FA Enabled - %small.pull-right= User.with_two_factor.count + %small.pull-right= number_with_delimiter(User.with_two_factor.count) %li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"} = link_to admin_users_path(filter: 'two_factor_disabled') do 2FA Disabled - %small.pull-right= User.without_two_factor.count + %small.pull-right= number_with_delimiter(User.without_two_factor.count) %li{class: "#{'active' if params[:filter] == "blocked"}"} = link_to admin_users_path(filter: "blocked") do Blocked - %small.pull-right= User.blocked.count + %small.pull-right= number_with_delimiter(User.blocked.count) %li{class: "#{'active' if params[:filter] == "wop"}"} = link_to admin_users_path(filter: "wop") do Without projects - %small.pull-right= User.without_projects.count + %small.pull-right= number_with_delimiter(User.without_projects.count) %hr = form_tag admin_users_path, method: :get, class: 'form-inline' do .form-group @@ -42,7 +42,7 @@ %section.col-md-9 .panel.panel-default .panel-heading - Users (#{@users.total_count}) + Users (#{number_with_delimiter(@users.total_count)}) .panel-head-actions .dropdown.inline %a.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"} diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index c60ac5eefac..cffdb52cc23 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -29,13 +29,13 @@ = icon('cog fw') %span Runners - %span.count= Ci::Runner.count(:all) + %span.count= number_with_delimiter(Ci::Runner.count(:all)) = nav_link path: 'builds#index' do = link_to admin_builds_path do = icon('link fw') %span Builds - %span.count= Ci::Build.count(:all) + %span.count= number_with_delimiter(Ci::Build.count(:all)) = nav_link(controller: :logs) do = link_to admin_logs_path, title: 'Logs' do = icon('file-text fw') @@ -80,7 +80,7 @@ = icon('exclamation-circle fw') %span Abuse Reports - %span.count= AbuseReport.count(:all) + %span.count= number_with_delimiter(AbuseReport.count(:all)) = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do = link_to admin_application_settings_path, title: 'Settings' do diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index da698831300..106abd24a56 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -24,13 +24,13 @@ = icon('exclamation-circle fw') %span Issues - %span.count= current_user.assigned_issues.opened.count + %span.count= number_with_delimiter(current_user.assigned_issues.opened.count) = nav_link(path: 'dashboard#merge_requests') do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do = icon('tasks fw') %span Merge Requests - %span.count= current_user.assigned_merge_requests.opened.count + %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) = nav_link(controller: :snippets) do = link_to dashboard_snippets_path, title: 'Snippets' do = icon('clipboard fw') diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 68da8d5de2a..e5e2a59eaed 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -25,14 +25,14 @@ %span Issues - if current_user - %span.count= Issue.opened.of_group(@group).count + %span.count= number_with_delimiter(Issue.opened.of_group(@group).count) = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group), title: 'Merge Requests' do = icon('tasks fw') %span Merge Requests - if current_user - %span.count= MergeRequest.opened.of_group(@group).count + %span.count= number_with_delimiter(MergeRequest.opened.of_group(@group).count) = nav_link(controller: [:group_members]) do = link_to group_group_members_path(@group), title: 'Members' do = icon('users fw') diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 64b30783c05..f3ded04419b 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -27,7 +27,7 @@ = icon('envelope-o fw') %span Emails - %span.count= current_user.emails.count + 1 + %span.count= number_with_delimiter(current_user.emails.count + 1) - unless current_user.ldap_user? = nav_link(controller: :passwords) do = link_to edit_profile_password_path, title: 'Password' do @@ -45,7 +45,7 @@ = icon('key fw') %span SSH Keys - %span.count= current_user.keys.count + %span.count= number_with_delimiter(current_user.keys.count) = nav_link(controller: :preferences) do = link_to profile_preferences_path, title: 'Preferences' do -# TODO (rspeicher): Better icon? diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index c0d62028639..d3eaf0f3209 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -44,7 +44,7 @@ = icon('cubes fw') %span Builds - %span.count.builds_counter= @project.builds.running_or_pending.count(:all) + %span.count.builds_counter= number_with_delimiter(@project.builds.running_or_pending.count(:all)) - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do @@ -67,7 +67,7 @@ %span Issues - if @project.default_issues_tracker? - %span.count.issue_counter= @project.issues.opened.count + %span.count.issue_counter= number_with_delimiter(@project.issues.opened.count) - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do @@ -75,7 +75,7 @@ = icon('tasks fw') %span Merge Requests - %span.count.merge_counter= @project.merge_requests.opened.count + %span.count.merge_counter= number_with_delimiter(@project.merge_requests.opened.count) - if project_nav_tab? :settings = nav_link(controller: [:project_members, :teams]) do diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index ca5b1a8386d..e0e89b764d5 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -7,7 +7,7 @@ - if @issues.present? .issuable-filter-count %span.pull-right - = @issues.total_count + = number_with_delimiter(@issues.total_count) issues for this filter = paginate @issues, theme: "gitlab" diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml index 0af970e4b92..29d09d0a652 100644 --- a/app/views/projects/merge_requests/_merge_requests.html.haml +++ b/app/views/projects/merge_requests/_merge_requests.html.haml @@ -7,7 +7,7 @@ - if @merge_requests.present? .issuable-filter-count %span.pull-right - = @merge_requests.total_count + = number_with_delimiter(@merge_requests.total_count) merge requests for this filter = paginate @merge_requests, theme: "gitlab" -- cgit v1.2.1 From 9a9a81bea0bf4e68b3e1ed7ec619947a37d2dbff Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 31 Dec 2015 20:42:46 -0500 Subject: Don't attempt to set Referrer policy in Safari While Safari supports the policy, it does not (currently, as of 9.x) recognize `origin-when-cross-origin` as a valid value, so we omit the policy entirely under Safari. Closes #5609 --- CHANGELOG | 1 + app/views/layouts/_head.html.haml | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ea403642e32..2b7d5808e7e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.4.0 (unreleased) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) + - Fix version check image in Safari v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 2e0bd2007a3..dd133ee8b56 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,9 +1,6 @@ %head{prefix: "og: http://ogp.me/ns#"} %meta{charset: "utf-8"} %meta{'http-equiv' => 'X-UA-Compatible', content: 'IE=edge'} - %meta{name: 'referrer', content: 'origin-when-cross-origin'} - - %meta{name: "description", content: page_description} -# Open Graph - http://ogp.me/ %meta{property: 'og:type', content: "object"} @@ -20,8 +17,8 @@ %meta{property: 'twitter:image', content: page_image} = page_card_meta_tags - - page_title "GitLab" - %title= page_title + %title= page_title('GitLab') + %meta{name: "description", content: page_description} = favicon_link_tag 'favicon.ico' @@ -34,6 +31,8 @@ = include_gon + - unless browser.safari? + %meta{name: 'referrer', content: 'origin-when-cross-origin'} %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'} %meta{name: 'theme-color', content: '#474D57'} -- cgit v1.2.1 From 74969021efed0ae15f19fef5bfc8e9ed7d7fa4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20K=C3=B6hler?= Date: Fri, 1 Jan 2016 13:32:24 +0100 Subject: Fix typo in CI settings [ci skip] --- app/views/admin/application_settings/_form.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 214e0209bb7..89b38a0dad0 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -149,7 +149,7 @@ .checkbox = f.label :shared_runners_enabled do = f.check_box :shared_runners_enabled - Enable shared runners for a new projects + Enable shared runners for new projects .form-group = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2' -- cgit v1.2.1 From 26de72a233c0b20937c4d86baab69946d385e147 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Fri, 1 Jan 2016 13:52:26 +0100 Subject: Fix broken SVN migration link [ci skip] --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index f40a257b42f..78644505df5 100644 --- a/doc/README.md +++ b/doc/README.md @@ -7,7 +7,7 @@ - [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab. - [Importing to GitLab](workflow/importing/README.md). - [Markdown](markdown/markdown.md) GitLab's advanced formatting system. -- [Migrating from SVN](migration/README.md) Convert a SVN repository to Git and GitLab +- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab - [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do. - [Profile Settings](profile/README.md) - [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat. -- cgit v1.2.1 From f3ea2f29a5b18f176ed0bbfde55e9cc7d429ebe6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 17:40:10 -0500 Subject: Remove jquery.history.js plugin We're not concerning ourselves with non-HTML5 browser compatibility, and this removes 21 KB from our compiled JavaScript. Bonus fix: There was an extra space after the query string in the URLs that has now been removed. --- app/assets/javascripts/application.js.coffee | 1 - app/assets/javascripts/issues.js.coffee | 4 ++-- app/assets/javascripts/merge_requests.js.coffee | 4 ++-- vendor/assets/javascripts/jquery.history.js | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.history.js diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index affab5bb030..fe76d11ab58 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -10,7 +10,6 @@ #= require jquery.cookie #= require jquery.endless-scroll #= require jquery.highlight -#= require jquery.history #= require jquery.waitforimages #= require jquery.atwho #= require jquery.scrollTo diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index ac9e022e727..35d34b20fae 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -54,7 +54,7 @@ form = $("#issue_search_form") search = $("#issue_search").val() $('.issues-holder').css("opacity", '0.5') - issues_url = form.attr('action') + '? '+ form.serialize() + issues_url = form.attr('action') + '?' + form.serialize() $.ajax type: "GET" @@ -65,7 +65,7 @@ success: (data) -> $('.issues-holder').html(data.html) # Change url so if user reload a page - search results are saved - History.replaceState {page: issues_url}, document.title, issues_url + history.replaceState {page: issues_url}, document.title, issues_url Issues.reload() dataType: "json" diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee index 83434c1b9ba..b3c73ffce5d 100644 --- a/app/assets/javascripts/merge_requests.js.coffee +++ b/app/assets/javascripts/merge_requests.js.coffee @@ -16,7 +16,7 @@ form = $("#issue_search_form") search = $("#issue_search").val() $('.merge-requests-holder').css("opacity", '0.5') - issues_url = form.attr('action') + '? '+ form.serialize() + issues_url = form.attr('action') + '?' + form.serialize() $.ajax type: "GET" @@ -27,7 +27,7 @@ success: (data) -> $('.merge-requests-holder').html(data.html) # Change url so if user reload a page - search results are saved - History.replaceState {page: issues_url}, document.title, issues_url + history.replaceState {page: issues_url}, document.title, issues_url MergeRequests.reload() dataType: "json" diff --git a/vendor/assets/javascripts/jquery.history.js b/vendor/assets/javascripts/jquery.history.js deleted file mode 100644 index 8d4edcd210e..00000000000 --- a/vendor/assets/javascripts/jquery.history.js +++ /dev/null @@ -1 +0,0 @@ -window.JSON||(window.JSON={}),function(){function f(a){return a<10?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c")&&c[0]);return a>4?a:!1}();return a},m.isInternetExplorer=function(){var a=m.isInternetExplorer.cached=typeof m.isInternetExplorer.cached!="undefined"?m.isInternetExplorer.cached:Boolean(m.getInternetExplorerMajorVersion());return a},m.emulated={pushState:!Boolean(a.history&&a.history.pushState&&a.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(e.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(e.userAgent)),hashChange:Boolean(!("onhashchange"in a||"onhashchange"in d)||m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8)},m.enabled=!m.emulated.pushState,m.bugs={setHash:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),safariPoll:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),ieDoubleCheck:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<7)},m.isEmptyObject=function(a){for(var b in a)return!1;return!0},m.cloneObject=function(a){var b,c;return a?(b=k.stringify(a),c=k.parse(b)):c={},c},m.getRootUrl=function(){var a=d.location.protocol+"//"+(d.location.hostname||d.location.host);if(d.location.port||!1)a+=":"+d.location.port;return a+="/",a},m.getBaseHref=function(){var a=d.getElementsByTagName("base"),b=null,c="";return a.length===1&&(b=a[0],c=b.href.replace(/[^\/]+$/,"")),c=c.replace(/\/+$/,""),c&&(c+="/"),c},m.getBaseUrl=function(){var a=m.getBaseHref()||m.getBasePageUrl()||m.getRootUrl();return a},m.getPageUrl=function(){var a=m.getState(!1,!1),b=(a||{}).url||d.location.href,c;return c=b.replace(/\/+$/,"").replace(/[^\/]+$/,function(a,b,c){return/\./.test(a)?a:a+"/"}),c},m.getBasePageUrl=function(){var a=d.location.href.replace(/[#\?].*/,"").replace(/[^\/]+$/,function(a,b,c){return/[^\/]$/.test(a)?"":a}).replace(/\/+$/,"")+"/";return a},m.getFullUrl=function(a,b){var c=a,d=a.substring(0,1);return b=typeof b=="undefined"?!0:b,/[a-z]+\:\/\//.test(a)||(d==="/"?c=m.getRootUrl()+a.replace(/^\/+/,""):d==="#"?c=m.getPageUrl().replace(/#.*/,"")+a:d==="?"?c=m.getPageUrl().replace(/[\?#].*/,"")+a:b?c=m.getBaseUrl()+a.replace(/^(\.\/)+/,""):c=m.getBasePageUrl()+a.replace(/^(\.\/)+/,"")),c.replace(/\#$/,"")},m.getShortUrl=function(a){var b=a,c=m.getBaseUrl(),d=m.getRootUrl();return m.emulated.pushState&&(b=b.replace(c,"")),b=b.replace(d,"/"),m.isTraditionalAnchor(b)&&(b="./"+b),b=b.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),b},m.store={},m.idToState=m.idToState||{},m.stateToId=m.stateToId||{},m.urlToId=m.urlToId||{},m.storedStates=m.storedStates||[],m.savedStates=m.savedStates||[],m.normalizeStore=function(){m.store.idToState=m.store.idToState||{},m.store.urlToId=m.store.urlToId||{},m.store.stateToId=m.store.stateToId||{}},m.getState=function(a,b){typeof a=="undefined"&&(a=!0),typeof b=="undefined"&&(b=!0);var c=m.getLastSavedState();return!c&&b&&(c=m.createStateObject()),a&&(c=m.cloneObject(c),c.url=c.cleanUrl||c.url),c},m.getIdByState=function(a){var b=m.extractId(a.url),c;if(!b){c=m.getStateString(a);if(typeof m.stateToId[c]!="undefined")b=m.stateToId[c];else if(typeof m.store.stateToId[c]!="undefined")b=m.store.stateToId[c];else{for(;;){b=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof m.idToState[b]=="undefined"&&typeof m.store.idToState[b]=="undefined")break}m.stateToId[c]=b,m.idToState[b]=a}}return b},m.normalizeState=function(a){var b,c;if(!a||typeof a!="object")a={};if(typeof a.normalized!="undefined")return a;if(!a.data||typeof a.data!="object")a.data={};b={},b.normalized=!0,b.title=a.title||"",b.url=m.getFullUrl(m.unescapeString(a.url||d.location.href)),b.hash=m.getShortUrl(b.url),b.data=m.cloneObject(a.data),b.id=m.getIdByState(b),b.cleanUrl=b.url.replace(/\??\&_suid.*/,""),b.url=b.cleanUrl,c=!m.isEmptyObject(b.data);if(b.title||c)b.hash=m.getShortUrl(b.url).replace(/\??\&_suid.*/,""),/\?/.test(b.hash)||(b.hash+="?"),b.hash+="&_suid="+b.id;return b.hashedUrl=m.getFullUrl(b.hash),(m.emulated.pushState||m.bugs.safariPoll)&&m.hasUrlDuplicate(b)&&(b.url=b.hashedUrl),b},m.createStateObject=function(a,b,c){var d={data:a,title:b,url:c};return d=m.normalizeState(d),d},m.getStateById=function(a){a=String(a);var c=m.idToState[a]||m.store.idToState[a]||b;return c},m.getStateString=function(a){var b,c,d;return b=m.normalizeState(a),c={data:b.data,title:a.title,url:a.url},d=k.stringify(c),d},m.getStateId=function(a){var b,c;return b=m.normalizeState(a),c=b.id,c},m.getHashByState=function(a){var b,c;return b=m.normalizeState(a),c=b.hash,c},m.extractId=function(a){var b,c,d;return c=/(.*)\&_suid=([0-9]+)$/.exec(a),d=c?c[1]||a:a,b=c?String(c[2]||""):"",b||!1},m.isTraditionalAnchor=function(a){var b=!/[\/\?\.]/.test(a);return b},m.extractState=function(a,b){var c=null,d,e;return b=b||!1,d=m.extractId(a),d&&(c=m.getStateById(d)),c||(e=m.getFullUrl(a),d=m.getIdByUrl(e)||!1,d&&(c=m.getStateById(d)),!c&&b&&!m.isTraditionalAnchor(a)&&(c=m.createStateObject(null,null,e))),c},m.getIdByUrl=function(a){var c=m.urlToId[a]||m.store.urlToId[a]||b;return c},m.getLastSavedState=function(){return m.savedStates[m.savedStates.length-1]||b},m.getLastStoredState=function(){return m.storedStates[m.storedStates.length-1]||b},m.hasUrlDuplicate=function(a){var b=!1,c;return c=m.extractState(a.url),b=c&&c.id!==a.id,b},m.storeState=function(a){return m.urlToId[a.url]=a.id,m.storedStates.push(m.cloneObject(a)),a},m.isLastSavedState=function(a){var b=!1,c,d,e;return m.savedStates.length&&(c=a.id,d=m.getLastSavedState(),e=d.id,b=c===e),b},m.saveState=function(a){return m.isLastSavedState(a)?!1:(m.savedStates.push(m.cloneObject(a)),!0)},m.getStateByIndex=function(a){var b=null;return typeof a=="undefined"?b=m.savedStates[m.savedStates.length-1]:a<0?b=m.savedStates[m.savedStates.length+a]:b=m.savedStates[a],b},m.getHash=function(){var a=m.unescapeHash(d.location.hash);return a},m.unescapeString=function(b){var c=b,d;for(;;){d=a.unescape(c);if(d===c)break;c=d}return c},m.unescapeHash=function(a){var b=m.normalizeHash(a);return b=m.unescapeString(b),b},m.normalizeHash=function(a){var b=a.replace(/[^#]*#/,"").replace(/#.*/,"");return b},m.setHash=function(a,b){var c,e,f;return b!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.setHash,args:arguments,queue:b}),!1):(c=m.escapeHash(a),m.busy(!0),e=m.extractState(a,!0),e&&!m.emulated.pushState?m.pushState(e.data,e.title,e.url,!1):d.location.hash!==c&&(m.bugs.setHash?(f=m.getPageUrl(),m.pushState(null,null,f+"#"+c,!1)):d.location.hash=c),m)},m.escapeHash=function(b){var c=m.normalizeHash(b);return c=a.escape(c),m.bugs.hashEscape||(c=c.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),c},m.getHashByUrl=function(a){var b=String(a).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return b=m.unescapeHash(b),b},m.setTitle=function(a){var b=a.title,c;b||(c=m.getStateByIndex(0),c&&c.url===a.url&&(b=c.title||m.options.initialTitle));try{d.getElementsByTagName("title")[0].innerHTML=b.replace("<","<").replace(">",">").replace(" & "," & ")}catch(e){}return d.title=b,m},m.queues=[],m.busy=function(a){typeof a!="undefined"?m.busy.flag=a:typeof m.busy.flag=="undefined"&&(m.busy.flag=!1);if(!m.busy.flag){h(m.busy.timeout);var b=function(){var a,c,d;if(m.busy.flag)return;for(a=m.queues.length-1;a>=0;--a){c=m.queues[a];if(c.length===0)continue;d=c.shift(),m.fireQueueItem(d),m.busy.timeout=g(b,m.options.busyDelay)}};m.busy.timeout=g(b,m.options.busyDelay)}return m.busy.flag},m.busy.flag=!1,m.fireQueueItem=function(a){return a.callback.apply(a.scope||m,a.args||[])},m.pushQueue=function(a){return m.queues[a.queue||0]=m.queues[a.queue||0]||[],m.queues[a.queue||0].push(a),m},m.queue=function(a,b){return typeof a=="function"&&(a={callback:a}),typeof b!="undefined"&&(a.queue=b),m.busy()?m.pushQueue(a):m.fireQueueItem(a),m},m.clearQueue=function(){return m.busy.flag=!1,m.queues=[],m},m.stateChanged=!1,m.doubleChecker=!1,m.doubleCheckComplete=function(){return m.stateChanged=!0,m.doubleCheckClear(),m},m.doubleCheckClear=function(){return m.doubleChecker&&(h(m.doubleChecker),m.doubleChecker=!1),m},m.doubleCheck=function(a){return m.stateChanged=!1,m.doubleCheckClear(),m.bugs.ieDoubleCheck&&(m.doubleChecker=g(function(){return m.doubleCheckClear(),m.stateChanged||a(),!0},m.options.doubleCheckInterval)),m},m.safariStatePoll=function(){var b=m.extractState(d.location.href),c;if(!m.isLastSavedState(b))c=b;else return;return c||(c=m.createStateObject()),m.Adapter.trigger(a,"popstate"),m},m.back=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.back,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.back(!1)}),n.go(-1),!0)},m.forward=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.forward,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.forward(!1)}),n.go(1),!0)},m.go=function(a,b){var c;if(a>0)for(c=1;c<=a;++c)m.forward(b);else{if(!(a<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(c=-1;c>=a;--c)m.back(b)}return m};if(m.emulated.pushState){var o=function(){};m.pushState=m.pushState||o,m.replaceState=m.replaceState||o}else m.onPopState=function(b,c){var e=!1,f=!1,g,h;return m.doubleCheckComplete(),g=m.getHash(),g?(h=m.extractState(g||d.location.href,!0),h?m.replaceState(h.data,h.title,h.url,!1):(m.Adapter.trigger(a,"anchorchange"),m.busy(!1)),m.expectedStateId=!1,!1):(e=m.Adapter.extractEventData("state",b,c)||!1,e?f=m.getStateById(e):m.expectedStateId?f=m.getStateById(m.expectedStateId):f=m.extractState(d.location.href),f||(f=m.createStateObject(null,null,d.location.href)),m.expectedStateId=!1,m.isLastSavedState(f)?(m.busy(!1),!1):(m.storeState(f),m.saveState(f),m.setTitle(f),m.Adapter.trigger(a,"statechange"),m.busy(!1),!0))},m.Adapter.bind(a,"popstate",m.onPopState),m.pushState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.pushState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.pushState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0},m.replaceState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.replaceState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.replaceState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0};if(f){try{m.store=k.parse(f.getItem("History.store"))||{}}catch(p){m.store={}}m.normalizeStore()}else m.store={},m.normalizeStore();m.Adapter.bind(a,"beforeunload",m.clearAllIntervals),m.Adapter.bind(a,"unload",m.clearAllIntervals),m.saveState(m.storeState(m.extractState(d.location.href,!0))),f&&(m.onUnload=function(){var a,b;try{a=k.parse(f.getItem("History.store"))||{}}catch(c){a={}}a.idToState=a.idToState||{},a.urlToId=a.urlToId||{},a.stateToId=a.stateToId||{};for(b in m.idToState){if(!m.idToState.hasOwnProperty(b))continue;a.idToState[b]=m.idToState[b]}for(b in m.urlToId){if(!m.urlToId.hasOwnProperty(b))continue;a.urlToId[b]=m.urlToId[b]}for(b in m.stateToId){if(!m.stateToId.hasOwnProperty(b))continue;a.stateToId[b]=m.stateToId[b]}m.store=a,m.normalizeStore(),f.setItem("History.store",k.stringify(a))},m.intervalList.push(i(m.onUnload,m.options.storeInterval)),m.Adapter.bind(a,"beforeunload",m.onUnload),m.Adapter.bind(a,"unload",m.onUnload));if(!m.emulated.pushState){m.bugs.safariPoll&&m.intervalList.push(i(m.safariStatePoll,m.options.safariPollInterval));if(e.vendor==="Apple Computer, Inc."||(e.appCodeName||"")==="Mozilla")m.Adapter.bind(a,"hashchange",function(){m.Adapter.trigger(a,"popstate")}),m.getHash()&&m.Adapter.onDomLoad(function(){m.Adapter.trigger(a,"hashchange")})}},m.init()}(window) \ No newline at end of file -- cgit v1.2.1 From 3c4d24b71ed08310c3704547601ff3b9ebaceb03 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 17:52:40 -0500 Subject: Remove jquery.blockUI.js plugin It was required but never used. --- app/assets/javascripts/application.js.coffee | 1 - vendor/assets/javascripts/jquery.blockUI.js | 590 --------------------------- 2 files changed, 591 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.blockUI.js diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index affab5bb030..65ca1367724 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -14,7 +14,6 @@ #= require jquery.waitforimages #= require jquery.atwho #= require jquery.scrollTo -#= require jquery.blockUI #= require jquery.turbolinks #= require turbolinks #= require autosave diff --git a/vendor/assets/javascripts/jquery.blockUI.js b/vendor/assets/javascripts/jquery.blockUI.js deleted file mode 100644 index c8702d79b65..00000000000 --- a/vendor/assets/javascripts/jquery.blockUI.js +++ /dev/null @@ -1,590 +0,0 @@ -/*! - * jQuery blockUI plugin - * Version 2.60.0-2013.04.05 - * @requires jQuery v1.7 or later - * - * Examples at: http://malsup.com/jquery/block/ - * Copyright (c) 2007-2013 M. Alsup - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - * Thanks to Amir-Hossein Sobhi for some excellent contributions! - */ - -;(function() { -/*jshint eqeqeq:false curly:false latedef:false */ -"use strict"; - - function setup($) { - $.fn._fadeIn = $.fn.fadeIn; - - var noOp = $.noop || function() {}; - - // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle - // retarded userAgent strings on Vista) - var msie = /MSIE/.test(navigator.userAgent); - var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent); - var mode = document.documentMode || 0; - var setExpr = $.isFunction( document.createElement('div').style.setExpression ); - - // global $ methods for blocking/unblocking the entire page - $.blockUI = function(opts) { install(window, opts); }; - $.unblockUI = function(opts) { remove(window, opts); }; - - // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) - $.growlUI = function(title, message, timeout, onClose) { - var $m = $('
'); - if (title) $m.append('

'+title+'

'); - if (message) $m.append('

'+message+'

'); - if (timeout === undefined) timeout = 3000; - $.blockUI({ - message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, - timeout: timeout, showOverlay: false, - onUnblock: onClose, - css: $.blockUI.defaults.growlCSS - }); - }; - - // plugin method for blocking element content - $.fn.block = function(opts) { - if ( this[0] === window ) { - $.blockUI( opts ); - return this; - } - var fullOpts = $.extend({}, $.blockUI.defaults, opts || {}); - this.each(function() { - var $el = $(this); - if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked')) - return; - $el.unblock({ fadeOut: 0 }); - }); - - return this.each(function() { - if ($.css(this,'position') == 'static') { - this.style.position = 'relative'; - $(this).data('blockUI.static', true); - } - this.style.zoom = 1; // force 'hasLayout' in ie - install(this, opts); - }); - }; - - // plugin method for unblocking element content - $.fn.unblock = function(opts) { - if ( this[0] === window ) { - $.unblockUI( opts ); - return this; - } - return this.each(function() { - remove(this, opts); - }); - }; - - $.blockUI.version = 2.60; // 2nd generation blocking at no extra cost! - - // override these in your code to change the default behavior and style - $.blockUI.defaults = { - // message displayed when blocking (use null for no message) - message: '

Please wait...

', - - title: null, // title string; only used when theme == true - draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) - - theme: false, // set to true to use with jQuery UI themes - - // styles for the message when blocking; if you wish to disable - // these and use an external stylesheet then do this in your code: - // $.blockUI.defaults.css = {}; - css: { - padding: 0, - margin: 0, - width: '30%', - top: '40%', - left: '35%', - textAlign: 'center', - color: '#000', - border: '3px solid #aaa', - backgroundColor:'#fff', - cursor: 'wait' - }, - - // minimal style set used when themes are used - themedCSS: { - width: '30%', - top: '40%', - left: '35%' - }, - - // styles for the overlay - overlayCSS: { - backgroundColor: '#000', - opacity: 0.6, - cursor: 'wait' - }, - - // style to replace wait cursor before unblocking to correct issue - // of lingering wait cursor - cursorReset: 'default', - - // styles applied when using $.growlUI - growlCSS: { - width: '350px', - top: '10px', - left: '', - right: '10px', - border: 'none', - padding: '5px', - opacity: 0.6, - cursor: 'default', - color: '#fff', - backgroundColor: '#000', - '-webkit-border-radius':'10px', - '-moz-border-radius': '10px', - 'border-radius': '10px' - }, - - // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w - // (hat tip to Jorge H. N. de Vasconcelos) - /*jshint scripturl:true */ - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', - - // force usage of iframe in non-IE browsers (handy for blocking applets) - forceIframe: false, - - // z-index for the blocking overlay - baseZ: 1000, - - // set these to true to have the message automatically centered - centerX: true, // <-- only effects element blocking (page block controlled via css above) - centerY: true, - - // allow body element to be stetched in ie6; this makes blocking look better - // on "short" pages. disable if you wish to prevent changes to the body height - allowBodyStretch: true, - - // enable if you want key and mouse events to be disabled for content that is blocked - bindEvents: true, - - // be default blockUI will supress tab navigation from leaving blocking content - // (if bindEvents is true) - constrainTabKey: true, - - // fadeIn time in millis; set to 0 to disable fadeIn on block - fadeIn: 200, - - // fadeOut time in millis; set to 0 to disable fadeOut on unblock - fadeOut: 400, - - // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock - timeout: 0, - - // disable if you don't want to show the overlay - showOverlay: true, - - // if true, focus will be placed in the first available input field when - // page blocking - focusInput: true, - - // elements that can receive focus - focusableElements: ':input:enabled:visible', - - // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) - // no longer needed in 2012 - // applyPlatformOpacityRules: true, - - // callback method invoked when fadeIn has completed and blocking message is visible - onBlock: null, - - // callback method invoked when unblocking has completed; the callback is - // passed the element that has been unblocked (which is the window object for page - // blocks) and the options that were passed to the unblock call: - // onUnblock(element, options) - onUnblock: null, - - // callback method invoked when the overlay area is clicked. - // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used. - onOverlayClick: null, - - // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 - quirksmodeOffsetHack: 4, - - // class name of the message block - blockMsgClass: 'blockMsg', - - // if it is already blocked, then ignore it (don't unblock and reblock) - ignoreIfBlocked: false - }; - - // private data and functions follow... - - var pageBlock = null; - var pageBlockEls = []; - - function install(el, opts) { - var css, themedCSS; - var full = (el == window); - var msg = (opts && opts.message !== undefined ? opts.message : undefined); - opts = $.extend({}, $.blockUI.defaults, opts || {}); - - if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked')) - return; - - opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); - css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); - if (opts.onOverlayClick) - opts.overlayCSS.cursor = 'pointer'; - - themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); - msg = msg === undefined ? opts.message : msg; - - // remove the current block (if there is one) - if (full && pageBlock) - remove(window, {fadeOut:0}); - - // if an existing element is being used as the blocking content then we capture - // its current place in the DOM (and current display style) so we can restore - // it when we unblock - if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { - var node = msg.jquery ? msg[0] : msg; - var data = {}; - $(el).data('blockUI.history', data); - data.el = node; - data.parent = node.parentNode; - data.display = node.style.display; - data.position = node.style.position; - if (data.parent) - data.parent.removeChild(node); - } - - $(el).data('blockUI.onUnblock', opts.onUnblock); - var z = opts.baseZ; - - // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; - // layer1 is the iframe layer which is used to supress bleed through of underlying content - // layer2 is the overlay layer which has opacity and a wait cursor (by default) - // layer3 is the message content that is displayed while blocking - var lyr1, lyr2, lyr3, s; - if (msie || opts.forceIframe) - lyr1 = $(''); - else - lyr1 = $(''); - - if (opts.theme) - lyr2 = $(''); - else - lyr2 = $(''); - - if (opts.theme && full) { - s = ''; - } - else if (opts.theme) { - s = ''; - } - else if (full) { - s = ''; - } - else { - s = ''; - } - lyr3 = $(s); - - // if we have a message, style it - if (msg) { - if (opts.theme) { - lyr3.css(themedCSS); - lyr3.addClass('ui-widget-content'); - } - else - lyr3.css(css); - } - - // style the overlay - if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/) - lyr2.css(opts.overlayCSS); - lyr2.css('position', full ? 'fixed' : 'absolute'); - - // make iframe layer transparent in IE - if (msie || opts.forceIframe) - lyr1.css('opacity',0.0); - - //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); - var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); - $.each(layers, function() { - this.appendTo($par); - }); - - if (opts.theme && opts.draggable && $.fn.draggable) { - lyr3.draggable({ - handle: '.ui-dialog-titlebar', - cancel: 'li' - }); - } - - // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) - var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0); - if (ie6 || expr) { - // give body 100% height - if (full && opts.allowBodyStretch && $.support.boxModel) - $('html,body').css('height','100%'); - - // fix ie6 issue when blocked element has a border width - if ((ie6 || !$.support.boxModel) && !full) { - var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); - var fixT = t ? '(0 - '+t+')' : 0; - var fixL = l ? '(0 - '+l+')' : 0; - } - - // simulate fixed position - $.each(layers, function(i,o) { - var s = o[0].style; - s.position = 'absolute'; - if (i < 2) { - if (full) - s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"'); - else - s.setExpression('height','this.parentNode.offsetHeight + "px"'); - if (full) - s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'); - else - s.setExpression('width','this.parentNode.offsetWidth + "px"'); - if (fixL) s.setExpression('left', fixL); - if (fixT) s.setExpression('top', fixT); - } - else if (opts.centerY) { - if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); - s.marginTop = 0; - } - else if (!opts.centerY && full) { - var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0; - var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; - s.setExpression('top',expression); - } - }); - } - - // show the message - if (msg) { - if (opts.theme) - lyr3.find('.ui-widget-content').append(msg); - else - lyr3.append(msg); - if (msg.jquery || msg.nodeType) - $(msg).show(); - } - - if ((msie || opts.forceIframe) && opts.showOverlay) - lyr1.show(); // opacity is zero - if (opts.fadeIn) { - var cb = opts.onBlock ? opts.onBlock : noOp; - var cb1 = (opts.showOverlay && !msg) ? cb : noOp; - var cb2 = msg ? cb : noOp; - if (opts.showOverlay) - lyr2._fadeIn(opts.fadeIn, cb1); - if (msg) - lyr3._fadeIn(opts.fadeIn, cb2); - } - else { - if (opts.showOverlay) - lyr2.show(); - if (msg) - lyr3.show(); - if (opts.onBlock) - opts.onBlock(); - } - - // bind key and mouse events - bind(1, el, opts); - - if (full) { - pageBlock = lyr3[0]; - pageBlockEls = $(opts.focusableElements,pageBlock); - if (opts.focusInput) - setTimeout(focus, 20); - } - else - center(lyr3[0], opts.centerX, opts.centerY); - - if (opts.timeout) { - // auto-unblock - var to = setTimeout(function() { - if (full) - $.unblockUI(opts); - else - $(el).unblock(opts); - }, opts.timeout); - $(el).data('blockUI.timeout', to); - } - } - - // remove the block - function remove(el, opts) { - var count; - var full = (el == window); - var $el = $(el); - var data = $el.data('blockUI.history'); - var to = $el.data('blockUI.timeout'); - if (to) { - clearTimeout(to); - $el.removeData('blockUI.timeout'); - } - opts = $.extend({}, $.blockUI.defaults, opts || {}); - bind(0, el, opts); // unbind events - - if (opts.onUnblock === null) { - opts.onUnblock = $el.data('blockUI.onUnblock'); - $el.removeData('blockUI.onUnblock'); - } - - var els; - if (full) // crazy selector to handle odd field errors in ie6/7 - els = $('body').children().filter('.blockUI').add('body > .blockUI'); - else - els = $el.find('>.blockUI'); - - // fix cursor issue - if ( opts.cursorReset ) { - if ( els.length > 1 ) - els[1].style.cursor = opts.cursorReset; - if ( els.length > 2 ) - els[2].style.cursor = opts.cursorReset; - } - - if (full) - pageBlock = pageBlockEls = null; - - if (opts.fadeOut) { - count = els.length; - els.fadeOut(opts.fadeOut, function() { - if ( --count === 0) - reset(els,data,opts,el); - }); - } - else - reset(els, data, opts, el); - } - - // move blocking element back into the DOM where it started - function reset(els,data,opts,el) { - var $el = $(el); - els.each(function(i,o) { - // remove via DOM calls so we don't lose event handlers - if (this.parentNode) - this.parentNode.removeChild(this); - }); - - if (data && data.el) { - data.el.style.display = data.display; - data.el.style.position = data.position; - if (data.parent) - data.parent.appendChild(data.el); - $el.removeData('blockUI.history'); - } - - if ($el.data('blockUI.static')) { - $el.css('position', 'static'); // #22 - } - - if (typeof opts.onUnblock == 'function') - opts.onUnblock(el,opts); - - // fix issue in Safari 6 where block artifacts remain until reflow - var body = $(document.body), w = body.width(), cssW = body[0].style.width; - body.width(w-1).width(w); - body[0].style.width = cssW; - } - - // bind/unbind the handler - function bind(b, el, opts) { - var full = el == window, $el = $(el); - - // don't bother unbinding if there is nothing to unbind - if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) - return; - - $el.data('blockUI.isBlocked', b); - - // don't bind events when overlay is not in use or if bindEvents is false - if (!full || !opts.bindEvents || (b && !opts.showOverlay)) - return; - - // bind anchors and inputs for mouse and key events - var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove'; - if (b) - $(document).bind(events, opts, handler); - else - $(document).unbind(events, handler); - - // former impl... - // var $e = $('a,:input'); - // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); - } - - // event handler to suppress keyboard/mouse events when blocking - function handler(e) { - // allow tab navigation (conditionally) - if (e.keyCode && e.keyCode == 9) { - if (pageBlock && e.data.constrainTabKey) { - var els = pageBlockEls; - var fwd = !e.shiftKey && e.target === els[els.length-1]; - var back = e.shiftKey && e.target === els[0]; - if (fwd || back) { - setTimeout(function(){focus(back);},10); - return false; - } - } - } - var opts = e.data; - var target = $(e.target); - if (target.hasClass('blockOverlay') && opts.onOverlayClick) - opts.onOverlayClick(); - - // allow events within the message content - if (target.parents('div.' + opts.blockMsgClass).length > 0) - return true; - - // allow events for content that is not being blocked - return target.parents().children().filter('div.blockUI').length === 0; - } - - function focus(back) { - if (!pageBlockEls) - return; - var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; - if (e) - e.focus(); - } - - function center(el, x, y) { - var p = el.parentNode, s = el.style; - var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); - var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); - if (x) s.left = l > 0 ? (l+'px') : '0'; - if (y) s.top = t > 0 ? (t+'px') : '0'; - } - - function sz(el, p) { - return parseInt($.css(el,p),10)||0; - } - - } - - - /*global define:true */ - if (typeof define === 'function' && define.amd && define.amd.jQuery) { - define(['jquery'], setup); - } else { - setup(jQuery); - } - -})(); -- cgit v1.2.1 From 93ef00850001962955a2f9c8f34a530a9cf8972f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 21:11:39 -0500 Subject: Bump bootstrap-sass to ~> 3.3.0 --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index 2a1c4f7d73a..f77b7d25bb2 100644 --- a/Gemfile +++ b/Gemfile @@ -200,7 +200,7 @@ gem 'turbolinks', '~> 2.5.0' gem 'jquery-turbolinks', '~> 2.1.0' gem 'addressable', '~> 2.3.8' -gem 'bootstrap-sass', '~> 3.0' +gem 'bootstrap-sass', '~> 3.3.0' gem 'font-awesome-rails', '~> 4.2' gem 'gitlab_emoji', '~> 0.2.0' gem 'gon', '~> 6.0.1' diff --git a/Gemfile.lock b/Gemfile.lock index 9769ae80a7d..3b977944eec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,7 +66,7 @@ GEM attr_encrypted (1.3.4) encryptor (>= 1.3.0) attr_required (1.0.0) - autoprefixer-rails (6.1.2) + autoprefixer-rails (6.2.3) execjs json awesome_print (1.2.0) @@ -82,9 +82,9 @@ GEM erubis (>= 2.6.6) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) - bootstrap-sass (3.3.5) - autoprefixer-rails (>= 5.0.0.1) - sass (>= 3.2.19) + bootstrap-sass (3.3.6) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) brakeman (3.1.4) erubis (~> 2.6) fastercsv (~> 1.5) @@ -843,7 +843,7 @@ DEPENDENCIES benchmark-ips better_errors (~> 1.0.1) binding_of_caller (~> 0.7.2) - bootstrap-sass (~> 3.0) + bootstrap-sass (~> 3.3.0) brakeman (~> 3.1.0) browser (~> 1.0.0) bullet -- cgit v1.2.1 From 762210db6d3a16307c032d84454b55474221e50c Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 21:29:21 -0500 Subject: Bump cal-heatmap-rails to ~> 3.5.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 2a1c4f7d73a..2418a02a451 100644 --- a/Gemfile +++ b/Gemfile @@ -172,7 +172,7 @@ gem 'ruby-fogbugz', '~> 0.2.1' gem 'd3_rails', '~> 3.5.5' #cal-heatmap -gem "cal-heatmap-rails", "~> 0.0.1" +gem 'cal-heatmap-rails', '~> 3.5.0' # underscore-rails gem "underscore-rails", "~> 1.8.0" diff --git a/Gemfile.lock b/Gemfile.lock index 9769ae80a7d..ddd31e4802d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,7 +106,7 @@ GEM bundler (~> 1.2) thor (~> 0.18) byebug (8.2.1) - cal-heatmap-rails (0.0.1) + cal-heatmap-rails (3.5.1) capybara (2.4.4) mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -849,7 +849,7 @@ DEPENDENCIES bullet bundler-audit byebug - cal-heatmap-rails (~> 0.0.1) + cal-heatmap-rails (~> 3.5.0) capybara (~> 2.4.0) capybara-screenshot (~> 1.0.0) carrierwave (~> 0.9.0) -- cgit v1.2.1 From 2417f55467247471d2b39de0387b30617ecf59de Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 21:34:49 -0500 Subject: Bump d3_rails to ~> 3.5.0 --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 2418a02a451..1a628a68310 100644 --- a/Gemfile +++ b/Gemfile @@ -169,7 +169,7 @@ gem 'asana', '~> 0.4.0' gem 'ruby-fogbugz', '~> 0.2.1' # d3 -gem 'd3_rails', '~> 3.5.5' +gem 'd3_rails', '~> 3.5.0' #cal-heatmap gem 'cal-heatmap-rails', '~> 3.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index ddd31e4802d..7b70c05bc48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -859,7 +859,7 @@ DEPENDENCIES connection_pool (~> 2.0) coveralls (~> 0.8.2) creole (~> 0.5.0) - d3_rails (~> 3.5.5) + d3_rails (~> 3.5.0) database_cleaner (~> 1.4.0) default_value_for (~> 3.0.0) devise (~> 3.5.3) -- cgit v1.2.1 From f8c3b69fb675f33607315acfacb4fdac170a3635 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 22:10:16 -0500 Subject: Remove unused "options" object from Calendar JS --- app/assets/javascripts/calendar.js.coffee | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 97621236924..d80e0e716ce 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -1,9 +1,4 @@ class @Calendar - options = - month: "short" - day: "numeric" - year: "numeric" - constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> cal = new CalHeatMap() cal.init -- cgit v1.2.1 From 4caf0433b7a06427c82eb1b2830d2c2dd830f144 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 22:10:48 -0500 Subject: Reorder JS requires The old way broke d3, for some reason. --- app/assets/javascripts/application.js.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index affab5bb030..e7e997640d5 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -16,6 +16,8 @@ #= require jquery.scrollTo #= require jquery.blockUI #= require jquery.turbolinks +#= require d3 +#= require cal-heatmap #= require turbolinks #= require autosave #= require bootstrap @@ -27,7 +29,6 @@ #= require branch-graph #= require ace/ace #= require ace/ext-searchbox -#= require d3 #= require underscore #= require nprogress #= require nprogress-turbolinks @@ -39,7 +40,6 @@ #= require shortcuts_dashboard_navigation #= require shortcuts_issuable #= require shortcuts_network -#= require cal-heatmap #= require jquery.nicescroll.min #= require_tree . -- cgit v1.2.1 From 1a1113f7c4ad6e5bceb898593cba6caecbdf0097 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 22:11:34 -0500 Subject: Simplify `ContributionsCalendar#starting_year` and `#starting_month` --- lib/gitlab/contributions_calendar.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb index 8a7f8dc5003..85583dce9ee 100644 --- a/lib/gitlab/contributions_calendar.rb +++ b/lib/gitlab/contributions_calendar.rb @@ -45,11 +45,11 @@ module Gitlab end def starting_year - (Time.now - 1.year).strftime("%Y") + 1.year.ago.year end def starting_month - Date.today.strftime("%m").to_i + Date.today.month end end end -- cgit v1.2.1 From d1873f16b4cc954b1417fa84517d44b22a05821f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 22:12:50 -0500 Subject: Update cal-heatmap style overrides Some were no longer needed, others needed `!important`. --- app/assets/stylesheets/framework/calendar.scss | 42 ++++---------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index a36fefe22c5..580012abd77 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -19,38 +19,33 @@ } } } + /** * This overwrites the default values of the cal-heatmap gem */ .calendar { .qi { - background-color: #999; fill: #fff; } .q1 { - background-color: #dae289; - fill: #ededed; + fill: #ededed !important; } .q2 { - background-color: #cedb9c; - fill: #ACD5F2; + fill: #ACD5F2 !important; } .q3 { - background-color: #b5cf6b; - fill: #7FA8D1; + fill: #7FA8D1 !important; } .q4 { - background-color: #637939; - fill: #49729B; + fill: #49729B !important; } .q5 { - background-color: #3b6427; - fill: #254E77; + fill: #254E77 !important; } .domain-background { @@ -59,32 +54,7 @@ } .ch-tooltip { - position: absolute; - display: none; - margin-top: 22px; - margin-left: 1px; - font-size: 13px; padding: 3px; font-weight: 550; - background-color: #222; - span { - position: absolute; - width: 200px; - text-align: center; - visibility: hidden; - border-radius: 10px; - &:after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - margin-left: -8px; - width: 0; - height: 0; - border-top: 8px solid #000000; - border-right: 8px solid transparent; - border-left: 8px solid transparent; - } - } } } -- cgit v1.2.1 From c80f75ea1fc1dda4001ab32d52136445abb09b53 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 Jan 2016 15:29:55 -0500 Subject: Give the logo shapes meaningful IDs --- app/views/shared/_logo.svg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg index da49c48acd3..90f5f4e672b 100644 --- a/app/views/shared/_logo.svg +++ b/app/views/shared/_logo.svg @@ -5,13 +5,13 @@ - - - - - - - + + + + + + + -- cgit v1.2.1 From e5800d65de66aded6178252c4ae5025633eccc5e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 Jan 2016 15:32:40 -0500 Subject: Use the logo as a loading indicator Closes #5616 --- app/assets/javascripts/logo.js.coffee | 45 +++++++++++++++++++++++++++ app/assets/stylesheets/framework/sidebar.scss | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/logo.js.coffee diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee new file mode 100644 index 00000000000..8cd9f865e37 --- /dev/null +++ b/app/assets/javascripts/logo.js.coffee @@ -0,0 +1,45 @@ +NProgress.configure(showSpinner: false) + +defaultClass = 'tanuki-shape' +highlightClass = 'highlight' +pieces = [ + 'path#tanuki-right-cheek', + 'path#tanuki-right-eye, path#tanuki-right-ear', + 'path#tanuki-nose', + 'path#tanuki-left-eye, path#tanuki-left-ear', + 'path#tanuki-left-cheek', +] +timeout = null + +clearHighlights = -> + $(".#{defaultClass}").attr('class', defaultClass) + +start = -> + clearHighlights() + work(0) + +stop = -> + window.clearTimeout(timeout) + clearHighlights() + +work = (pieceIndex) => + # jQuery's addClass won't work on an SVG. Who knew! + $piece = $(pieces[pieceIndex]) + $piece.attr('class', "#{defaultClass} #{highlightClass}") + + timeout = setTimeout(=> + $piece.attr('class', defaultClass) + + # If we hit the last piece, reset the index and then reverse the array to + # get a nice back-and-forth sweeping look + if pieceIndex + 1 >= pieces.length + nextIndex = 0 + pieces.reverse() + else + nextIndex = pieceIndex + 1 + + work(nextIndex) + , 200) + +$(document).on 'page:fetch', start +$(document).on 'page:change', stop diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 458af76cb75..83243dd2457 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -105,7 +105,7 @@ .tanuki-shape { transition: all 0.8s; - &:hover { + &:hover, &.highlight { fill: rgb(255, 255, 255); transition: all 0.1s; } -- cgit v1.2.1 From 71c31ecf731ff9f6f1d7107ae03397ec98c9f61f Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 Jan 2016 19:57:21 -0500 Subject: Ensure the sweep always starts from the left --- app/assets/javascripts/logo.js.coffee | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee index 8cd9f865e37..47135a6c5eb 100644 --- a/app/assets/javascripts/logo.js.coffee +++ b/app/assets/javascripts/logo.js.coffee @@ -1,14 +1,14 @@ NProgress.configure(showSpinner: false) defaultClass = 'tanuki-shape' -highlightClass = 'highlight' pieces = [ - 'path#tanuki-right-cheek', - 'path#tanuki-right-eye, path#tanuki-right-ear', - 'path#tanuki-nose', - 'path#tanuki-left-eye, path#tanuki-left-ear', 'path#tanuki-left-cheek', + 'path#tanuki-left-eye, path#tanuki-left-ear', + 'path#tanuki-nose', + 'path#tanuki-right-eye, path#tanuki-right-ear', + 'path#tanuki-right-cheek', ] +firstPiece = pieces[0] timeout = null clearHighlights = -> @@ -16,18 +16,19 @@ clearHighlights = -> start = -> clearHighlights() + pieces.reverse() unless pieces[0] == firstPiece work(0) stop = -> window.clearTimeout(timeout) clearHighlights() -work = (pieceIndex) => +work = (pieceIndex) -> # jQuery's addClass won't work on an SVG. Who knew! $piece = $(pieces[pieceIndex]) - $piece.attr('class', "#{defaultClass} #{highlightClass}") + $piece.attr('class', "#{defaultClass} highlight") - timeout = setTimeout(=> + timeout = setTimeout(-> $piece.attr('class', defaultClass) # If we hit the last piece, reset the index and then reverse the array to -- cgit v1.2.1 From fd178c1e7d23b0bf96565ae5177485e847c9271d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 2 Jan 2016 19:53:45 -0500 Subject: Prevent duplicate "username has already been taken" validation message Closes #201 - two-year-old bug, woo! :boom: :tada: --- app/models/user.rb | 5 ++++- spec/models/user_spec.rb | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index df87f3b79bd..20f907e4347 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -352,10 +352,13 @@ class User < ActiveRecord::Base end def namespace_uniq + # Return early if username already failed the first uniqueness validation + return if self.errors[:username].include?('has already been taken') + namespace_name = self.username existing_namespace = Namespace.by_path(namespace_name) if existing_namespace && existing_namespace != self.namespace - self.errors.add :username, "already exists" + self.errors.add(:username, 'has already been taken') end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2f184bbaf92..a16161e673e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -106,7 +106,7 @@ describe User, models: true do end it 'validates uniqueness' do - expect(subject).to validate_uniqueness_of(:username) + expect(subject).to validate_uniqueness_of(:username).case_insensitive end end -- cgit v1.2.1 From befdc6770799735bde5eeb26a1170cedd18a00de Mon Sep 17 00:00:00 2001 From: Atul Bhosale Date: Sun, 3 Jan 2016 18:28:52 +0530 Subject: Update copyright notice to 2016 [ci skip] --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d8cb29f3638..1dc1bdb7411 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2015 GitLab B.V. +Copyright (c) 2011-2016 GitLab B.V. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal -- cgit v1.2.1 From 2802d4992d064e63cf2015c7bb002318eaa84c41 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 3 Jan 2016 11:20:14 -0800 Subject: Expire view caches when application settings change Closes #5728 --- CHANGELOG | 1 + app/views/events/_event.html.haml | 2 +- app/views/projects/commits/_commit.html.haml | 2 +- app/views/shared/projects/_project.html.haml | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index ea403642e32..4c1148bc4dc 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.4.0 (unreleased) + - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Implement new UI for group page - Implement search inside emoji picker - Add API support for looking up a user by username (Stan Hu) diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index 9aacc79d686..46432a92348 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -3,7 +3,7 @@ .event-item-timestamp #{time_ago_with_tooltip(event.created_at)} - = cache [event, "v2.1"] do + = cache [event, current_application_settings, "v2.1"] do = image_tag avatar_icon(event.author_email, 46), class: "avatar s46", alt:'' - if event.created_project? = render "events/event/created_project", event: event diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 28b82dd31f3..012825f0fdb 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -5,7 +5,7 @@ - note_count = notes.user.count - ci_commit = project.ci_commit(commit.sha) -- cache_key = [project.path_with_namespace, commit.id, note_count] +- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count] - cache_key.push(ci_commit.status) if ci_commit = cache(cache_key) do diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index c36995b94d7..86249851a82 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -5,7 +5,7 @@ - css_class = '' unless local_assigns[:css_class] - css_class += " no-description" unless project.description.present? %li.project-row{ class: css_class } - = cache [project.namespace, project, controller.controller_name, controller.action_name, 'v2.2'] do + = cache [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.2'] do = link_to project_path(project), class: dom_class(project) do - if avatar .dash-project-avatar -- cgit v1.2.1 From 086cfc8685a6489ca032899307c77f828f515fbb Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 26 Aug 2015 23:36:17 -0700 Subject: Fix API project lookups when querying with a namespace with dots Attempting to use the /projects/:id API by specifying :id in "namespace/project" format would always result in a 404 if the namespace contained a dot. The reason? From http://guides.rubyonrails.org/routing.html#specifying-constraints: "By default the :id parameter doesn't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within an :id add a constraint which overrides this - for example id: /[^\/]+/ allows anything except a slash." Closes https://github.com/gitlabhq/gitlabhq/issues/9573 --- CHANGELOG | 1 + lib/api/projects.rb | 2 +- spec/requests/api/projects_spec.rb | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 2b7d5808e7e..e651aece696 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.4.0 (unreleased) - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) - Fix version check image in Safari + - Fix API project lookups when querying with a namespace with dots (Stan Hu) v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/lib/api/projects.rb b/lib/api/projects.rb index a9e0960872a..0781236cf6d 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -3,7 +3,7 @@ module API class Projects < Grape::API before { authenticate! } - resource :projects do + resource :projects, requirements: { id: /[^\/]+/ } do helpers do def map_public_to_visibility_level(attrs) publik = attrs.delete(:public) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 7f0f9454b10..ab2530859ea 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -382,6 +382,15 @@ describe API::API, api: true do expect(response.status).to eq(404) end + it 'should handle users with dots' do + dot_user = create(:user, username: 'dot.user') + project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace) + + get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) + expect(response.status).to eq(200) + expect(json_response['name']).to eq(project.name) + end + describe 'permissions' do context 'all projects' do it 'Contains permission information' do -- cgit v1.2.1 From 8bfc46451eae5bd92380b9fbdf91b9636d55066c Mon Sep 17 00:00:00 2001 From: Josh Frye Date: Wed, 23 Dec 2015 10:20:07 -0500 Subject: Show 'New Merge Request' buttons on canonical repo. --- CHANGELOG | 2 ++ app/views/projects/buttons/_dropdown.html.haml | 7 ++++--- app/views/projects/merge_requests/index.html.haml | 5 +++-- features/project/fork.feature | 10 ++++++++++ features/steps/project/fork.rb | 10 ++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2b7d5808e7e..e4245060489 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -91,6 +91,8 @@ v 8.3.0 - Do not show build status unless builds are enabled and `.gitlab-ci.yml` is present - Persist runners registration token in database - Fix online editor should not remove newlines at the end of the file + - Expose Git's version in the admin area + - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 1f639fecc30..459e6da2fe2 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -8,11 +8,12 @@ = 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) + - merge_project = can?(current_user, :create_merge_request, @project) ? @project : current_user.fork_of(@project) + - if merge_project %li - = link_to new_namespace_project_merge_request_path(@project.namespace, @project) do + = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do = icon('tasks fw') - New merge request + New Merge Request - if can?(current_user, :create_snippet, @project) %li = link_to new_namespace_project_snippet_path(@project.namespace, @project) do diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 086298e5af1..972fce9ad3d 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -6,9 +6,10 @@ .controls = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project) - - if can? current_user, :create_merge_request, @project + - merge_project = can?(current_user, :create_merge_request, @project) ? @project : current_user.fork_of(@project) + - if merge_project .pull-left.hidden-xs - = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new", title: "New Merge Request" do + = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do %i.fa.fa-plus New Merge Request = render 'shared/issuable/filter', type: :merge_requests diff --git a/features/project/fork.feature b/features/project/fork.feature index 22f68e5b340..40849352370 100644 --- a/features/project/fork.feature +++ b/features/project/fork.feature @@ -1,3 +1,4 @@ +@forks Feature: Project Fork Background: Given I sign in as a user @@ -14,3 +15,12 @@ Feature: Project Fork And I click link "Fork" When I fork to my namespace Then I should see a "Name has already been taken" warning + + Scenario: Merge request on canonical repo goes to fork merge request page + Given I click link "Fork" + And I fork to my namespace + Then I should see the forked project page + When I visit project "Shop" page + Then I should see "New merge request" + And I goto the Merge Requests page + Then I should see "New merge request" diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index b0230add34f..878ddea46ff 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -30,4 +30,14 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps click_link current_user.name end end + + step 'I should see "New Merge Request"' do + expect(page).to have_content "New Merge Request" + end + + step 'I goto the Merge Requests page' do + page.within '.page-sidebar-expanded' do + click_link "Merge Requests" + end + end end -- cgit v1.2.1 From 95e6327b85b8230dc834c781203c72b318551b5d Mon Sep 17 00:00:00 2001 From: Josh Frye Date: Wed, 23 Dec 2015 12:24:25 -0500 Subject: Fix tests --- app/views/projects/buttons/_dropdown.html.haml | 2 +- app/views/projects/merge_requests/index.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 459e6da2fe2..35a9d3223a6 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -8,7 +8,7 @@ = link_to url_for_new_issue(@project, only_path: true) do = icon('exclamation-circle fw') New issue - - merge_project = can?(current_user, :create_merge_request, @project) ? @project : current_user.fork_of(@project) + - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - if merge_project %li = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 972fce9ad3d..8d5d0394a82 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -6,7 +6,7 @@ .controls = render 'shared/issuable/search_form', path: namespace_project_merge_requests_path(@project.namespace, @project) - - merge_project = can?(current_user, :create_merge_request, @project) ? @project : current_user.fork_of(@project) + - merge_project = can?(current_user, :create_merge_request, @project) ? @project : (current_user && current_user.fork_of(@project)) - if merge_project .pull-left.hidden-xs = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project), class: "btn btn-new", title: "New Merge Request" do -- cgit v1.2.1 From 796c11e9a7d955ac40c3d1f8427f3f65063ac8c4 Mon Sep 17 00:00:00 2001 From: Josh Frye Date: Wed, 23 Dec 2015 12:36:49 -0500 Subject: Remove feature tag from testing locally --- features/project/fork.feature | 1 - 1 file changed, 1 deletion(-) diff --git a/features/project/fork.feature b/features/project/fork.feature index 40849352370..1182f493e34 100644 --- a/features/project/fork.feature +++ b/features/project/fork.feature @@ -1,4 +1,3 @@ -@forks Feature: Project Fork Background: Given I sign in as a user -- cgit v1.2.1 From edd2ce38369e5a332b1b9932647d670862ffddbf Mon Sep 17 00:00:00 2001 From: Josh Frye Date: Fri, 25 Dec 2015 11:30:48 -0500 Subject: Change text back. Add additional tests. --- app/views/projects/buttons/_dropdown.html.haml | 2 +- features/project/fork.feature | 2 ++ features/steps/project/fork.rb | 13 +++++++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 35a9d3223a6..f9ab78e7874 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -13,7 +13,7 @@ %li = link_to new_namespace_project_merge_request_path(merge_project.namespace, merge_project) do = icon('tasks fw') - New Merge Request + New merge request - if can?(current_user, :create_snippet, @project) %li = link_to new_namespace_project_snippet_path(@project.namespace, @project) do diff --git a/features/project/fork.feature b/features/project/fork.feature index 1182f493e34..37cd53ee977 100644 --- a/features/project/fork.feature +++ b/features/project/fork.feature @@ -23,3 +23,5 @@ Feature: Project Fork Then I should see "New merge request" And I goto the Merge Requests page Then I should see "New merge request" + And I click link "New merge request" + Then I should see the new merge request page for my namespace diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index 878ddea46ff..e98bd51ca89 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -31,8 +31,8 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps end end - step 'I should see "New Merge Request"' do - expect(page).to have_content "New Merge Request" + step 'I should see "New merge request"' do + expect(page).to have_content(/new merge request/i) end step 'I goto the Merge Requests page' do @@ -40,4 +40,13 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps click_link "Merge Requests" end end + + step 'I click link "New merge request"' do + expect(page).to have_content(/new merge request/i) + click_link "New Merge Request" + end + + step 'I should see the new merge request page for my namespace' do + current_path.should have_content(/#{current_user.namespace.name}/i) + end end -- cgit v1.2.1 From 0134a85bee1e1f932d7c9d8752d8eb8e5148baf2 Mon Sep 17 00:00:00 2001 From: Christian Mehlmauer Date: Mon, 4 Jan 2016 00:29:30 +0100 Subject: added make command to docs --- doc/update/patch_versions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index c19ee49f9e0..a10e62877ba 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -48,6 +48,7 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca cd /home/git/gitlab-workhorse sudo -u git -H git fetch sudo -u git -H git checkout `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` +sudo -u git -H make ``` ### 5. Install libs, migrations, etc. -- cgit v1.2.1 From 96075be6f4688a59335130dc796132ad4f232442 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 11:37:46 +0100 Subject: Ability to increment custom transaction values This will be used to store/increment the total query/view rendering timings on a per transaction basis. This in turn can greatly reduce the amount of metrics stored. --- lib/gitlab/metrics/transaction.rb | 15 +++++++++++++-- spec/lib/gitlab/metrics/transaction_spec.rb | 12 ++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 43a7dab5323..0aaebf262d4 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -19,7 +19,8 @@ module Gitlab @started_at = nil @finished_at = nil - @tags = {} + @values = Hash.new(0) + @tags = {} end def duration @@ -44,6 +45,10 @@ module Gitlab @metrics << Metric.new(series, values, tags) end + def increment(name, value) + @values[name] += value + end + def add_tag(key, value) @tags[key] = value end @@ -54,7 +59,13 @@ module Gitlab end def track_self - add_metric(@series, { duration: duration }, @tags) + values = { duration: duration } + + @values.each do |name, value| + values[name] = value + end + + add_metric(@series, values, @tags) end def submit diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 345163bfbea..18d63bdbdb9 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -38,6 +38,18 @@ describe Gitlab::Metrics::Transaction do end end + describe '#increment' do + it 'increments a counter' do + transaction.increment(:time, 1) + transaction.increment(:time, 2) + + expect(transaction).to receive(:add_metric). + with('rspec', { duration: 0.0, time: 3 }, {}) + + transaction.track_self + end + end + describe '#add_tag' do it 'adds a tag' do transaction.add_tag(:foo, 'bar') -- cgit v1.2.1 From 66a997a91403eef62ffd9fb789e899619d021a26 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 12:14:36 +0100 Subject: Track total query/view timings in transactions --- config/initializers/metrics.rb | 1 + lib/gitlab/metrics/subscribers/action_view.rb | 1 + lib/gitlab/metrics/subscribers/active_record.rb | 22 ++++++++++++++ .../gitlab/metrics/subscribers/action_view_spec.rb | 3 ++ .../metrics/subscribers/active_record_spec.rb | 35 ++++++++++++++++++++++ 5 files changed, 62 insertions(+) create mode 100644 lib/gitlab/metrics/subscribers/active_record.rb create mode 100644 spec/lib/gitlab/metrics/subscribers/active_record_spec.rb diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb index ebb20be7283..52ace27b7ae 100644 --- a/config/initializers/metrics.rb +++ b/config/initializers/metrics.rb @@ -6,6 +6,7 @@ if Gitlab::Metrics.enabled? # These are manually require'd so the classes are registered properly with # ActiveSupport. require 'gitlab/metrics/subscribers/action_view' + require 'gitlab/metrics/subscribers/active_record' Gitlab::Application.configure do |config| config.middleware.use(Gitlab::Metrics::RackMiddleware) diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 7e0dcf99d92..7c0105d543a 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -19,6 +19,7 @@ module Gitlab values = values_for(event) tags = tags_for(event) + current_transaction.increment(:view_duration, event.duration) current_transaction.add_metric(SERIES, values, tags) end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb new file mode 100644 index 00000000000..8008b3bc895 --- /dev/null +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -0,0 +1,22 @@ +module Gitlab + module Metrics + module Subscribers + # Class for tracking the total query duration of a transaction. + class ActiveRecord < ActiveSupport::Subscriber + attach_to :active_record + + def sql(event) + return unless current_transaction + + current_transaction.increment(:sql_duration, event.duration) + end + + private + + def current_transaction + Transaction.current + end + end + end + end +end diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb index bca76ca5a69..699d50f770a 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -28,6 +28,9 @@ describe Gitlab::Metrics::Subscribers::ActionView do line: 4 } + expect(transaction).to receive(:increment). + with(:view_duration, 2.1) + expect(transaction).to receive(:add_metric). with(described_class::SERIES, values, tags) diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb new file mode 100644 index 00000000000..9ecedd934c6 --- /dev/null +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe Gitlab::Metrics::Subscribers::ActiveRecord do + let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } + let(:subscriber) { described_class.new } + + let(:event) do + double(:event, duration: 0.2, + payload: { sql: 'SELECT * FROM users WHERE id = 10' }) + end + + describe '#sql' do + describe 'without a current transaction' do + it 'simply returns' do + expect_any_instance_of(Gitlab::Metrics::Transaction). + to_not receive(:increment) + + subscriber.sql(event) + end + end + + describe 'with a current transaction' do + it 'increments the :sql_duration value' do + expect(subscriber).to receive(:current_transaction). + at_least(:once). + and_return(transaction) + + expect(transaction).to receive(:increment). + with(:sql_duration, 0.2) + + subscriber.sql(event) + end + end + end +end -- cgit v1.2.1 From 825b46f8a3eb620f99192217d414b72dffe597d7 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 12:19:45 +0100 Subject: Track total method call times per transaction This makes it easier to see where time is spent without having to aggregate all the individual points in the method_calls series. --- lib/gitlab/metrics/instrumentation.rb | 2 ++ spec/lib/gitlab/metrics/instrumentation_spec.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/gitlab/metrics/instrumentation.rb b/lib/gitlab/metrics/instrumentation.rb index 06fc2f25948..d9fce2e6758 100644 --- a/lib/gitlab/metrics/instrumentation.rb +++ b/lib/gitlab/metrics/instrumentation.rb @@ -123,6 +123,8 @@ module Gitlab duration = (Time.now - start) * 1000.0 if duration >= Gitlab::Metrics.method_call_threshold + trans.increment(:method_duration, duration) + trans.add_metric(Gitlab::Metrics::Instrumentation::SERIES, { duration: duration }, method: #{label.inspect}) diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index a9003d8796b..af020f652be 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -48,6 +48,9 @@ describe Gitlab::Metrics::Instrumentation do allow(described_class).to receive(:transaction). and_return(transaction) + expect(transaction).to receive(:increment). + with(:method_duration, a_kind_of(Numeric)) + expect(transaction).to receive(:add_metric). with(described_class::SERIES, an_instance_of(Hash), method: 'Dummy.foo') @@ -102,6 +105,9 @@ describe Gitlab::Metrics::Instrumentation do allow(described_class).to receive(:transaction). and_return(transaction) + expect(transaction).to receive(:increment). + with(:method_duration, a_kind_of(Numeric)) + expect(transaction).to receive(:add_metric). with(described_class::SERIES, an_instance_of(Hash), method: 'Dummy#bar') -- cgit v1.2.1 From 2ea464bb272fa52ff34a188a921f0bc90811ca45 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 12:45:31 +0100 Subject: Use separate series for Rails/Sidekiq sample stats This removes the need for any tags to differentiate between Sidekiq and Rails statistics while still being able to separate the two. --- lib/gitlab/metrics/sampler.rb | 19 ++++++++++++----- spec/lib/gitlab/metrics/sampler_spec.rb | 38 ++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/lib/gitlab/metrics/sampler.rb b/lib/gitlab/metrics/sampler.rb index 998578e1c0a..1ea425bc904 100644 --- a/lib/gitlab/metrics/sampler.rb +++ b/lib/gitlab/metrics/sampler.rb @@ -50,12 +50,11 @@ module Gitlab end def sample_memory_usage - @metrics << Metric.new('memory_usage', value: System.memory_usage) + add_metric('memory_usage', value: System.memory_usage) end def sample_file_descriptors - @metrics << Metric. - new('file_descriptors', value: System.file_descriptor_count) + add_metric('file_descriptors', value: System.file_descriptor_count) end if Metrics.mri? @@ -69,7 +68,7 @@ module Gitlab counts['Symbol'] = Symbol.all_symbols.length counts.each do |name, count| - @metrics << Metric.new('object_counts', { count: count }, type: name) + add_metric('object_counts', { count: count }, type: name) end end else @@ -91,7 +90,17 @@ module Gitlab stats[:count] = stats[:minor_gc_count] + stats[:major_gc_count] - @metrics << Metric.new('gc_statistics', stats) + add_metric('gc_statistics', stats) + end + + def add_metric(series, values, tags = {}) + prefix = sidekiq? ? 'sidekiq_' : 'rails_' + + @metrics << Metric.new("#{prefix}#{series}", values, tags) + end + + def sidekiq? + Sidekiq.server? end end end diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb index 51a941c48cd..27211350fbe 100644 --- a/spec/lib/gitlab/metrics/sampler_spec.rb +++ b/spec/lib/gitlab/metrics/sampler_spec.rb @@ -51,8 +51,8 @@ describe Gitlab::Metrics::Sampler do expect(Gitlab::Metrics::System).to receive(:memory_usage). and_return(9000) - expect(Gitlab::Metrics::Metric).to receive(:new). - with('memory_usage', value: 9000). + expect(sampler).to receive(:add_metric). + with(/memory_usage/, value: 9000). and_call_original sampler.sample_memory_usage @@ -64,8 +64,8 @@ describe Gitlab::Metrics::Sampler do expect(Gitlab::Metrics::System).to receive(:file_descriptor_count). and_return(4) - expect(Gitlab::Metrics::Metric).to receive(:new). - with('file_descriptors', value: 4). + expect(sampler).to receive(:add_metric). + with(/file_descriptors/, value: 4). and_call_original sampler.sample_file_descriptors @@ -74,8 +74,8 @@ describe Gitlab::Metrics::Sampler do describe '#sample_objects' do it 'adds a metric containing the amount of allocated objects' do - expect(Gitlab::Metrics::Metric).to receive(:new). - with('object_counts', an_instance_of(Hash), an_instance_of(Hash)). + expect(sampler).to receive(:add_metric). + with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash)). at_least(:once). and_call_original @@ -87,11 +87,33 @@ describe Gitlab::Metrics::Sampler do it 'adds a metric containing garbage collection statistics' do expect(GC::Profiler).to receive(:total_time).and_return(0.24) - expect(Gitlab::Metrics::Metric).to receive(:new). - with('gc_statistics', an_instance_of(Hash)). + expect(sampler).to receive(:add_metric). + with(/gc_statistics/, an_instance_of(Hash)). and_call_original sampler.sample_gc end end + + describe '#add_metric' do + it 'prefixes the series name for a Rails process' do + expect(sampler).to receive(:sidekiq?).and_return(false) + + expect(Gitlab::Metrics::Metric).to receive(:new). + with('rails_cats', { value: 10 }, {}). + and_call_original + + sampler.add_metric('cats', value: 10) + end + + it 'prefixes the series name for a Sidekiq process' do + expect(sampler).to receive(:sidekiq?).and_return(true) + + expect(Gitlab::Metrics::Metric).to receive(:new). + with('sidekiq_cats', { value: 10 }, {}). + and_call_original + + sampler.add_metric('cats', value: 10) + end + end end -- cgit v1.2.1 From 4c5be69583a61b9cce40b43dc93f00e1ae909680 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 4 Jan 2016 14:06:48 +0200 Subject: catch Emoji encode error --- app/helpers/issues_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 80e2741b09a..c12456a187f 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -99,7 +99,7 @@ module IssuesHelper end def emoji_icon(name, unicode = nil, aliases = []) - unicode ||= Emoji.emoji_filename(name) + unicode ||= Emoji.emoji_filename(name) rescue "" content_tag :div, "", class: "icon emoji-icon emoji-#{unicode}", -- cgit v1.2.1 From 2ee8f555996baca6b470d223ffad65419b730398 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 13:17:02 +0100 Subject: Automatically prefix transaction series names This ensures Rails and Sidekiq transactions are split into the series "rails_transactions" and "sidekiq_transactions" respectively. --- lib/gitlab/metrics/rack_middleware.rb | 4 +--- lib/gitlab/metrics/sidekiq_middleware.rb | 4 +--- lib/gitlab/metrics/transaction.rb | 15 +++++++++------ spec/lib/gitlab/metrics/instrumentation_spec.rb | 2 +- spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb | 2 +- spec/lib/gitlab/metrics/subscribers/action_view_spec.rb | 2 +- spec/lib/gitlab/metrics/subscribers/active_record_spec.rb | 2 +- spec/lib/gitlab/metrics/transaction_spec.rb | 8 ++++---- 8 files changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index bb9e4fcb918..5c0587c4c51 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -4,8 +4,6 @@ module Gitlab class RackMiddleware CONTROLLER_KEY = 'action_controller.instance' - SERIES = 'rails_transactions' - def initialize(app) @app = app end @@ -32,7 +30,7 @@ module Gitlab end def transaction_from_env(env) - trans = Transaction.new(SERIES) + trans = Transaction.new trans.add_tag(:request_method, env['REQUEST_METHOD']) trans.add_tag(:request_uri, env['REQUEST_URI']) diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb index 6e804dd2562..ad441decfa2 100644 --- a/lib/gitlab/metrics/sidekiq_middleware.rb +++ b/lib/gitlab/metrics/sidekiq_middleware.rb @@ -4,10 +4,8 @@ module Gitlab # # This middleware is intended to be used as a server-side middleware. class SidekiqMiddleware - SERIES = 'sidekiq_transactions' - def call(worker, message, queue) - trans = Transaction.new(SERIES) + trans = Transaction.new begin trans.run { yield } diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 0aaebf262d4..68b86de0655 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -10,9 +10,7 @@ module Gitlab Thread.current[THREAD_KEY] end - # name - The name of this transaction as a String. - def initialize(series) - @series = series + def initialize @metrics = [] @uuid = SecureRandom.uuid @@ -40,9 +38,10 @@ module Gitlab end def add_metric(series, values, tags = {}) - tags = tags.merge(transaction_id: @uuid) + tags = tags.merge(transaction_id: @uuid) + prefix = sidekiq? ? 'sidekiq_' : 'rails_' - @metrics << Metric.new(series, values, tags) + @metrics << Metric.new("#{prefix}#{series}", values, tags) end def increment(name, value) @@ -65,12 +64,16 @@ module Gitlab values[name] = value end - add_metric(@series, values, @tags) + add_metric('transactions', values, @tags) end def submit Metrics.submit_metrics(@metrics.map(&:to_hash)) end + + def sidekiq? + Sidekiq.server? + end end end end diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index af020f652be..2a37cd40dde 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Instrumentation do - let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } + let(:transaction) { Gitlab::Metrics::Transaction.new } before do @dummy = Class.new do diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb index 5fda6de52f4..5882e7d81c7 100644 --- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb @@ -15,7 +15,7 @@ describe Gitlab::Metrics::SidekiqMiddleware do describe '#tag_worker' do it 'adds the worker class and action to the transaction' do - trans = Gitlab::Metrics::Transaction.new('rspec') + trans = Gitlab::Metrics::Transaction.new worker = double(:worker, class: double(:class, name: 'TestWorker')) expect(trans).to receive(:add_tag).with(:action, 'TestWorker#perform') diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb index 699d50f770a..05e4fbbeb51 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Subscribers::ActionView do - let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } + let(:transaction) { Gitlab::Metrics::Transaction.new } let(:subscriber) { described_class.new } diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 9ecedd934c6..81f35ba5d40 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Subscribers::ActiveRecord do - let(:transaction) { Gitlab::Metrics::Transaction.new('rspec') } + let(:transaction) { Gitlab::Metrics::Transaction.new } let(:subscriber) { described_class.new } let(:event) do diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 18d63bdbdb9..b9b94947afa 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::Metrics::Transaction do - let(:transaction) { described_class.new('rspec') } + let(:transaction) { described_class.new } describe '#duration' do it 'returns the duration of a transaction in seconds' do @@ -32,7 +32,7 @@ describe Gitlab::Metrics::Transaction do describe '#add_metric' do it 'adds a metric tagged with the transaction UUID' do expect(Gitlab::Metrics::Metric).to receive(:new). - with('foo', { number: 10 }, { transaction_id: transaction.uuid }) + with('rails_foo', { number: 10 }, { transaction_id: transaction.uuid }) transaction.add_metric('foo', number: 10) end @@ -44,7 +44,7 @@ describe Gitlab::Metrics::Transaction do transaction.increment(:time, 2) expect(transaction).to receive(:add_metric). - with('rspec', { duration: 0.0, time: 3 }, {}) + with('transactions', { duration: 0.0, time: 3 }, {}) transaction.track_self end @@ -70,7 +70,7 @@ describe Gitlab::Metrics::Transaction do describe '#track_self' do it 'adds a metric for the transaction itself' do expect(transaction).to receive(:add_metric). - with('rspec', { duration: transaction.duration }, {}) + with('transactions', { duration: transaction.duration }, {}) transaction.track_self end -- cgit v1.2.1 From 567dc62b6dd114ac129eb2f45baa8155f5f11a51 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 30 Dec 2015 12:25:42 -0200 Subject: Show 'All' tab by default in the builds page --- CHANGELOG | 2 + app/controllers/admin/builds_controller.rb | 6 +- app/controllers/projects/builds_controller.rb | 6 +- app/views/admin/builds/index.html.haml | 10 +-- app/views/projects/builds/index.html.haml | 12 +-- spec/features/admin/admin_builds_spec.rb | 119 ++++++++++++++++---------- spec/features/builds_spec.rb | 23 +++-- 7 files changed, 107 insertions(+), 71 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b25e0eabd7d..a0d5039905b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,8 @@ v 8.4.0 (unreleased) - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) - Fix version check image in Safari + - Enable "Add key" button when user fills in a proper key (Stan Hu) + - Show 'All' tab by default in the builds page v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/app/controllers/admin/builds_controller.rb b/app/controllers/admin/builds_controller.rb index 83d9684c706..0db91eaaf2e 100644 --- a/app/controllers/admin/builds_controller.rb +++ b/app/controllers/admin/builds_controller.rb @@ -5,12 +5,12 @@ class Admin::BuildsController < Admin::ApplicationController @builds = @all_builds.order('created_at DESC') @builds = case @scope - when 'all' - @builds + when 'running' + @builds.running_or_pending.reverse_order when 'finished' @builds.finished else - @builds.running_or_pending.reverse_order + @builds end @builds = @builds.page(params[:page]).per(30) end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 26ba12520c7..39d3ba26ba2 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -12,12 +12,12 @@ class Projects::BuildsController < Projects::ApplicationController @builds = @all_builds.order('created_at DESC') @builds = case @scope - when 'all' - @builds + when 'running' + @builds.running_or_pending.reverse_order when 'finished' @builds.finished else - @builds.running_or_pending.reverse_order + @builds end @builds = @builds.page(params[:page]).per(30) end diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml index 52c36af6225..ddd4e1481eb 100644 --- a/app/views/admin/builds/index.html.haml +++ b/app/views/admin/builds/index.html.haml @@ -7,6 +7,11 @@ %ul.center-top-menu %li{class: ('active' if @scope.nil?)} = link_to admin_builds_path do + All + %span.badge.js-totalbuilds-count= @all_builds.count(:id) + + %li{class: ('active' if @scope == 'running')} + = link_to admin_builds_path(scope: :running) do Running %span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id)) @@ -15,11 +20,6 @@ Finished %span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id)) - %li{class: ('active' if @scope == 'all')} - = link_to admin_builds_path(scope: :all) do - All - %span.badge.js-totalbuilds-count= number_with_delimiter(@all_builds.count(:id)) - .gray-content-block #{(@scope || 'running').capitalize} builds diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml index 1a26908ab11..2fa5ad80fda 100644 --- a/app/views/projects/builds/index.html.haml +++ b/app/views/projects/builds/index.html.haml @@ -11,6 +11,12 @@ %ul.center-top-menu %li{class: ('active' if @scope.nil?)} = link_to project_builds_path(@project) do + All + %span.badge.js-totalbuilds-count + = number_with_delimiter(@all_builds.count(:id)) + + %li{class: ('active' if @scope == 'running')} + = link_to project_builds_path(@project, scope: :running) do Running %span.badge.js-running-count = number_with_delimiter(@all_builds.running_or_pending.count(:id)) @@ -21,12 +27,6 @@ %span.badge.js-running-count = number_with_delimiter(@all_builds.finished.count(:id)) - %li{class: ('active' if @scope == 'all')} - = link_to project_builds_path(@project, scope: :all) do - All - %span.badge.js-totalbuilds-count - = number_with_delimiter(@all_builds.count(:id)) - .gray-content-block #{(@scope || 'running').capitalize} builds from this project diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb index 72764b1629d..b955d0b0c46 100644 --- a/spec/features/admin/admin_builds_spec.rb +++ b/spec/features/admin/admin_builds_spec.rb @@ -1,69 +1,98 @@ require 'spec_helper' -describe "Admin Builds" do - let(:commit) { FactoryGirl.create :ci_commit } - let(:build) { FactoryGirl.create :ci_build, commit: commit } - +describe 'Admin Builds' do before do login_as :admin end - describe "GET /admin/builds" do - before do - build - visit admin_builds_path - end - - it { expect(page).to have_content "Running" } - it { expect(page).to have_content build.short_sha } - end + describe 'GET /admin/builds' do + let(:commit) { create(:ci_commit) } - describe "Tabs" do - it "shows all builds" do - FactoryGirl.create :ci_build, commit: commit, status: "pending" - FactoryGirl.create :ci_build, commit: commit, status: "running" - FactoryGirl.create :ci_build, commit: commit, status: "success" - FactoryGirl.create :ci_build, commit: commit, status: "failed" + context 'All tab' do + context 'when have builds' do + it 'shows all builds' do + create(:ci_build, commit: commit, status: :pending) + create(:ci_build, commit: commit, status: :running) + create(:ci_build, commit: commit, status: :success) + create(:ci_build, commit: commit, status: :failed) - visit admin_builds_path + visit admin_builds_path - within ".center-top-menu" do - click_on "All" + expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') + expect(page.all('.build-link').size).to eq(4) + expect(page).to have_link 'Cancel all' + end end - expect(page.all(".build-link").size).to eq(4) + context 'when have no builds' do + it 'shows a message' do + visit admin_builds_path + + expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') + expect(page).to have_content 'No builds to show' + expect(page).not_to have_link 'Cancel all' + end + end end - it "shows finished builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build1 = FactoryGirl.create :ci_build, commit: commit, status: "running" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" + context 'Running tab' do + context 'when have running builds' do + it 'shows running builds' do + build1 = create(:ci_build, commit: commit, status: :pending) + build2 = create(:ci_build, commit: commit, status: :success) + build3 = create(:ci_build, commit: commit, status: :failed) + + visit admin_builds_path(scope: :running) + + expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running') + expect(page.find('.build-link')).to have_content(build1.id) + expect(page.find('.build-link')).not_to have_content(build2.id) + expect(page.find('.build-link')).not_to have_content(build3.id) + expect(page).to have_link 'Cancel all' + end + end - visit admin_builds_path + context 'when have no builds running' do + it 'shows a message' do + create(:ci_build, commit: commit, status: :success) - within ".center-top-menu" do - click_on "Finished" - end + visit admin_builds_path(scope: :running) - expect(page.find(".build-link")).not_to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build1.id) - expect(page.find(".build-link")).to have_content(build2.id) + expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running') + expect(page).to have_content 'No builds to show' + expect(page).not_to have_link 'Cancel all' + end + end end - it "shows running builds" do - build = FactoryGirl.create :ci_build, commit: commit, status: "pending" - build2 = FactoryGirl.create :ci_build, commit: commit, status: "success" - build3 = FactoryGirl.create :ci_build, commit: commit, status: "failed" + context 'Finished tab' do + context 'when have finished builds' do + it 'shows finished builds' do + build1 = create(:ci_build, commit: commit, status: :pending) + build2 = create(:ci_build, commit: commit, status: :running) + build3 = create(:ci_build, commit: commit, status: :success) + + visit admin_builds_path(scope: :finished) + + expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished') + expect(page.find('.build-link')).not_to have_content(build1.id) + expect(page.find('.build-link')).not_to have_content(build2.id) + expect(page.find('.build-link')).to have_content(build3.id) + expect(page).to have_link 'Cancel all' + end + end - visit admin_builds_path + context 'when have no builds finished' do + it 'shows a message' do + create(:ci_build, commit: commit, status: :running) - within ".center-top-menu" do - click_on "Running" - end + visit admin_builds_path(scope: :finished) - expect(page.find(".build-link")).to have_content(build.id) - expect(page.find(".build-link")).not_to have_content(build2.id) - expect(page.find(".build-link")).not_to have_content(build3.id) + expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished') + expect(page).to have_content 'No builds to show' + expect(page).to have_link 'Cancel all' + end + end end end end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index f0031a0a247..240e56839df 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -15,11 +15,11 @@ describe "Builds" do context "Running scope" do before do @build.run! - visit namespace_project_builds_path(@project.namespace, @project) + visit namespace_project_builds_path(@project.namespace, @project, scope: :running) end - it { expect(page).to have_content 'Running' } - it { expect(page).to have_content 'Cancel running' } + it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'Running') } + it { expect(page).to have_link 'Cancel running' } it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } @@ -31,21 +31,22 @@ describe "Builds" do visit namespace_project_builds_path(@project.namespace, @project, scope: :finished) end + it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'Finished') } it { expect(page).to have_content 'No builds to show' } - it { expect(page).to have_content 'Cancel running' } + it { expect(page).to have_link 'Cancel running' } end context "All builds" do before do @project.builds.running_or_pending.each(&:success) - visit namespace_project_builds_path(@project.namespace, @project, scope: :all) + visit namespace_project_builds_path(@project.namespace, @project) end - it { expect(page).to have_content 'All' } + it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') } it { expect(page).to have_content @build.short_sha } it { expect(page).to have_content @build.ref } it { expect(page).to have_content @build.name } - it { expect(page).to_not have_content 'Cancel running' } + it { expect(page).to_not have_link 'Cancel running' } end end @@ -56,8 +57,12 @@ describe "Builds" do click_link "Cancel running" end - it { expect(page).to have_content 'No builds to show' } - it { expect(page).to_not have_content 'Cancel running' } + it { expect(page).to have_selector('.project-issuable-filter li.active', text: 'All') } + it { expect(page).to have_content 'canceled' } + it { expect(page).to have_content @build.short_sha } + it { expect(page).to have_content @build.ref } + it { expect(page).to have_content @build.name } + it { expect(page).to_not have_link 'Cancel running' } end describe "GET /:project/builds/:id" do -- cgit v1.2.1 From 8de491a68fcb130d436d2c85c0fda900381875cf Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 4 Jan 2016 14:21:39 +0100 Subject: Fix Rubocop styling in AR subscriber specs --- spec/lib/gitlab/metrics/subscribers/active_record_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 81f35ba5d40..7bc070a4d09 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -6,7 +6,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do let(:event) do double(:event, duration: 0.2, - payload: { sql: 'SELECT * FROM users WHERE id = 10' }) + payload: { sql: 'SELECT * FROM users WHERE id = 10' }) end describe '#sql' do -- cgit v1.2.1 From 4b027bc93a7875c3937f6b90ac1049b4a4d72da5 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 4 Jan 2016 14:29:06 +0100 Subject: Add DEBUG_BANZAI_CACHE env var to debug Banzai cache issue. --- lib/banzai/renderer.rb | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb index 115ae914524..910e1c6994e 100644 --- a/lib/banzai/renderer.rb +++ b/lib/banzai/renderer.rb @@ -1,7 +1,5 @@ module Banzai module Renderer - CACHE_ENABLED = false - # Convert a Markdown String into an HTML-safe String of HTML # # Note that while the returned HTML will have been sanitized of dangerous @@ -20,13 +18,22 @@ module Banzai cache_key = context.delete(:cache_key) cache_key = full_cache_key(cache_key, context[:pipeline]) - if cache_key && CACHE_ENABLED - Rails.cache.fetch(cache_key) do - cacheless_render(text, context) + cacheless = cacheless_render(text, context) + + if cache_key && ENV["DEBUG_BANZAI_CACHE"] + cached = Rails.cache.fetch(cache_key) { cacheless } + + if cached != cacheless + Rails.logger.warn "Banzai cache mismatch" + Rails.logger.warn "Text: #{text.inspect}" + Rails.logger.warn "Context: #{context.inspect}" + Rails.logger.warn "Cache key: #{cache_key.inspect}" + Rails.logger.warn "Cacheless: #{cacheless.inspect}" + Rails.logger.warn "With cache: #{cached.inspect}" end - else - cacheless_render(text, context) end + + cacheless end def self.render_result(text, context = {}) -- cgit v1.2.1 From 79ec7f289748ca5812d51c3a61e3a2f9c2464fda Mon Sep 17 00:00:00 2001 From: Steve Norman Date: Tue, 5 May 2015 15:13:11 +0000 Subject: Added system hooks messages for renaming and transferring a project --- CHANGELOG | 1 + app/models/project.rb | 6 ++++ app/services/projects/transfer_service.rb | 3 ++ app/services/system_hooks_service.rb | 11 ++++++- doc/system_hooks/system_hooks.md | 50 +++++++++++++++++++++++++++++- spec/services/system_hooks_service_spec.rb | 47 +++++++++++++++++++--------- 6 files changed, 102 insertions(+), 16 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b25e0eabd7d..d0e065ac6c2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,7 @@ v 8.4.0 (unreleased) - Add link to merge request on build detail page - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) + - Add system hook messages for project rename and transfer (Steve Norman) - Fix version check image in Safari v 8.3.3 (unreleased) diff --git a/app/models/project.rb b/app/models/project.rb index 017471995ec..eadc42d1da5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,6 +81,7 @@ class Project < ActiveRecord::Base acts_as_taggable_on :tags attr_accessor :new_default_branch + attr_accessor :old_path_with_namespace # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' @@ -701,6 +702,11 @@ class Project < ActiveRecord::Base gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") send_move_instructions(old_path_with_namespace) reset_events_cache + + @old_path_with_namespace = old_path_with_namespace + + SystemHooksService.new.execute_hooks_for(self, :rename) + @repository = nil rescue # Returning false does not rollback after_* transaction but gives diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 64ea6dd42eb..2e734654466 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -55,6 +55,9 @@ module Projects # Move uploads Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) + project.old_path_with_namespace = old_path + + SystemHooksService.new.execute_hooks_for(project, :transfer) true end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 8b5143e1eb7..6dc854ec33d 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -18,7 +18,8 @@ class SystemHooksService def build_event_data(model, event) data = { event_name: build_event_name(model, event), - created_at: model.created_at.xmlschema + created_at: model.created_at.xmlschema, + updated_at: model.updated_at.xmlschema } case model @@ -34,6 +35,14 @@ class SystemHooksService end when Project data.merge!(project_data(model)) + + if event == :rename || event == :transfer + data.merge!({ + old_path_with_namespace: model.old_path_with_namespace + }) + end + + data when User data.merge!({ name: model.name, diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 5cb05b13b3e..49f98ded046 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -1,6 +1,6 @@ # System hooks -Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. +Your GitLab instance can perform HTTP POST requests on the following events: `project_create`, `project_destroy`, `project_rename`, `project_transfer`, `user_add_to_team`, `user_remove_from_team`, `user_create`, `user_destroy`, `key_create`, `key_destroy`, `group_create`, `group_destroy`, `user_add_to_group` and `user_remove_from_group`. System hooks can be used, e.g. for logging or changing information in a LDAP server. @@ -17,6 +17,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:54Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_create", "name": "StoreCloud", "owner_email": "johnsmith@gmail.com", @@ -33,6 +34,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:58Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "project_destroy", "name": "Underscore", "owner_email": "johnsmith@gmail.com", @@ -44,11 +46,48 @@ X-Gitlab-Event: System Hook } ``` +**Project renamed:** + +```json +{ + "created_at": "2012-07-21T07:30:58Z", + "updated_at": "2012-07-21T07:38:22Z", + "event_name": "project_rename", + "name": "Underscore", + "path": "underscore", + "path_with_namespace": "jsmith/underscore", + "project_id": 73, + "owner_name": "John Smith", + "owner_email": "johnsmith@gmail.com", + "project_visibility": "internal", + "old_path_with_namespace": "jsmith/overscore", +} +``` + +**Project transferred:** + +```json +{ + "created_at": "2012-07-21T07:30:58Z", + "updated_at": "2012-07-21T07:38:22Z", + "event_name": "project_transfer", + "name": "Underscore", + "path": "underscore", + "path_with_namespace": "scores/underscore", + "project_id": 73, + "owner_name": "John Smith", + "owner_email": "johnsmith@gmail.com", + "project_visibility": "internal", + "old_path_with_namespace": "jsmith/overscore", +} +``` + **New Team Member:** ```json { "created_at": "2012-07-21T07:30:56Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_add_to_team", "project_access": "Master", "project_id": 74, @@ -67,6 +106,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:56Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_remove_from_team", "project_access": "Master", "project_id": 74, @@ -85,6 +125,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:44:07Z", + "updated_at": "2012-07-21T07:38:22Z", "email": "js@gitlabhq.com", "event_name": "user_create", "name": "John Smith", @@ -97,6 +138,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:44:07Z", + "updated_at": "2012-07-21T07:38:22Z", "email": "js@gitlabhq.com", "event_name": "user_destroy", "name": "John Smith", @@ -110,6 +152,7 @@ X-Gitlab-Event: System Hook { "event_name": "key_create", "created_at": "2014-08-18 18:45:16 UTC", + "updated_at": "2012-07-21T07:38:22Z", "username": "root", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "id": 4 @@ -122,6 +165,7 @@ X-Gitlab-Event: System Hook { "event_name": "key_destroy", "created_at": "2014-08-18 18:45:16 UTC", + "updated_at": "2012-07-21T07:38:22Z", "username": "root", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC58FwqHUbebw2SdT7SP4FxZ0w+lAO/erhy2ylhlcW/tZ3GY3mBu9VeeiSGoGz8hCx80Zrz+aQv28xfFfKlC8XQFpCWwsnWnQqO2Lv9bS8V1fIHgMxOHIt5Vs+9CAWGCCvUOAurjsUDoE2ALIXLDMKnJxcxD13XjWdK54j6ZXDB4syLF0C2PnAQSVY9X7MfCYwtuFmhQhKaBussAXpaVMRHltie3UYSBUUuZaB3J4cg/7TxlmxcNd+ppPRIpSZAB0NI6aOnqoBCpimscO/VpQRJMVLr3XiSYeT6HBiDXWHnIVPfQc03OGcaFqOit6p8lYKMaP/iUQLm+pgpZqrXZ9vB john@localhost", "id": 4 @@ -133,6 +177,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:54Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_create", "name": "StoreCloud", "owner_email": "johnsmith@gmail.com", @@ -147,6 +192,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:54Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "group_destroy", "name": "StoreCloud", "owner_email": "johnsmith@gmail.com", @@ -161,6 +207,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:56Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_add_to_group", "group_access": "Master", "group_id": 78, @@ -176,6 +223,7 @@ X-Gitlab-Event: System Hook ```json { "created_at": "2012-07-21T07:30:56Z", + "updated_at": "2012-07-21T07:38:22Z", "event_name": "user_remove_from_group", "group_access": "Master", "group_id": 78, diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index febc78d2784..4455ae7b321 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -9,37 +9,54 @@ describe SystemHooksService, services: true do let(:group_member) { create(:group_member) } context 'event data' do - it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :email, :user_id) } - it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) } - it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) } + it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) } + it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } + it do + project.old_path_with_namespace = 'renamed_from_path' + expect(event_data(project, :rename)).to include( + :event_name, :name, :created_at, :updated_at, :path, :project_id, + :owner_name, :owner_email, :project_visibility, + :old_path_with_namespace + ) + end + it do + project.old_path_with_namespace = 'transfered_from_path' + expect(event_data(project, :transfer)).to include( + :event_name, :name, :created_at, :updated_at, :path, :project_id, + :owner_name, :owner_email, :project_visibility, + :old_path_with_namespace + ) + end + it do expect(event_data(group, :create)).to include( - :event_name, :name, :created_at, :path, :group_id, :owner_name, - :owner_email + :event_name, :name, :created_at, :updated_at, :path, :group_id, + :owner_name, :owner_email ) end it do expect(event_data(group, :destroy)).to include( - :event_name, :name, :created_at, :path, :group_id, :owner_name, - :owner_email + :event_name, :name, :created_at, :updated_at, :path, :group_id, + :owner_name, :owner_email ) end it do expect(event_data(group_member, :create)).to include( - :event_name, :created_at, :group_name, :group_path, :group_id, :user_id, - :user_name, :user_email, :group_access + :event_name, :created_at, :updated_at, :group_name, :group_path, + :group_id, :user_id, :user_name, :user_email, :group_access ) end it do expect(event_data(group_member, :destroy)).to include( - :event_name, :created_at, :group_name, :group_path, :group_id, :user_id, - :user_name, :user_email, :group_access + :event_name, :created_at, :updated_at, :group_name, :group_path, + :group_id, :user_id, :user_name, :user_email, :group_access ) end end @@ -49,6 +66,8 @@ describe SystemHooksService, services: true do it { expect(event_name(user, :destroy)).to eq "user_destroy" } it { expect(event_name(project, :create)).to eq "project_create" } it { expect(event_name(project, :destroy)).to eq "project_destroy" } + it { expect(event_name(project, :rename)).to eq "project_rename" } + it { expect(event_name(project, :transfer)).to eq "project_transfer" } it { expect(event_name(project_member, :create)).to eq "user_add_to_team" } it { expect(event_name(project_member, :destroy)).to eq "user_remove_from_team" } it { expect(event_name(key, :create)).to eq 'key_create' } -- cgit v1.2.1 From 8b1844912561a7e6dd0cc361ea1514f2a340e275 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Mon, 28 Dec 2015 13:32:18 +0200 Subject: remove public field from namespace and refactoring --- app/controllers/explore/groups_controller.rb | 2 +- app/controllers/users_controller.rb | 2 +- app/finders/groups_finder.rb | 44 ------------------- app/finders/joined_groups_finder.rb | 49 ---------------------- app/helpers/search_helper.rb | 2 +- app/models/ability.rb | 2 +- app/models/group.rb | 8 ---- app/views/groups/edit.html.haml | 9 ---- app/views/groups/show.html.haml | 4 +- .../20151228111122_remove_public_from_namespace.rb | 6 +++ db/schema.rb | 8 ++-- features/explore/groups.feature | 15 ------- spec/finders/groups_finder_spec.rb | 48 --------------------- spec/finders/joined_groups_finder_spec.rb | 49 ---------------------- spec/helpers/search_helper_spec.rb | 2 +- spec/models/group_spec.rb | 27 ------------ 16 files changed, 16 insertions(+), 261 deletions(-) delete mode 100644 app/finders/groups_finder.rb delete mode 100644 app/finders/joined_groups_finder.rb create mode 100644 db/migrate/20151228111122_remove_public_from_namespace.rb delete mode 100644 spec/finders/groups_finder_spec.rb delete mode 100644 spec/finders/joined_groups_finder_spec.rb diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 9575a87ee41..a9bf4321f73 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -1,6 +1,6 @@ class Explore::GroupsController < Explore::ApplicationController def index - @groups = GroupsFinder.new.execute(current_user) + @groups = Group.order_id_desc @groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.page(params[:page]).per(PER_PAGE) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 30cb869eb2a..280228dbcc0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -7,7 +7,7 @@ class UsersController < ApplicationController @projects = PersonalProjectsFinder.new(@user).execute(current_user) - @groups = JoinedGroupsFinder.new(@user).execute(current_user) + @groups = @user.groups.order_id_desc respond_to do |format| format.html diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb deleted file mode 100644 index 91cb0f228f0..00000000000 --- a/app/finders/groups_finder.rb +++ /dev/null @@ -1,44 +0,0 @@ -class GroupsFinder - # Finds the groups available to the given user. - # - # current_user - The user to find the groups for. - # - # Returns an ActiveRecord::Relation. - def execute(current_user = nil) - if current_user - relation = groups_visible_to_user(current_user) - else - relation = public_groups - end - - relation.order_id_desc - end - - private - - # This method returns the groups "current_user" can see. - def groups_visible_to_user(current_user) - base = groups_for_projects(public_and_internal_projects) - - union = Gitlab::SQL::Union. - new([base.select(:id), current_user.authorized_groups.select(:id)]) - - Group.where("namespaces.id IN (#{union.to_sql})") - end - - def public_groups - groups_for_projects(public_projects) - end - - def groups_for_projects(projects) - Group.public_and_given_groups(projects.select(:namespace_id)) - end - - def public_projects - Project.unscoped.public_only - end - - def public_and_internal_projects - Project.unscoped.public_and_internal_only - end -end diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb deleted file mode 100644 index e7523136fea..00000000000 --- a/app/finders/joined_groups_finder.rb +++ /dev/null @@ -1,49 +0,0 @@ -# Class for finding the groups a user is a member of. -class JoinedGroupsFinder - def initialize(user = nil) - @user = user - end - - # Finds the groups of the source user, optionally limited to those visible to - # the current user. - # - # current_user - If given the groups of "@user" will only include the groups - # "current_user" can also see. - # - # Returns an ActiveRecord::Relation. - def execute(current_user = nil) - if current_user - relation = groups_visible_to_user(current_user) - else - relation = public_groups - end - - relation.order_id_desc - end - - private - - # Returns the groups the user in "current_user" can see. - # - # This list includes all public/internal projects as well as the projects of - # "@user" that "current_user" also has access to. - def groups_visible_to_user(current_user) - base = @user.authorized_groups.visible_to_user(current_user) - extra = public_and_internal_groups - union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)]) - - Group.where("namespaces.id IN (#{union.to_sql})") - end - - def public_groups - groups_for_projects(@user.authorized_projects.public_only) - end - - def public_and_internal_groups - groups_for_projects(@user.authorized_projects.public_and_internal_only) - end - - def groups_for_projects(projects) - @user.groups.public_and_given_groups(projects.select(:namespace_id)) - end -end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index a6ee6880247..d4f78258626 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) - GroupsFinder.new.execute(current_user).search(term).limit(limit).map do |group| + Group.search(term).limit(limit).map do |group| { label: "group: #{search_result_sanitize(group.name)}", url: group_path(group) diff --git a/app/models/ability.rb b/app/models/ability.rb index 1b3ee757040..5a1a67db8e1 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -69,7 +69,7 @@ class Ability subject.group end - if group && group.public_profile? + if group && group.projects.public_only.any? [:read_group] else [] diff --git a/app/models/group.rb b/app/models/group.rb index 1b5b875a19e..b8f2ab6ae5d 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -50,10 +50,6 @@ class Group < Namespace User.reference_pattern end - def public_and_given_groups(ids) - where('public IS TRUE OR namespaces.id IN (?)', ids) - end - def visible_to_user(user) where(id: user.authorized_groups.select(:id).reorder(nil)) end @@ -125,10 +121,6 @@ class Group < Namespace end end - def public_profile? - self.public || projects.public_only.any? - end - def post_create_hook Gitlab::AppLogger.info("Group \"#{name}\" was created") diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 1dea77c2e96..7e3e2e28bc9 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -24,15 +24,6 @@ %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 are no public projects inside this group) - .form-actions = f.submit 'Save group', class: "btn btn-save" diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index c2c7c581b3e..a607d860d7d 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -47,5 +47,5 @@ = render "projects", projects: @projects - else - %p - This group does not have public projects + %p.center-top-menu.no-top + No projects to show diff --git a/db/migrate/20151228111122_remove_public_from_namespace.rb b/db/migrate/20151228111122_remove_public_from_namespace.rb new file mode 100644 index 00000000000..f4c848bbf47 --- /dev/null +++ b/db/migrate/20151228111122_remove_public_from_namespace.rb @@ -0,0 +1,6 @@ +# Migration type: online +class RemovePublicFromNamespace < ActiveRecord::Migration + def change + remove_column :namespaces, :public, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index df7f72d5ad4..7a6d34b8153 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -547,22 +547,20 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree create_table "namespaces", force: :cascade 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 add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree - add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree create_table "notes", force: :cascade do |t| diff --git a/features/explore/groups.feature b/features/explore/groups.feature index a42e59c98f2..5fc9b135601 100644 --- a/features/explore/groups.feature +++ b/features/explore/groups.feature @@ -105,15 +105,6 @@ Feature: Explore Groups When I visit the public groups area Then I should see group "TestGroup" - Scenario: I should not see group with internal project in public groups area - Given group "TestGroup" has internal project "Internal" - When I visit the public groups area - Then I should not see group "TestGroup" - - Scenario: I should not see group with private project in public groups area - When I visit the public groups area - Then I should not see group "TestGroup" - Scenario: I should see group with public project in public groups area as user Given group "TestGroup" has public project "Community" When I sign in as a user @@ -125,9 +116,3 @@ Feature: Explore Groups When I sign in as a user And I visit the public groups area Then I should see group "TestGroup" - - Scenario: I should not see group with private project in public groups area as user - When I sign in as a user - And I visit the public groups area - Then I should not see group "TestGroup" - diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb deleted file mode 100644 index 4f6a000822e..00000000000 --- a/spec/finders/groups_finder_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'spec_helper' - -describe GroupsFinder do - describe '#execute' do - let(:user) { create(:user) } - - let(:group1) { create(:group) } - let(:group2) { create(:group) } - let(:group3) { create(:group) } - let(:group4) { create(:group, public: true) } - - let!(:public_project) { create(:project, :public, group: group1) } - let!(:internal_project) { create(:project, :internal, group: group2) } - let!(:private_project) { create(:project, :private, group: group3) } - - let(:finder) { described_class.new } - - describe 'with a user' do - subject { finder.execute(user) } - - describe 'when the user is not a member of any groups' do - it { is_expected.to eq([group4, group2, group1]) } - end - - describe 'when the user is a member of a group' do - before do - group3.add_user(user, Gitlab::Access::DEVELOPER) - end - - it { is_expected.to eq([group4, group3, group2, group1]) } - end - - describe 'when the user is a member of a private project' do - before do - private_project.team.add_user(user, Gitlab::Access::DEVELOPER) - end - - it { is_expected.to eq([group4, group3, group2, group1]) } - end - end - - describe 'without a user' do - subject { finder.execute } - - it { is_expected.to eq([group4, group1]) } - end - end -end diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb deleted file mode 100644 index 2d9068cc720..00000000000 --- a/spec/finders/joined_groups_finder_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe JoinedGroupsFinder do - describe '#execute' do - let(:source_user) { create(:user) } - let(:current_user) { create(:user) } - - let(:group1) { create(:group) } - let(:group2) { create(:group) } - let(:group3) { create(:group) } - let(:group4) { create(:group, public: true) } - - let!(:public_project) { create(:project, :public, group: group1) } - let!(:internal_project) { create(:project, :internal, group: group2) } - let!(:private_project) { create(:project, :private, group: group3) } - - let(:finder) { described_class.new(source_user) } - - before do - [group1, group2, group3, group4].each do |group| - group.add_user(source_user, Gitlab::Access::MASTER) - end - end - - describe 'with a current user' do - describe 'when the current user has access to the projects of the source user' do - before do - private_project.team.add_user(current_user, Gitlab::Access::DEVELOPER) - end - - subject { finder.execute(current_user) } - - it { is_expected.to eq([group4, group3, group2, group1]) } - end - - describe 'when the current user does not have access to the projects of the source user' do - subject { finder.execute(current_user) } - - it { is_expected.to eq([group4, group2, group1]) } - end - end - - describe 'without a current user' do - subject { finder.execute } - - it { is_expected.to eq([group4, group1]) } - end - end -end diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index ebe9c29d91c..f0d553f5f1d 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -43,7 +43,7 @@ describe SearchHelper do end it "includes the public group" do - group = create(:group, public: true) + group = create(:group) expect(search_autocomplete_opts(group.name).size).to eq(1) end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 646f767e6fe..ba5acceadff 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -38,14 +38,6 @@ describe Group, models: true do it { is_expected.not_to validate_presence_of :owner } end - describe '.public_and_given_groups' do - let!(:public_group) { create(:group, public: true) } - - subject { described_class.public_and_given_groups([group.id]) } - - it { is_expected.to eq([public_group, group]) } - end - describe '.visible_to_user' do let!(:group) { create(:group) } let!(:user) { create(:user) } @@ -112,23 +104,4 @@ describe Group, models: true do expect(group.avatar_type).to eq(["only images allowed"]) end end - - describe "public_profile?" do - it "returns true for public group" do - group = create(:group, public: true) - expect(group.public_profile?).to be_truthy - end - - it "returns true for non-public group with public project" do - group = create(:group) - create(:project, :public, group: group) - expect(group.public_profile?).to be_truthy - end - - it "returns false for non-public group with no public projects" do - group = create(:group) - create(:project, group: group) - expect(group.public_profile?).to be_falsy - end - end end -- cgit v1.2.1 From 712af98e9c11b28a11fb31fc86af5ef83a5485cb Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 4 Jan 2016 09:07:50 -0500 Subject: removes footer message about access to project --- app/views/projects/show.html.haml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 5d04776f7f4..ffbe445b447 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -68,11 +68,4 @@ = render 'projects/last_commit', commit: @repository.commit, project: @project %div{class: "project-show-#{default_project_view}"} - = render default_project_view - -- if current_user - - access = user_max_access_in_project(current_user.id, @project) - - if access - .prepend-top-20.project-footer - .gray-content-block.footer-block.center - You have #{access} access to this project. \ No newline at end of file + = render default_project_view \ No newline at end of file -- cgit v1.2.1 From 770517d3e7f9926957b384084ab726651bfecaea Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 4 Jan 2016 09:42:10 -0500 Subject: Revert "Merge branch 'rs-remove-jquery-blockui' into 'master' " This reverts commit bc12750fcc7a8637771e1449493cdd3cee9ebd64, reversing changes made to 8a04b84e09c4318ac46808d0debc4997b52a2314. --- app/assets/javascripts/application.js.coffee | 1 + vendor/assets/javascripts/jquery.blockUI.js | 590 +++++++++++++++++++++++++++ 2 files changed, 591 insertions(+) create mode 100644 vendor/assets/javascripts/jquery.blockUI.js diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index b9b095e004a..7d3f18fcdbe 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -13,6 +13,7 @@ #= require jquery.waitforimages #= require jquery.atwho #= require jquery.scrollTo +#= require jquery.blockUI #= require jquery.turbolinks #= require d3 #= require cal-heatmap diff --git a/vendor/assets/javascripts/jquery.blockUI.js b/vendor/assets/javascripts/jquery.blockUI.js new file mode 100644 index 00000000000..c8702d79b65 --- /dev/null +++ b/vendor/assets/javascripts/jquery.blockUI.js @@ -0,0 +1,590 @@ +/*! + * jQuery blockUI plugin + * Version 2.60.0-2013.04.05 + * @requires jQuery v1.7 or later + * + * Examples at: http://malsup.com/jquery/block/ + * Copyright (c) 2007-2013 M. Alsup + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * Thanks to Amir-Hossein Sobhi for some excellent contributions! + */ + +;(function() { +/*jshint eqeqeq:false curly:false latedef:false */ +"use strict"; + + function setup($) { + $.fn._fadeIn = $.fn.fadeIn; + + var noOp = $.noop || function() {}; + + // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle + // retarded userAgent strings on Vista) + var msie = /MSIE/.test(navigator.userAgent); + var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent); + var mode = document.documentMode || 0; + var setExpr = $.isFunction( document.createElement('div').style.setExpression ); + + // global $ methods for blocking/unblocking the entire page + $.blockUI = function(opts) { install(window, opts); }; + $.unblockUI = function(opts) { remove(window, opts); }; + + // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) + $.growlUI = function(title, message, timeout, onClose) { + var $m = $('
'); + if (title) $m.append('

'+title+'

'); + if (message) $m.append('

'+message+'

'); + if (timeout === undefined) timeout = 3000; + $.blockUI({ + message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, + timeout: timeout, showOverlay: false, + onUnblock: onClose, + css: $.blockUI.defaults.growlCSS + }); + }; + + // plugin method for blocking element content + $.fn.block = function(opts) { + if ( this[0] === window ) { + $.blockUI( opts ); + return this; + } + var fullOpts = $.extend({}, $.blockUI.defaults, opts || {}); + this.each(function() { + var $el = $(this); + if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked')) + return; + $el.unblock({ fadeOut: 0 }); + }); + + return this.each(function() { + if ($.css(this,'position') == 'static') { + this.style.position = 'relative'; + $(this).data('blockUI.static', true); + } + this.style.zoom = 1; // force 'hasLayout' in ie + install(this, opts); + }); + }; + + // plugin method for unblocking element content + $.fn.unblock = function(opts) { + if ( this[0] === window ) { + $.unblockUI( opts ); + return this; + } + return this.each(function() { + remove(this, opts); + }); + }; + + $.blockUI.version = 2.60; // 2nd generation blocking at no extra cost! + + // override these in your code to change the default behavior and style + $.blockUI.defaults = { + // message displayed when blocking (use null for no message) + message: '

Please wait...

', + + title: null, // title string; only used when theme == true + draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) + + theme: false, // set to true to use with jQuery UI themes + + // styles for the message when blocking; if you wish to disable + // these and use an external stylesheet then do this in your code: + // $.blockUI.defaults.css = {}; + css: { + padding: 0, + margin: 0, + width: '30%', + top: '40%', + left: '35%', + textAlign: 'center', + color: '#000', + border: '3px solid #aaa', + backgroundColor:'#fff', + cursor: 'wait' + }, + + // minimal style set used when themes are used + themedCSS: { + width: '30%', + top: '40%', + left: '35%' + }, + + // styles for the overlay + overlayCSS: { + backgroundColor: '#000', + opacity: 0.6, + cursor: 'wait' + }, + + // style to replace wait cursor before unblocking to correct issue + // of lingering wait cursor + cursorReset: 'default', + + // styles applied when using $.growlUI + growlCSS: { + width: '350px', + top: '10px', + left: '', + right: '10px', + border: 'none', + padding: '5px', + opacity: 0.6, + cursor: 'default', + color: '#fff', + backgroundColor: '#000', + '-webkit-border-radius':'10px', + '-moz-border-radius': '10px', + 'border-radius': '10px' + }, + + // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w + // (hat tip to Jorge H. N. de Vasconcelos) + /*jshint scripturl:true */ + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', + + // force usage of iframe in non-IE browsers (handy for blocking applets) + forceIframe: false, + + // z-index for the blocking overlay + baseZ: 1000, + + // set these to true to have the message automatically centered + centerX: true, // <-- only effects element blocking (page block controlled via css above) + centerY: true, + + // allow body element to be stetched in ie6; this makes blocking look better + // on "short" pages. disable if you wish to prevent changes to the body height + allowBodyStretch: true, + + // enable if you want key and mouse events to be disabled for content that is blocked + bindEvents: true, + + // be default blockUI will supress tab navigation from leaving blocking content + // (if bindEvents is true) + constrainTabKey: true, + + // fadeIn time in millis; set to 0 to disable fadeIn on block + fadeIn: 200, + + // fadeOut time in millis; set to 0 to disable fadeOut on unblock + fadeOut: 400, + + // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock + timeout: 0, + + // disable if you don't want to show the overlay + showOverlay: true, + + // if true, focus will be placed in the first available input field when + // page blocking + focusInput: true, + + // elements that can receive focus + focusableElements: ':input:enabled:visible', + + // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) + // no longer needed in 2012 + // applyPlatformOpacityRules: true, + + // callback method invoked when fadeIn has completed and blocking message is visible + onBlock: null, + + // callback method invoked when unblocking has completed; the callback is + // passed the element that has been unblocked (which is the window object for page + // blocks) and the options that were passed to the unblock call: + // onUnblock(element, options) + onUnblock: null, + + // callback method invoked when the overlay area is clicked. + // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used. + onOverlayClick: null, + + // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 + quirksmodeOffsetHack: 4, + + // class name of the message block + blockMsgClass: 'blockMsg', + + // if it is already blocked, then ignore it (don't unblock and reblock) + ignoreIfBlocked: false + }; + + // private data and functions follow... + + var pageBlock = null; + var pageBlockEls = []; + + function install(el, opts) { + var css, themedCSS; + var full = (el == window); + var msg = (opts && opts.message !== undefined ? opts.message : undefined); + opts = $.extend({}, $.blockUI.defaults, opts || {}); + + if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked')) + return; + + opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); + css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); + if (opts.onOverlayClick) + opts.overlayCSS.cursor = 'pointer'; + + themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); + msg = msg === undefined ? opts.message : msg; + + // remove the current block (if there is one) + if (full && pageBlock) + remove(window, {fadeOut:0}); + + // if an existing element is being used as the blocking content then we capture + // its current place in the DOM (and current display style) so we can restore + // it when we unblock + if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { + var node = msg.jquery ? msg[0] : msg; + var data = {}; + $(el).data('blockUI.history', data); + data.el = node; + data.parent = node.parentNode; + data.display = node.style.display; + data.position = node.style.position; + if (data.parent) + data.parent.removeChild(node); + } + + $(el).data('blockUI.onUnblock', opts.onUnblock); + var z = opts.baseZ; + + // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; + // layer1 is the iframe layer which is used to supress bleed through of underlying content + // layer2 is the overlay layer which has opacity and a wait cursor (by default) + // layer3 is the message content that is displayed while blocking + var lyr1, lyr2, lyr3, s; + if (msie || opts.forceIframe) + lyr1 = $(''); + else + lyr1 = $(''); + + if (opts.theme) + lyr2 = $(''); + else + lyr2 = $(''); + + if (opts.theme && full) { + s = ''; + } + else if (opts.theme) { + s = ''; + } + else if (full) { + s = ''; + } + else { + s = ''; + } + lyr3 = $(s); + + // if we have a message, style it + if (msg) { + if (opts.theme) { + lyr3.css(themedCSS); + lyr3.addClass('ui-widget-content'); + } + else + lyr3.css(css); + } + + // style the overlay + if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/) + lyr2.css(opts.overlayCSS); + lyr2.css('position', full ? 'fixed' : 'absolute'); + + // make iframe layer transparent in IE + if (msie || opts.forceIframe) + lyr1.css('opacity',0.0); + + //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); + var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); + $.each(layers, function() { + this.appendTo($par); + }); + + if (opts.theme && opts.draggable && $.fn.draggable) { + lyr3.draggable({ + handle: '.ui-dialog-titlebar', + cancel: 'li' + }); + } + + // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) + var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0); + if (ie6 || expr) { + // give body 100% height + if (full && opts.allowBodyStretch && $.support.boxModel) + $('html,body').css('height','100%'); + + // fix ie6 issue when blocked element has a border width + if ((ie6 || !$.support.boxModel) && !full) { + var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); + var fixT = t ? '(0 - '+t+')' : 0; + var fixL = l ? '(0 - '+l+')' : 0; + } + + // simulate fixed position + $.each(layers, function(i,o) { + var s = o[0].style; + s.position = 'absolute'; + if (i < 2) { + if (full) + s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"'); + else + s.setExpression('height','this.parentNode.offsetHeight + "px"'); + if (full) + s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'); + else + s.setExpression('width','this.parentNode.offsetWidth + "px"'); + if (fixL) s.setExpression('left', fixL); + if (fixT) s.setExpression('top', fixT); + } + else if (opts.centerY) { + if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); + s.marginTop = 0; + } + else if (!opts.centerY && full) { + var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0; + var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; + s.setExpression('top',expression); + } + }); + } + + // show the message + if (msg) { + if (opts.theme) + lyr3.find('.ui-widget-content').append(msg); + else + lyr3.append(msg); + if (msg.jquery || msg.nodeType) + $(msg).show(); + } + + if ((msie || opts.forceIframe) && opts.showOverlay) + lyr1.show(); // opacity is zero + if (opts.fadeIn) { + var cb = opts.onBlock ? opts.onBlock : noOp; + var cb1 = (opts.showOverlay && !msg) ? cb : noOp; + var cb2 = msg ? cb : noOp; + if (opts.showOverlay) + lyr2._fadeIn(opts.fadeIn, cb1); + if (msg) + lyr3._fadeIn(opts.fadeIn, cb2); + } + else { + if (opts.showOverlay) + lyr2.show(); + if (msg) + lyr3.show(); + if (opts.onBlock) + opts.onBlock(); + } + + // bind key and mouse events + bind(1, el, opts); + + if (full) { + pageBlock = lyr3[0]; + pageBlockEls = $(opts.focusableElements,pageBlock); + if (opts.focusInput) + setTimeout(focus, 20); + } + else + center(lyr3[0], opts.centerX, opts.centerY); + + if (opts.timeout) { + // auto-unblock + var to = setTimeout(function() { + if (full) + $.unblockUI(opts); + else + $(el).unblock(opts); + }, opts.timeout); + $(el).data('blockUI.timeout', to); + } + } + + // remove the block + function remove(el, opts) { + var count; + var full = (el == window); + var $el = $(el); + var data = $el.data('blockUI.history'); + var to = $el.data('blockUI.timeout'); + if (to) { + clearTimeout(to); + $el.removeData('blockUI.timeout'); + } + opts = $.extend({}, $.blockUI.defaults, opts || {}); + bind(0, el, opts); // unbind events + + if (opts.onUnblock === null) { + opts.onUnblock = $el.data('blockUI.onUnblock'); + $el.removeData('blockUI.onUnblock'); + } + + var els; + if (full) // crazy selector to handle odd field errors in ie6/7 + els = $('body').children().filter('.blockUI').add('body > .blockUI'); + else + els = $el.find('>.blockUI'); + + // fix cursor issue + if ( opts.cursorReset ) { + if ( els.length > 1 ) + els[1].style.cursor = opts.cursorReset; + if ( els.length > 2 ) + els[2].style.cursor = opts.cursorReset; + } + + if (full) + pageBlock = pageBlockEls = null; + + if (opts.fadeOut) { + count = els.length; + els.fadeOut(opts.fadeOut, function() { + if ( --count === 0) + reset(els,data,opts,el); + }); + } + else + reset(els, data, opts, el); + } + + // move blocking element back into the DOM where it started + function reset(els,data,opts,el) { + var $el = $(el); + els.each(function(i,o) { + // remove via DOM calls so we don't lose event handlers + if (this.parentNode) + this.parentNode.removeChild(this); + }); + + if (data && data.el) { + data.el.style.display = data.display; + data.el.style.position = data.position; + if (data.parent) + data.parent.appendChild(data.el); + $el.removeData('blockUI.history'); + } + + if ($el.data('blockUI.static')) { + $el.css('position', 'static'); // #22 + } + + if (typeof opts.onUnblock == 'function') + opts.onUnblock(el,opts); + + // fix issue in Safari 6 where block artifacts remain until reflow + var body = $(document.body), w = body.width(), cssW = body[0].style.width; + body.width(w-1).width(w); + body[0].style.width = cssW; + } + + // bind/unbind the handler + function bind(b, el, opts) { + var full = el == window, $el = $(el); + + // don't bother unbinding if there is nothing to unbind + if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) + return; + + $el.data('blockUI.isBlocked', b); + + // don't bind events when overlay is not in use or if bindEvents is false + if (!full || !opts.bindEvents || (b && !opts.showOverlay)) + return; + + // bind anchors and inputs for mouse and key events + var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove'; + if (b) + $(document).bind(events, opts, handler); + else + $(document).unbind(events, handler); + + // former impl... + // var $e = $('a,:input'); + // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); + } + + // event handler to suppress keyboard/mouse events when blocking + function handler(e) { + // allow tab navigation (conditionally) + if (e.keyCode && e.keyCode == 9) { + if (pageBlock && e.data.constrainTabKey) { + var els = pageBlockEls; + var fwd = !e.shiftKey && e.target === els[els.length-1]; + var back = e.shiftKey && e.target === els[0]; + if (fwd || back) { + setTimeout(function(){focus(back);},10); + return false; + } + } + } + var opts = e.data; + var target = $(e.target); + if (target.hasClass('blockOverlay') && opts.onOverlayClick) + opts.onOverlayClick(); + + // allow events within the message content + if (target.parents('div.' + opts.blockMsgClass).length > 0) + return true; + + // allow events for content that is not being blocked + return target.parents().children().filter('div.blockUI').length === 0; + } + + function focus(back) { + if (!pageBlockEls) + return; + var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; + if (e) + e.focus(); + } + + function center(el, x, y) { + var p = el.parentNode, s = el.style; + var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); + var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); + if (x) s.left = l > 0 ? (l+'px') : '0'; + if (y) s.top = t > 0 ? (t+'px') : '0'; + } + + function sz(el, p) { + return parseInt($.css(el,p),10)||0; + } + + } + + + /*global define:true */ + if (typeof define === 'function' && define.amd && define.amd.jQuery) { + define(['jquery'], setup); + } else { + setup(jQuery); + } + +})(); -- cgit v1.2.1 From bb6b793c55150ecbe072456bc4b151191764b642 Mon Sep 17 00:00:00 2001 From: Mike Wyatt Date: Mon, 4 Jan 2016 11:19:39 -0400 Subject: Don't log backtrace in Asana service --- app/models/project_services/asana_service.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index cb4f6ddb3a5..7d367e40037 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -118,7 +118,6 @@ http://app.asana.com/-/account_api' end rescue => e Rails.logger.error(e.message) - Rails.logger.error(e.backtrace.join("\n")) next end end -- cgit v1.2.1 From 618056031827727ff1928b125569dae2e05f9bd1 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Mon, 4 Jan 2016 10:36:53 -0500 Subject: rempves tests for "you have master access" text --- spec/features/projects_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 4836b3b9b14..9a01c89ae2a 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -80,7 +80,6 @@ feature 'Project', feature: true do visit namespace_project_path(project.namespace, project) end - it { expect(page).to have_content('You have Master access to this project.') } it 'click project-settings and find leave project' do find('#project-settings-button').click expect(page).to have_link('Leave Project') -- cgit v1.2.1 From d9da81f736b770bb44c4869aef5d5c455e74ab7a Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Mon, 4 Jan 2016 16:38:32 +0100 Subject: Add triggers feature to API --- lib/api/entities.rb | 10 ++++++++ lib/api/triggers.rb | 18 ++++++++++++++ spec/factories/ci/trigger_requests.rb | 2 ++ spec/requests/api/triggers_spec.rb | 45 +++++++++++++++++++++++++++++++---- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 26e7c956e8f..bc0cd76a2b8 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -365,5 +365,15 @@ module API class TriggerRequest < Grape::Entity expose :id, :variables end + + class Trigger < Grape::Entity + expose :id, :token, :created_at, :updated_at, :deleted_at + expose :last_used do |repo_obj, _options| + if repo_obj.respond_to?(:last_trigger_request) + request = repo_obj.last_trigger_request + request.created_at if request + end + end + end end end diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 2781f1cf191..9a1e3fdc976 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -43,6 +43,24 @@ module API render_api_error!(errors, 400) end end + + # Get triggers list + # + # Parameters: + # id (required) - The ID of a project + # page (optional) - The page number for pagination + # per_page (optional) - The value of items per page to show + # Example Request: + # GET /projects/:id/triggers + get ':id/triggers' do + authenticate! + authorize_admin_project + + triggers = user_project.triggers.includes(:trigger_requests) + triggers = paginate(triggers) + + present triggers, with: Entities::Trigger + end end end end diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb index db053c610cd..5298d9fa7c3 100644 --- a/spec/factories/ci/trigger_requests.rb +++ b/spec/factories/ci/trigger_requests.rb @@ -3,6 +3,8 @@ FactoryGirl.define do factory :ci_trigger_request, class: Ci::TriggerRequest do factory :ci_trigger_request_with_variables do + trigger :ci_trigger + variables do { TRIGGER_KEY: 'TRIGGER_VALUE' diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 314bd7ddc59..4b356108c80 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -3,11 +3,19 @@ require 'spec_helper' describe API::API do include ApiHelpers + let(:user) { create(:user) } + let(:user2) { create(:user) } + let!(:trigger_token) { 'secure token' } + let!(:trigger_token_2) { 'secure token 2' } + let!(:project) { create(:project, creator_id: user.id) } + let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } + let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) } + let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) } + let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2) } + let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') } + describe 'POST /projects/:project_id/trigger' do - let!(:trigger_token) { 'secure token' } - let!(:project) { FactoryGirl.create(:project) } - let!(:project2) { FactoryGirl.create(:empty_project) } - let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) } + let!(:project2) { create(:empty_project) } let(:options) do { token: trigger_token @@ -77,4 +85,33 @@ describe API::API do end end end + + describe 'GET /projects/:id/triggets' do + context 'authenticated user with valid permissions' do + it 'should return list of triggers' do + get api("/projects/#{project.id}/triggers", user) + + expect(response.status).to eq(200) + expect(json_response).to be_a(Array) + expect(json_response[0]['token']).to eq(trigger_token) + expect(json_response[1]['token']).to eq(trigger_token_2) + end + end + + context 'authenticated user with invalid permissions' do + it 'should not return triggers list' do + get api("/projects/#{project.id}/triggers", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthentikated user' do + it 'should not return triggers list' do + get api("/projects/#{project.id}/triggers") + + expect(response.status).to eq(401) + end + end + end end -- cgit v1.2.1 From ad42441d2dc98dbf385e54c352a6a5a38155b223 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 1 Jan 2016 17:52:40 -0500 Subject: Remove jquery.blockUI.js plugin It was only used to block the issue (but not merge request) list when the sort was changed. --- app/assets/javascripts/application.js.coffee | 1 - app/assets/javascripts/issues.js.coffee | 7 - vendor/assets/javascripts/jquery.blockUI.js | 590 --------------------------- 3 files changed, 598 deletions(-) delete mode 100644 vendor/assets/javascripts/jquery.blockUI.js diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 7d3f18fcdbe..b9b095e004a 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -13,7 +13,6 @@ #= require jquery.waitforimages #= require jquery.atwho #= require jquery.scrollTo -#= require jquery.blockUI #= require jquery.turbolinks #= require d3 #= require cal-heatmap diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 35d34b20fae..a0acf3028bf 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -15,13 +15,6 @@ $(this).html totalIssues + 1 else $(this).html totalIssues - 1 - $("body").on "click", ".issues-other-filters .dropdown-menu a", -> - $('.issues-list').block( - message: null, - overlayCSS: - backgroundColor: '#DDD' - opacity: .4 - ) reload: -> Issues.initSelects() diff --git a/vendor/assets/javascripts/jquery.blockUI.js b/vendor/assets/javascripts/jquery.blockUI.js deleted file mode 100644 index c8702d79b65..00000000000 --- a/vendor/assets/javascripts/jquery.blockUI.js +++ /dev/null @@ -1,590 +0,0 @@ -/*! - * jQuery blockUI plugin - * Version 2.60.0-2013.04.05 - * @requires jQuery v1.7 or later - * - * Examples at: http://malsup.com/jquery/block/ - * Copyright (c) 2007-2013 M. Alsup - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - * Thanks to Amir-Hossein Sobhi for some excellent contributions! - */ - -;(function() { -/*jshint eqeqeq:false curly:false latedef:false */ -"use strict"; - - function setup($) { - $.fn._fadeIn = $.fn.fadeIn; - - var noOp = $.noop || function() {}; - - // this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle - // retarded userAgent strings on Vista) - var msie = /MSIE/.test(navigator.userAgent); - var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent); - var mode = document.documentMode || 0; - var setExpr = $.isFunction( document.createElement('div').style.setExpression ); - - // global $ methods for blocking/unblocking the entire page - $.blockUI = function(opts) { install(window, opts); }; - $.unblockUI = function(opts) { remove(window, opts); }; - - // convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) - $.growlUI = function(title, message, timeout, onClose) { - var $m = $('
'); - if (title) $m.append('

'+title+'

'); - if (message) $m.append('

'+message+'

'); - if (timeout === undefined) timeout = 3000; - $.blockUI({ - message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, - timeout: timeout, showOverlay: false, - onUnblock: onClose, - css: $.blockUI.defaults.growlCSS - }); - }; - - // plugin method for blocking element content - $.fn.block = function(opts) { - if ( this[0] === window ) { - $.blockUI( opts ); - return this; - } - var fullOpts = $.extend({}, $.blockUI.defaults, opts || {}); - this.each(function() { - var $el = $(this); - if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked')) - return; - $el.unblock({ fadeOut: 0 }); - }); - - return this.each(function() { - if ($.css(this,'position') == 'static') { - this.style.position = 'relative'; - $(this).data('blockUI.static', true); - } - this.style.zoom = 1; // force 'hasLayout' in ie - install(this, opts); - }); - }; - - // plugin method for unblocking element content - $.fn.unblock = function(opts) { - if ( this[0] === window ) { - $.unblockUI( opts ); - return this; - } - return this.each(function() { - remove(this, opts); - }); - }; - - $.blockUI.version = 2.60; // 2nd generation blocking at no extra cost! - - // override these in your code to change the default behavior and style - $.blockUI.defaults = { - // message displayed when blocking (use null for no message) - message: '

Please wait...

', - - title: null, // title string; only used when theme == true - draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) - - theme: false, // set to true to use with jQuery UI themes - - // styles for the message when blocking; if you wish to disable - // these and use an external stylesheet then do this in your code: - // $.blockUI.defaults.css = {}; - css: { - padding: 0, - margin: 0, - width: '30%', - top: '40%', - left: '35%', - textAlign: 'center', - color: '#000', - border: '3px solid #aaa', - backgroundColor:'#fff', - cursor: 'wait' - }, - - // minimal style set used when themes are used - themedCSS: { - width: '30%', - top: '40%', - left: '35%' - }, - - // styles for the overlay - overlayCSS: { - backgroundColor: '#000', - opacity: 0.6, - cursor: 'wait' - }, - - // style to replace wait cursor before unblocking to correct issue - // of lingering wait cursor - cursorReset: 'default', - - // styles applied when using $.growlUI - growlCSS: { - width: '350px', - top: '10px', - left: '', - right: '10px', - border: 'none', - padding: '5px', - opacity: 0.6, - cursor: 'default', - color: '#fff', - backgroundColor: '#000', - '-webkit-border-radius':'10px', - '-moz-border-radius': '10px', - 'border-radius': '10px' - }, - - // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w - // (hat tip to Jorge H. N. de Vasconcelos) - /*jshint scripturl:true */ - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', - - // force usage of iframe in non-IE browsers (handy for blocking applets) - forceIframe: false, - - // z-index for the blocking overlay - baseZ: 1000, - - // set these to true to have the message automatically centered - centerX: true, // <-- only effects element blocking (page block controlled via css above) - centerY: true, - - // allow body element to be stetched in ie6; this makes blocking look better - // on "short" pages. disable if you wish to prevent changes to the body height - allowBodyStretch: true, - - // enable if you want key and mouse events to be disabled for content that is blocked - bindEvents: true, - - // be default blockUI will supress tab navigation from leaving blocking content - // (if bindEvents is true) - constrainTabKey: true, - - // fadeIn time in millis; set to 0 to disable fadeIn on block - fadeIn: 200, - - // fadeOut time in millis; set to 0 to disable fadeOut on unblock - fadeOut: 400, - - // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock - timeout: 0, - - // disable if you don't want to show the overlay - showOverlay: true, - - // if true, focus will be placed in the first available input field when - // page blocking - focusInput: true, - - // elements that can receive focus - focusableElements: ':input:enabled:visible', - - // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) - // no longer needed in 2012 - // applyPlatformOpacityRules: true, - - // callback method invoked when fadeIn has completed and blocking message is visible - onBlock: null, - - // callback method invoked when unblocking has completed; the callback is - // passed the element that has been unblocked (which is the window object for page - // blocks) and the options that were passed to the unblock call: - // onUnblock(element, options) - onUnblock: null, - - // callback method invoked when the overlay area is clicked. - // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used. - onOverlayClick: null, - - // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 - quirksmodeOffsetHack: 4, - - // class name of the message block - blockMsgClass: 'blockMsg', - - // if it is already blocked, then ignore it (don't unblock and reblock) - ignoreIfBlocked: false - }; - - // private data and functions follow... - - var pageBlock = null; - var pageBlockEls = []; - - function install(el, opts) { - var css, themedCSS; - var full = (el == window); - var msg = (opts && opts.message !== undefined ? opts.message : undefined); - opts = $.extend({}, $.blockUI.defaults, opts || {}); - - if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked')) - return; - - opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); - css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); - if (opts.onOverlayClick) - opts.overlayCSS.cursor = 'pointer'; - - themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); - msg = msg === undefined ? opts.message : msg; - - // remove the current block (if there is one) - if (full && pageBlock) - remove(window, {fadeOut:0}); - - // if an existing element is being used as the blocking content then we capture - // its current place in the DOM (and current display style) so we can restore - // it when we unblock - if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { - var node = msg.jquery ? msg[0] : msg; - var data = {}; - $(el).data('blockUI.history', data); - data.el = node; - data.parent = node.parentNode; - data.display = node.style.display; - data.position = node.style.position; - if (data.parent) - data.parent.removeChild(node); - } - - $(el).data('blockUI.onUnblock', opts.onUnblock); - var z = opts.baseZ; - - // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; - // layer1 is the iframe layer which is used to supress bleed through of underlying content - // layer2 is the overlay layer which has opacity and a wait cursor (by default) - // layer3 is the message content that is displayed while blocking - var lyr1, lyr2, lyr3, s; - if (msie || opts.forceIframe) - lyr1 = $(''); - else - lyr1 = $(''); - - if (opts.theme) - lyr2 = $(''); - else - lyr2 = $(''); - - if (opts.theme && full) { - s = ''; - } - else if (opts.theme) { - s = ''; - } - else if (full) { - s = ''; - } - else { - s = ''; - } - lyr3 = $(s); - - // if we have a message, style it - if (msg) { - if (opts.theme) { - lyr3.css(themedCSS); - lyr3.addClass('ui-widget-content'); - } - else - lyr3.css(css); - } - - // style the overlay - if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/) - lyr2.css(opts.overlayCSS); - lyr2.css('position', full ? 'fixed' : 'absolute'); - - // make iframe layer transparent in IE - if (msie || opts.forceIframe) - lyr1.css('opacity',0.0); - - //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); - var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); - $.each(layers, function() { - this.appendTo($par); - }); - - if (opts.theme && opts.draggable && $.fn.draggable) { - lyr3.draggable({ - handle: '.ui-dialog-titlebar', - cancel: 'li' - }); - } - - // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) - var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0); - if (ie6 || expr) { - // give body 100% height - if (full && opts.allowBodyStretch && $.support.boxModel) - $('html,body').css('height','100%'); - - // fix ie6 issue when blocked element has a border width - if ((ie6 || !$.support.boxModel) && !full) { - var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); - var fixT = t ? '(0 - '+t+')' : 0; - var fixL = l ? '(0 - '+l+')' : 0; - } - - // simulate fixed position - $.each(layers, function(i,o) { - var s = o[0].style; - s.position = 'absolute'; - if (i < 2) { - if (full) - s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"'); - else - s.setExpression('height','this.parentNode.offsetHeight + "px"'); - if (full) - s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'); - else - s.setExpression('width','this.parentNode.offsetWidth + "px"'); - if (fixL) s.setExpression('left', fixL); - if (fixT) s.setExpression('top', fixT); - } - else if (opts.centerY) { - if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); - s.marginTop = 0; - } - else if (!opts.centerY && full) { - var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0; - var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; - s.setExpression('top',expression); - } - }); - } - - // show the message - if (msg) { - if (opts.theme) - lyr3.find('.ui-widget-content').append(msg); - else - lyr3.append(msg); - if (msg.jquery || msg.nodeType) - $(msg).show(); - } - - if ((msie || opts.forceIframe) && opts.showOverlay) - lyr1.show(); // opacity is zero - if (opts.fadeIn) { - var cb = opts.onBlock ? opts.onBlock : noOp; - var cb1 = (opts.showOverlay && !msg) ? cb : noOp; - var cb2 = msg ? cb : noOp; - if (opts.showOverlay) - lyr2._fadeIn(opts.fadeIn, cb1); - if (msg) - lyr3._fadeIn(opts.fadeIn, cb2); - } - else { - if (opts.showOverlay) - lyr2.show(); - if (msg) - lyr3.show(); - if (opts.onBlock) - opts.onBlock(); - } - - // bind key and mouse events - bind(1, el, opts); - - if (full) { - pageBlock = lyr3[0]; - pageBlockEls = $(opts.focusableElements,pageBlock); - if (opts.focusInput) - setTimeout(focus, 20); - } - else - center(lyr3[0], opts.centerX, opts.centerY); - - if (opts.timeout) { - // auto-unblock - var to = setTimeout(function() { - if (full) - $.unblockUI(opts); - else - $(el).unblock(opts); - }, opts.timeout); - $(el).data('blockUI.timeout', to); - } - } - - // remove the block - function remove(el, opts) { - var count; - var full = (el == window); - var $el = $(el); - var data = $el.data('blockUI.history'); - var to = $el.data('blockUI.timeout'); - if (to) { - clearTimeout(to); - $el.removeData('blockUI.timeout'); - } - opts = $.extend({}, $.blockUI.defaults, opts || {}); - bind(0, el, opts); // unbind events - - if (opts.onUnblock === null) { - opts.onUnblock = $el.data('blockUI.onUnblock'); - $el.removeData('blockUI.onUnblock'); - } - - var els; - if (full) // crazy selector to handle odd field errors in ie6/7 - els = $('body').children().filter('.blockUI').add('body > .blockUI'); - else - els = $el.find('>.blockUI'); - - // fix cursor issue - if ( opts.cursorReset ) { - if ( els.length > 1 ) - els[1].style.cursor = opts.cursorReset; - if ( els.length > 2 ) - els[2].style.cursor = opts.cursorReset; - } - - if (full) - pageBlock = pageBlockEls = null; - - if (opts.fadeOut) { - count = els.length; - els.fadeOut(opts.fadeOut, function() { - if ( --count === 0) - reset(els,data,opts,el); - }); - } - else - reset(els, data, opts, el); - } - - // move blocking element back into the DOM where it started - function reset(els,data,opts,el) { - var $el = $(el); - els.each(function(i,o) { - // remove via DOM calls so we don't lose event handlers - if (this.parentNode) - this.parentNode.removeChild(this); - }); - - if (data && data.el) { - data.el.style.display = data.display; - data.el.style.position = data.position; - if (data.parent) - data.parent.appendChild(data.el); - $el.removeData('blockUI.history'); - } - - if ($el.data('blockUI.static')) { - $el.css('position', 'static'); // #22 - } - - if (typeof opts.onUnblock == 'function') - opts.onUnblock(el,opts); - - // fix issue in Safari 6 where block artifacts remain until reflow - var body = $(document.body), w = body.width(), cssW = body[0].style.width; - body.width(w-1).width(w); - body[0].style.width = cssW; - } - - // bind/unbind the handler - function bind(b, el, opts) { - var full = el == window, $el = $(el); - - // don't bother unbinding if there is nothing to unbind - if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) - return; - - $el.data('blockUI.isBlocked', b); - - // don't bind events when overlay is not in use or if bindEvents is false - if (!full || !opts.bindEvents || (b && !opts.showOverlay)) - return; - - // bind anchors and inputs for mouse and key events - var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove'; - if (b) - $(document).bind(events, opts, handler); - else - $(document).unbind(events, handler); - - // former impl... - // var $e = $('a,:input'); - // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); - } - - // event handler to suppress keyboard/mouse events when blocking - function handler(e) { - // allow tab navigation (conditionally) - if (e.keyCode && e.keyCode == 9) { - if (pageBlock && e.data.constrainTabKey) { - var els = pageBlockEls; - var fwd = !e.shiftKey && e.target === els[els.length-1]; - var back = e.shiftKey && e.target === els[0]; - if (fwd || back) { - setTimeout(function(){focus(back);},10); - return false; - } - } - } - var opts = e.data; - var target = $(e.target); - if (target.hasClass('blockOverlay') && opts.onOverlayClick) - opts.onOverlayClick(); - - // allow events within the message content - if (target.parents('div.' + opts.blockMsgClass).length > 0) - return true; - - // allow events for content that is not being blocked - return target.parents().children().filter('div.blockUI').length === 0; - } - - function focus(back) { - if (!pageBlockEls) - return; - var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; - if (e) - e.focus(); - } - - function center(el, x, y) { - var p = el.parentNode, s = el.style; - var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); - var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); - if (x) s.left = l > 0 ? (l+'px') : '0'; - if (y) s.top = t > 0 ? (t+'px') : '0'; - } - - function sz(el, p) { - return parseInt($.css(el,p),10)||0; - } - - } - - - /*global define:true */ - if (typeof define === 'function' && define.amd && define.amd.jQuery) { - define(['jquery'], setup); - } else { - setup(jQuery); - } - -})(); -- cgit v1.2.1 From 12ce1cbfcfff3e28cae82d327b056644457f65f6 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 4 Jan 2016 12:11:13 +0100 Subject: Merge pull request GH-9938 from huacnlee/hotfix/note_mail_with_notification Hotfix note mail with notification --- app/mailers/emails/notes.rb | 2 +- spec/services/notification_service_spec.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 65f37e92677..e1382d2da12 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -48,7 +48,7 @@ module Emails yield - SentNotification.record(@note, recipient_id, reply_key) + SentNotification.record_note(@note, recipient_id, reply_key) end end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index c103752198d..fc8cf425c3a 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -52,6 +52,9 @@ describe NotificationService, services: true do it do add_users_with_subscription(note.project, issue) + # Ensure create SentNotification by noteable = issue 6 times, not noteable = note + expect(SentNotification).to receive(:record).with(issue, any_args).exactly(6).times + ActionMailer::Base.deliveries.clear notification.new_note(note) -- cgit v1.2.1 From 10a9751ace57a368ebf39a1f8a3b172783cbeab1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 13:41:43 -0500 Subject: Update CHANGELOG [ci skip] --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index a0d5039905b..ff82ba8e86f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,7 +14,6 @@ v 8.4.0 (unreleased) - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) - Fix version check image in Safari - - Enable "Add key" button when user fills in a proper key (Stan Hu) - Show 'All' tab by default in the builds page v 8.3.3 (unreleased) -- cgit v1.2.1 From 7df3c1e8eafcb071f23ac1f4142e81ce8c1d9def Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 14:54:56 -0500 Subject: Correct the logo ID names Her left, not ours! --- app/assets/javascripts/logo.js.coffee | 8 ++++---- app/views/shared/_logo.svg | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee index 47135a6c5eb..a8f97bdf3d7 100644 --- a/app/assets/javascripts/logo.js.coffee +++ b/app/assets/javascripts/logo.js.coffee @@ -2,11 +2,11 @@ NProgress.configure(showSpinner: false) defaultClass = 'tanuki-shape' pieces = [ - 'path#tanuki-left-cheek', - 'path#tanuki-left-eye, path#tanuki-left-ear', - 'path#tanuki-nose', - 'path#tanuki-right-eye, path#tanuki-right-ear', 'path#tanuki-right-cheek', + 'path#tanuki-right-eye, path#tanuki-right-ear', + 'path#tanuki-nose', + 'path#tanuki-left-eye, path#tanuki-left-ear', + 'path#tanuki-left-cheek', ] firstPiece = pieces[0] timeout = null diff --git a/app/views/shared/_logo.svg b/app/views/shared/_logo.svg index 90f5f4e672b..3d279ec228c 100644 --- a/app/views/shared/_logo.svg +++ b/app/views/shared/_logo.svg @@ -5,13 +5,13 @@ - - - - - - - + + + + + + + -- cgit v1.2.1 From 9f46ca444354d4c6b52de3f23ce17c11f705d006 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 14:57:11 -0500 Subject: Decrease the logo sweep delay --- app/assets/javascripts/logo.js.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee index a8f97bdf3d7..b4dc993dead 100644 --- a/app/assets/javascripts/logo.js.coffee +++ b/app/assets/javascripts/logo.js.coffee @@ -1,5 +1,6 @@ NProgress.configure(showSpinner: false) +delay = 150 defaultClass = 'tanuki-shape' pieces = [ 'path#tanuki-right-cheek', @@ -12,7 +13,7 @@ firstPiece = pieces[0] timeout = null clearHighlights = -> - $(".#{defaultClass}").attr('class', defaultClass) + $(".#{defaultClass}.highlight").attr('class', defaultClass) start = -> clearHighlights() @@ -40,7 +41,7 @@ work = (pieceIndex) -> nextIndex = pieceIndex + 1 work(nextIndex) - , 200) + , delay) $(document).on 'page:fetch', start $(document).on 'page:change', stop -- cgit v1.2.1 From 567d87d90f2ba7195901ea24d240686c6030a4a7 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 15:18:04 -0500 Subject: Restructure logo JS to use `setInterval` --- app/assets/javascripts/logo.js.coffee | 46 ++++++++++++++++------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee index b4dc993dead..e864a674cdd 100644 --- a/app/assets/javascripts/logo.js.coffee +++ b/app/assets/javascripts/logo.js.coffee @@ -1,6 +1,5 @@ NProgress.configure(showSpinner: false) -delay = 150 defaultClass = 'tanuki-shape' pieces = [ 'path#tanuki-right-cheek', @@ -9,39 +8,36 @@ pieces = [ 'path#tanuki-left-eye, path#tanuki-left-ear', 'path#tanuki-left-cheek', ] +pieceIndex = 0 firstPiece = pieces[0] -timeout = null + +currentTimer = null +delay = 150 clearHighlights = -> $(".#{defaultClass}.highlight").attr('class', defaultClass) start = -> clearHighlights() + pieceIndex = 0 pieces.reverse() unless pieces[0] == firstPiece - work(0) + currentTimer = setInterval(work, delay) stop = -> - window.clearTimeout(timeout) + clearInterval(currentTimer) clearHighlights() -work = (pieceIndex) -> - # jQuery's addClass won't work on an SVG. Who knew! - $piece = $(pieces[pieceIndex]) - $piece.attr('class', "#{defaultClass} highlight") - - timeout = setTimeout(-> - $piece.attr('class', defaultClass) - - # If we hit the last piece, reset the index and then reverse the array to - # get a nice back-and-forth sweeping look - if pieceIndex + 1 >= pieces.length - nextIndex = 0 - pieces.reverse() - else - nextIndex = pieceIndex + 1 - - work(nextIndex) - , delay) - -$(document).on 'page:fetch', start -$(document).on 'page:change', stop +work = -> + clearHighlights() + $(pieces[pieceIndex]).attr('class', "#{defaultClass} highlight") + + # If we hit the last piece, reset the index and then reverse the array to + # get a nice back-and-forth sweeping look + if pieceIndex == pieces.length - 1 + pieceIndex = 0 + pieces.reverse() + else + pieceIndex++ + +$(document).on('page:fetch', start) +$(document).on('page:change', stop) -- cgit v1.2.1 From 93096247d86a88e84a0bff7c8dd8496179638a9d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 4 Jan 2016 15:00:21 -0800 Subject: Don't notify users twice if they are both project watchers and subscribers Closes #4708 --- CHANGELOG | 1 + app/services/notification_service.rb | 1 + spec/services/notification_service_spec.rb | 12 ++++++++++++ 3 files changed, 14 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e4b35b281bb..e8eb5d568f5 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.4.0 (unreleased) - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) + - Don't notify users twice if they are both project watchers and subscribers (Stan Hu) - Implement new UI for group page - Implement search inside emoji picker - Add API support for looking up a user by username (Stan Hu) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index bdf7b3ad2bb..e4edc55bf69 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -413,6 +413,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, target) recipients.delete(current_user) + recipients = recipients.uniq recipients end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index c103752198d..588ecc51382 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -61,6 +61,7 @@ describe NotificationService, services: true do should_email(note.noteable.assignee) should_email(@u_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_email(@subscribed_participant) should_not_email(note.author) should_not_email(@u_participating) @@ -245,6 +246,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -260,6 +262,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) end @@ -282,6 +285,7 @@ describe NotificationService, services: true do should_email(merge_request.assignee) should_email(@u_watcher) + should_email(@watcher_and_subscriber) should_email(@u_participant_mentioned) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -296,6 +300,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -310,6 +315,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -324,6 +330,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -338,6 +345,7 @@ describe NotificationService, services: true do should_email(@u_watcher) should_email(@u_participant_mentioned) should_email(@subscriber) + should_email(@watcher_and_subscriber) should_not_email(@unsubscriber) should_not_email(@u_participating) should_not_email(@u_disabled) @@ -387,14 +395,18 @@ describe NotificationService, services: true do @subscriber = create :user @unsubscriber = create :user @subscribed_participant = create(:user, username: 'subscribed_participant', notification_level: Notification::N_PARTICIPATING) + @watcher_and_subscriber = create(:user, notification_level: Notification::N_WATCH) project.team << [@subscribed_participant, :master] project.team << [@subscriber, :master] project.team << [@unsubscriber, :master] + project.team << [@watcher_and_subscriber, :master] issuable.subscriptions.create(user: @subscriber, subscribed: true) issuable.subscriptions.create(user: @subscribed_participant, subscribed: true) issuable.subscriptions.create(user: @unsubscriber, subscribed: false) + # Make the watcher a subscriber to detect dupes + issuable.subscriptions.create(user: @watcher_and_subscriber, subscribed: true) end def sent_to_user?(user) -- cgit v1.2.1 From b807a23b4b8fdf1deff120bc9c0bb762991cef3d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 18:35:05 -0500 Subject: Redesign the AbuseReports index - Shows when the reported user joined - Show relative timestamp for when the report was created - Parse message with restricted Markdown pipeline to autolink URLs --- app/views/admin/abuse_reports/_abuse_report.html.haml | 18 ++++++++++-------- app/views/admin/abuse_reports/index.html.haml | 3 +-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml index d3afc658cd6..cf50a376e11 100644 --- a/app/views/admin/abuse_reports/_abuse_report.html.haml +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -2,19 +2,21 @@ - user = abuse_report.user %tr %td - - if reporter - = link_to reporter.name, reporter + - if user + = link_to user.name, [:admin, user] + .light.small + Joined #{time_ago_with_tooltip(user.created_at)} - else (removed) %td - = abuse_report.created_at.to_s(:short) - %td - = abuse_report.message - %td - - if user - = link_to user.name, user + - if reporter + = link_to reporter.name, [:admin, reporter] - else (removed) + .light.small + = time_ago_with_tooltip(abuse_report.created_at) + %td + = markdown(abuse_report.message.squish!, pipeline: :single_line) %td - if user = link_to 'Remove user & report', admin_abuse_report_path(abuse_report, remove_user: true), diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index 40a5fe4628b..bc4a9cedb2c 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -6,10 +6,9 @@ %table.table %thead %tr + %th User %th Reported by - %th Reported at %th Message - %th User %th Primary action %th = render @abuse_reports -- cgit v1.2.1 From 0e60282e36faab8b0f4faee0b71716987df28416 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 18:46:43 -0500 Subject: Redirect back to user profile page after abuse report Now the reporter will see the fruits of their labor, namely, the red icon! --- app/controllers/abuse_reports_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 20bc5173f1d..5718fd22de9 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -14,7 +14,7 @@ class AbuseReportsController < ApplicationController end message = "Thank you for your report. A GitLab administrator will look into it shortly." - redirect_to root_path, notice: message + redirect_to @abuse_report.user, notice: message else render :new end @@ -23,6 +23,9 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(:user_id, :message) + params.require(:abuse_report).permit(%i( + message + user_id + )) end end -- cgit v1.2.1 From 01248d205103fe6c408e914e8943873ceb7acb2a Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 18:56:48 -0500 Subject: Make AbuseReportMailer responsible for knowing if it should deliver --- app/mailers/abuse_report_mailer.rb | 10 ++++++++- spec/mailers/abuse_report_mailer_spec.rb | 38 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 spec/mailers/abuse_report_mailer_spec.rb diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb index f0c41f69a5c..d0ce827a595 100644 --- a/app/mailers/abuse_report_mailer.rb +++ b/app/mailers/abuse_report_mailer.rb @@ -2,11 +2,19 @@ class AbuseReportMailer < BaseMailer include Gitlab::CurrentSettings def notify(abuse_report_id) + return unless deliverable? + @abuse_report = AbuseReport.find(abuse_report_id) mail( - to: current_application_settings.admin_notification_email, + to: current_application_settings.admin_notification_email, subject: "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse" ) end + + private + + def deliverable? + current_application_settings.admin_notification_email.present? + end end diff --git a/spec/mailers/abuse_report_mailer_spec.rb b/spec/mailers/abuse_report_mailer_spec.rb new file mode 100644 index 00000000000..eb433c38873 --- /dev/null +++ b/spec/mailers/abuse_report_mailer_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +describe AbuseReportMailer do + include EmailSpec::Matchers + + describe '.notify' do + context 'with admin_notification_email set' do + before do + stub_application_setting(admin_notification_email: 'admin@example.com') + end + + it 'sends to the admin_notification_email' do + report = create(:abuse_report) + + mail = described_class.notify(report.id) + + expect(mail).to deliver_to 'admin@example.com' + end + + it 'includes the user in the subject' do + report = create(:abuse_report) + + mail = described_class.notify(report.id) + + expect(mail).to have_subject "#{report.user.name} (#{report.user.username}) was reported for abuse" + end + end + + context 'with no admin_notification_email set' do + it 'returns early' do + stub_application_setting(admin_notification_email: nil) + + expect { described_class.notify(spy).deliver_now }. + not_to change { ActionMailer::Base.deliveries.count } + end + end + end +end -- cgit v1.2.1 From 46a220ae3c0e646aac63a3230399fcc8979df6ec Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 4 Jan 2016 18:59:42 -0500 Subject: Add `AbuseReport#notify` Tell, Don't Ask. --- app/controllers/abuse_reports_controller.rb | 4 +- app/models/abuse_report.rb | 6 ++ spec/controllers/abuse_reports_controller_spec.rb | 80 +++++++---------------- spec/models/abuse_report_spec.rb | 17 +++++ 4 files changed, 49 insertions(+), 58 deletions(-) diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 5718fd22de9..38814459f66 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -9,9 +9,7 @@ class AbuseReportsController < ApplicationController @abuse_report.reporter = current_user if @abuse_report.save - if current_application_settings.admin_notification_email.present? - AbuseReportMailer.notify(@abuse_report.id).deliver_later - end + @abuse_report.notify message = "Thank you for your report. A GitLab administrator will look into it shortly." redirect_to @abuse_report.user, notice: message diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index 89b3116b9f2..55864236b2f 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -18,4 +18,10 @@ class AbuseReport < ActiveRecord::Base validates :user, presence: true validates :message, presence: true validates :user_id, uniqueness: true + + def notify + return unless self.persisted? + + AbuseReportMailer.notify(self.id).deliver_later + end end diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb index 15824a1c67f..80a418feb3e 100644 --- a/spec/controllers/abuse_reports_controller_spec.rb +++ b/spec/controllers/abuse_reports_controller_spec.rb @@ -1,76 +1,46 @@ require 'spec_helper' describe AbuseReportsController do - let(:reporter) { create(:user) } - let(:user) { create(:user) } - let(:message) { "This user is a spammer" } + let(:reporter) { create(:user) } + let(:user) { create(:user) } + let(:attrs) do + attributes_for(:abuse_report) do |hash| + hash[:user_id] = user.id + end + end before do sign_in(reporter) end - describe "POST create" do - context "with admin notification email set" do - let(:admin_email) { "admin@example.com"} - - before(:each) do - stub_application_setting(admin_notification_email: admin_email) + describe 'POST create' do + context 'with valid attributes' do + it 'saves the abuse report' do + expect do + post :create, abuse_report: attrs + end.to change { AbuseReport.count }.by(1) end - it "sends a notification email" do - perform_enqueued_jobs do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - - email = ActionMailer::Base.deliveries.last + it 'calls notify' do + expect_any_instance_of(AbuseReport).to receive(:notify) - expect(email.to).to eq([admin_email]) - expect(email.subject).to include(user.username) - expect(email.text_part.body).to include(message) - end + post :create, abuse_report: attrs end - it "saves the abuse report" do - perform_enqueued_jobs do - expect do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - end.to change { AbuseReport.count }.by(1) - end - end - end + it 'redirects back to the reported user' do + post :create, abuse_report: attrs - context "without admin notification email set" do - before(:each) do - stub_application_setting(admin_notification_email: nil) + expect(response).to redirect_to user end + end - it "does not send a notification email" do - expect do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - end.not_to change { ActionMailer::Base.deliveries.count } - end + context 'with invalid attributes' do + it 'renders new' do + attrs.delete(:user_id) + post :create, abuse_report: attrs - it "saves the abuse report" do - expect do - post :create, - abuse_report: { - user_id: user.id, - message: message - } - end.to change { AbuseReport.count }.by(1) + expect(response).to render_template(:new) end end end - end diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index d45319b25d4..46cab1644c7 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -28,4 +28,21 @@ RSpec.describe AbuseReport, type: :model do it { is_expected.to validate_presence_of(:message) } it { is_expected.to validate_uniqueness_of(:user_id) } end + + describe '#notify' do + it 'delivers' do + expect(AbuseReportMailer).to receive(:notify).with(subject.id). + and_return(spy) + + subject.notify + end + + it 'returns early when not persisted' do + report = build(:abuse_report) + + expect(AbuseReportMailer).not_to receive(:notify) + + report.notify + end + end end -- cgit v1.2.1 From f7ba38c073387eedb13375ad6286ba08ce6badb9 Mon Sep 17 00:00:00 2001 From: Andriy Dyadyura Date: Tue, 5 Jan 2016 10:45:18 +0100 Subject: markdown fixes --- app/assets/stylesheets/framework/typography.scss | 6 +- db/schema.rb | 520 +++++++++++------------ 2 files changed, 263 insertions(+), 263 deletions(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index c3e4ad0ad00..05d0c865164 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -54,17 +54,17 @@ h3 { margin: 24px 0 12px 0; - font-size: 1.25em; + font-size: 1.1em; } h4 { margin: 24px 0 12px 0; - font-size: 1.1em; + font-size: 1em; } h5 { margin: 24px 0 12px 0; - font-size: 1em; + font-size: 0.95em; } h6 { diff --git a/db/schema.rb b/db/schema.rb index 7a6d34b8153..48e6983684a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -32,44 +32,44 @@ ActiveRecord::Schema.define(version: 20151229112614) do t.text "sign_in_text" t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url" - t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true + t.string "home_page_url", limit: 255 + t.integer "default_branch_protection", default: 2 + t.boolean "twitter_sharing_enabled", default: true t.text "restricted_visibility_levels" - t.boolean "version_check_enabled", default: true - t.integer "max_attachment_size", default: 10, null: false + t.boolean "version_check_enabled", default: true + t.integer "max_attachment_size", default: 10, null: false t.integer "default_project_visibility" t.integer "default_snippet_visibility" t.text "restricted_signup_domains" - t.boolean "user_oauth_applications", default: true - t.string "after_sign_out_path" - t.integer "session_expire_delay", default: 10080, null: false + t.boolean "user_oauth_applications", default: true + t.string "after_sign_out_path", limit: 255 + t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" 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 + t.string "admin_notification_email", limit: 255 + t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" - t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 - t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" + t.boolean "require_two_factor_authentication", default: false + t.integer "two_factor_grace_period", default: 48 + t.boolean "metrics_enabled", default: false + t.string "metrics_host", default: "localhost" t.string "metrics_username" t.string "metrics_password" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 - t.boolean "recaptcha_enabled", default: false + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 + t.boolean "recaptcha_enabled", default: false t.string "recaptcha_site_key" t.string "recaptcha_private_key" - t.integer "metrics_port", default: 8089 + t.integer "metrics_port", default: 8089 end create_table "audit_events", force: :cascade do |t| - t.integer "author_id", null: false - t.string "type", null: false - t.integer "entity_id", null: false - t.string "entity_type", null: false + t.integer "author_id", null: false + t.string "type", limit: 255, null: false + t.integer "entity_id", null: false + t.string "entity_type", limit: 255, null: false t.text "details" t.datetime "created_at" t.datetime "updated_at" @@ -80,14 +80,14 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree create_table "broadcast_messages", force: :cascade do |t| - t.text "message", null: false + t.text "message", null: false t.datetime "starts_at" t.datetime "ends_at" t.integer "alert_type" t.datetime "created_at" t.datetime "updated_at" - t.string "color" - t.string "font" + t.string "color", limit: 255 + t.string "font", limit: 255 end create_table "ci_application_settings", force: :cascade do |t| @@ -99,7 +99,7 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "ci_builds", force: :cascade do |t| t.integer "project_id" - t.string "status" + t.string "status", limit: 255 t.datetime "finished_at" t.text "trace" t.datetime "created_at" @@ -110,19 +110,19 @@ ActiveRecord::Schema.define(version: 20151229112614) do t.integer "commit_id" t.text "commands" t.integer "job_id" - t.string "name" - t.boolean "deploy", default: false + t.string "name", limit: 255 + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false - t.string "stage" + t.boolean "allow_failure", default: false, null: false + t.string "stage", limit: 255 t.integer "trigger_request_id" t.integer "stage_idx" t.boolean "tag" - t.string "ref" + t.string "ref", limit: 255 t.integer "user_id" - t.string "type" - t.string "target_url" - t.string "description" + t.string "type", limit: 255 + t.string "target_url", limit: 255 + t.string "description", limit: 255 t.text "artifacts_file" t.integer "gl_project_id" end @@ -141,13 +141,13 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "ci_commits", force: :cascade do |t| t.integer "project_id" - t.string "ref" - t.string "sha" - t.string "before_sha" + t.string "ref", limit: 255 + t.string "sha", limit: 255 + t.string "before_sha", limit: 255 t.text "push_data" t.datetime "created_at" t.datetime "updated_at" - t.boolean "tag", default: false + t.boolean "tag", default: false t.text "yaml_errors" t.datetime "committed_at" t.integer "gl_project_id" @@ -174,16 +174,16 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree create_table "ci_jobs", force: :cascade do |t| - t.integer "project_id", null: false + t.integer "project_id", null: false t.text "commands" - t.boolean "active", default: true, null: false + t.boolean "active", default: true, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "name" - t.boolean "build_branches", default: true, null: false - t.boolean "build_tags", default: false, null: false - t.string "job_type", default: "parallel" - t.string "refs" + t.string "name", limit: 255 + t.boolean "build_branches", default: true, null: false + t.boolean "build_tags", default: false, null: false + t.string "job_type", limit: 255, default: "parallel" + t.string "refs", limit: 255 t.datetime "deleted_at" end @@ -191,25 +191,25 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree create_table "ci_projects", force: :cascade do |t| - t.string "name" - t.integer "timeout", default: 3600, null: false + t.string "name", limit: 255 + t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - t.string "token" - t.string "default_ref" - t.string "path" - t.boolean "always_build", default: false, null: false + t.string "token", limit: 255 + t.string "default_ref", limit: 255 + t.string "path", limit: 255 + t.boolean "always_build", default: false, null: false t.integer "polling_interval" - t.boolean "public", default: false, null: false - t.string "ssh_url_to_repo" + t.boolean "public", default: false, null: false + t.string "ssh_url_to_repo", limit: 255 t.integer "gitlab_id" - t.boolean "allow_git_fetch", default: true, null: false - t.string "email_recipients", default: "", null: false - t.boolean "email_add_pusher", default: true, null: false - t.boolean "email_only_broken_builds", default: true, null: false - t.string "skip_refs" - t.string "coverage_regex" - t.boolean "shared_runners_enabled", default: false + t.boolean "allow_git_fetch", default: true, null: false + t.string "email_recipients", limit: 255, default: "", null: false + t.boolean "email_add_pusher", default: true, null: false + t.boolean "email_only_broken_builds", default: true, null: false + t.string "skip_refs", limit: 255 + t.string "coverage_regex", limit: 255 + t.boolean "shared_runners_enabled", default: false t.text "generated_yaml_config" end @@ -228,34 +228,34 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree create_table "ci_runners", force: :cascade do |t| - t.string "token" + t.string "token", limit: 255 t.datetime "created_at" t.datetime "updated_at" - t.string "description" + t.string "description", limit: 255 t.datetime "contacted_at" - t.boolean "active", default: true, null: false - t.boolean "is_shared", default: false - t.string "name" - t.string "version" - t.string "revision" - t.string "platform" - t.string "architecture" + t.boolean "active", default: true, null: false + t.boolean "is_shared", default: false + t.string "name", limit: 255 + t.string "version", limit: 255 + t.string "revision", limit: 255 + t.string "platform", limit: 255 + t.string "architecture", limit: 255 end create_table "ci_services", force: :cascade do |t| - t.string "type" - t.string "title" - t.integer "project_id", null: false + t.string "type", limit: 255 + t.string "title", limit: 255 + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" end add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree create_table "ci_sessions", force: :cascade do |t| - t.string "session_id", null: false + t.string "session_id", limit: 255, null: false t.text "data" t.datetime "created_at" t.datetime "updated_at" @@ -267,9 +267,9 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type" + t.string "taggable_type", limit: 255 t.integer "tagger_id" - t.string "tagger_type" + t.string "tagger_type", limit: 255 t.string "context", limit: 128 t.datetime "created_at" end @@ -278,8 +278,8 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "ci_tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 255 + t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree @@ -293,7 +293,7 @@ ActiveRecord::Schema.define(version: 20151229112614) do end create_table "ci_triggers", force: :cascade do |t| - t.string "token" + t.string "token", limit: 255 t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" @@ -306,19 +306,19 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "ci_variables", force: :cascade do |t| t.integer "project_id" - t.string "key" + t.string "key", limit: 255 t.text "value" t.text "encrypted_value" - t.string "encrypted_value_salt" - t.string "encrypted_value_iv" + t.string "encrypted_value_salt", limit: 255 + t.string "encrypted_value_iv", limit: 255 t.integer "gl_project_id" end add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree create_table "ci_web_hooks", force: :cascade do |t| - t.string "url", null: false - t.integer "project_id", null: false + t.string "url", limit: 255, null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -333,8 +333,8 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree create_table "emails", force: :cascade do |t| - t.integer "user_id", null: false - t.string "email", null: false + t.integer "user_id", null: false + t.string "email", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" end @@ -343,9 +343,9 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree create_table "events", force: :cascade do |t| - t.string "target_type" + t.string "target_type", limit: 255 t.integer "target_id" - t.string "title" + t.string "title", limit: 255 t.text "data" t.integer "project_id" t.datetime "created_at" @@ -371,8 +371,8 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree create_table "identities", force: :cascade do |t| - t.string "extern_uid" - t.string "provider" + t.string "extern_uid", limit: 255 + t.string "provider", limit: 255 t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -382,17 +382,17 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree create_table "issues", force: :cascade do |t| - t.string "title" + t.string "title", limit: 255 t.integer "assignee_id" t.integer "author_id" t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.integer "position", default: 0 - t.string "branch_name" + t.integer "position", default: 0 + t.string "branch_name", limit: 255 t.text "description" t.integer "milestone_id" - t.string "state" + t.string "state", limit: 255 t.integer "iid" t.integer "updated_by_id" end @@ -412,10 +412,10 @@ ActiveRecord::Schema.define(version: 20151229112614) do t.datetime "created_at" t.datetime "updated_at" t.text "key" - t.string "title" - t.string "type" - t.string "fingerprint" - t.boolean "public", default: false, null: false + t.string "title", limit: 255 + t.string "type", limit: 255 + t.string "fingerprint", limit: 255 + t.boolean "public", default: false, null: false end add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree @@ -424,7 +424,7 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" - t.string "target_type" + t.string "target_type", limit: 255 t.datetime "created_at" t.datetime "updated_at" end @@ -433,22 +433,22 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree create_table "labels", force: :cascade do |t| - t.string "title" - t.string "color" + t.string "title", limit: 255 + t.string "color", limit: 255 t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "template", default: false + t.boolean "template", default: false end add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree create_table "lfs_objects", force: :cascade do |t| - t.string "oid", null: false - t.integer "size", null: false + t.string "oid", limit: 255, null: false + t.integer "size", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file" + t.string "file", limit: 255 end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree @@ -463,17 +463,17 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree create_table "members", force: :cascade do |t| - t.integer "access_level", null: false - t.integer "source_id", null: false - t.string "source_type", null: false + t.integer "access_level", null: false + t.integer "source_id", null: false + t.string "source_type", limit: 255, null: false t.integer "user_id" - t.integer "notification_level", null: false - t.string "type" + t.integer "notification_level", null: false + t.string "type", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.integer "created_by_id" - t.string "invite_email" - t.string "invite_token" + t.string "invite_email", limit: 255 + t.string "invite_token", limit: 255 t.datetime "invite_accepted_at" end @@ -485,10 +485,10 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree create_table "merge_request_diffs", force: :cascade do |t| - t.string "state" + t.string "state", limit: 255 t.text "st_commits" t.text "st_diffs" - t.integer "merge_request_id", null: false + t.integer "merge_request_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -496,26 +496,26 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree create_table "merge_requests", force: :cascade do |t| - t.string "target_branch", null: false - t.string "source_branch", null: false - t.integer "source_project_id", null: false + t.string "target_branch", limit: 255, null: false + t.string "source_branch", limit: 255, null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" - t.string "title" + t.string "title", limit: 255 t.datetime "created_at" t.datetime "updated_at" t.integer "milestone_id" - t.string "state" - t.string "merge_status" - t.integer "target_project_id", null: false + t.string "state", limit: 255 + t.string "merge_status", limit: 255 + t.integer "target_project_id", null: false t.integer "iid" t.text "description" - t.integer "position", default: 0 + t.integer "position", default: 0 t.datetime "locked_at" t.integer "updated_by_id" - t.string "merge_error" + t.string "merge_error", limit: 255 t.text "merge_params" - t.boolean "merge_when_build_succeeds", default: false, null: false + t.boolean "merge_when_build_succeeds", default: false, null: false t.integer "merge_user_id" end @@ -531,13 +531,13 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", null: false - t.integer "project_id", null: false + t.string "title", limit: 255, null: false + t.integer "project_id", null: false t.text "description" t.date "due_date" t.datetime "created_at" t.datetime "updated_at" - t.string "state" + t.string "state", limit: 255 t.integer "iid" end @@ -547,14 +547,14 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree create_table "namespaces", force: :cascade do |t| - t.string "name", null: false - t.string "path", null: false + t.string "name", limit: 255, null: false + t.string "path", limit: 255, 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 "avatar" + t.string "type", limit: 255 + t.string "description", limit: 255, default: "", null: false + t.string "avatar", limit: 255 end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -565,19 +565,19 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "notes", force: :cascade do |t| t.text "note" - t.string "noteable_type" + t.string "noteable_type", limit: 255 t.integer "author_id" t.datetime "created_at" t.datetime "updated_at" t.integer "project_id" - t.string "attachment" - t.string "line_code" - t.string "commit_id" + t.string "attachment", limit: 255 + t.string "line_code", limit: 255 + t.string "commit_id", limit: 255 t.integer "noteable_id" - t.boolean "system", default: false, null: false + t.boolean "system", default: false, null: false t.text "st_diff" t.integer "updated_by_id" - t.boolean "is_award", default: false, null: false + t.boolean "is_award", default: false, null: false end add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree @@ -593,14 +593,14 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree create_table "oauth_access_grants", force: :cascade do |t| - t.integer "resource_owner_id", null: false - t.integer "application_id", null: false - t.string "token", null: false - t.integer "expires_in", null: false - t.text "redirect_uri", null: false - t.datetime "created_at", null: false + t.integer "resource_owner_id", null: false + t.integer "application_id", null: false + t.string "token", limit: 255, null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false + t.datetime "created_at", null: false t.datetime "revoked_at" - t.string "scopes" + t.string "scopes", limit: 255 end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree @@ -608,12 +608,12 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" - t.string "token", null: false - t.string "refresh_token" + t.string "token", limit: 255, null: false + t.string "refresh_token", limit: 255 t.integer "expires_in" t.datetime "revoked_at" - t.datetime "created_at", null: false - t.string "scopes" + t.datetime "created_at", null: false + t.string "scopes", limit: 255 end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -621,15 +621,15 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree create_table "oauth_applications", force: :cascade do |t| - t.string "name", null: false - t.string "uid", null: false - t.string "secret", null: false - t.text "redirect_uri", null: false - t.string "scopes", default: "", null: false + t.string "name", limit: 255, null: false + t.string "uid", limit: 255, null: false + t.string "secret", limit: 255, null: false + t.text "redirect_uri", null: false + t.string "scopes", limit: 255, default: "", null: false t.datetime "created_at" t.datetime "updated_at" t.integer "owner_id" - t.string "owner_type" + t.string "owner_type", limit: 255 end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree @@ -641,39 +641,39 @@ ActiveRecord::Schema.define(version: 20151229112614) do end create_table "projects", force: :cascade do |t| - t.string "name" - t.string "path" + t.string "name", limit: 255 + t.string "path", limit: 255 t.text "description" t.datetime "created_at" t.datetime "updated_at" t.integer "creator_id" - t.boolean "issues_enabled", default: true, null: false - t.boolean "wall_enabled", default: true, null: false - t.boolean "merge_requests_enabled", default: true, null: false - t.boolean "wiki_enabled", default: true, null: false + t.boolean "issues_enabled", default: true, null: false + t.boolean "wall_enabled", default: true, null: false + t.boolean "merge_requests_enabled", default: true, null: false + t.boolean "wiki_enabled", default: true, null: false t.integer "namespace_id" - t.string "issues_tracker", default: "gitlab", null: false - t.string "issues_tracker_id" - t.boolean "snippets_enabled", default: true, null: false + t.string "issues_tracker", limit: 255, default: "gitlab", null: false + t.string "issues_tracker_id", limit: 255 + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" - t.string "import_url" - t.integer "visibility_level", default: 0, null: false - t.boolean "archived", default: false, null: false - t.string "avatar" - t.string "import_status" - t.float "repository_size", default: 0.0 - t.integer "star_count", default: 0, null: false - t.string "import_type" - t.string "import_source" - t.integer "commit_count", default: 0 + t.string "import_url", limit: 255 + t.integer "visibility_level", default: 0, null: false + t.boolean "archived", default: false, null: false + t.string "avatar", limit: 255 + t.string "import_status", limit: 255 + t.float "repository_size", default: 0.0 + t.integer "star_count", default: 0, null: false + t.string "import_type", limit: 255 + t.string "import_source", limit: 255 + t.integer "commit_count", default: 0 t.text "import_error" t.integer "ci_id" - t.boolean "builds_enabled", default: true, null: false - t.boolean "shared_runners_enabled", default: true, null: false + t.boolean "builds_enabled", default: true, null: false + t.boolean "shared_runners_enabled", default: true, null: false t.string "runners_token" t.string "build_coverage_regex" - t.boolean "build_allow_git_fetch", default: true, null: false - t.integer "build_timeout", default: 3600, null: false + t.boolean "build_allow_git_fetch", default: true, null: false + t.integer "build_timeout", default: 3600, null: false end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree @@ -689,17 +689,17 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree create_table "protected_branches", force: :cascade do |t| - t.integer "project_id", null: false - t.string "name", null: false + t.integer "project_id", null: false + t.string "name", limit: 255, null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "developers_can_push", default: false, null: false + t.boolean "developers_can_push", default: false, null: false end add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree create_table "releases", force: :cascade do |t| - t.string "tag" + t.string "tag", limit: 255 t.text "description" t.integer "project_id" t.datetime "created_at" @@ -712,30 +712,30 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" - t.string "noteable_type" + t.string "noteable_type", limit: 255 t.integer "recipient_id" - t.string "commit_id" - t.string "reply_key", null: false - t.string "line_code" + t.string "commit_id", limit: 255 + t.string "reply_key", limit: 255, null: false + t.string "line_code", limit: 255 end add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree create_table "services", force: :cascade do |t| - t.string "type" - t.string "title" + t.string "type", limit: 255 + t.string "title", limit: 255 t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.boolean "active", default: false, null: false t.text "properties" - t.boolean "template", default: false - t.boolean "push_events", default: true - t.boolean "issues_events", default: true - t.boolean "merge_requests_events", default: true - t.boolean "tag_push_events", default: true - t.boolean "note_events", default: true, null: false - t.boolean "build_events", default: false, null: false + t.boolean "template", default: false + t.boolean "push_events", default: true + t.boolean "issues_events", default: true + t.boolean "merge_requests_events", default: true + t.boolean "tag_push_events", default: true + t.boolean "note_events", default: true, null: false + t.boolean "build_events", default: false, null: false end add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree @@ -743,16 +743,16 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title" + t.string "title", limit: 255 t.text "content" - t.integer "author_id", null: false + t.integer "author_id", null: false t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "file_name" + t.string "file_name", limit: 255 t.datetime "expires_at" - t.string "type" - t.integer "visibility_level", default: 0, null: false + t.string "type", limit: 255 + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -765,7 +765,7 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" - t.string "subscribable_type" + t.string "subscribable_type", limit: 255 t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -776,10 +776,10 @@ ActiveRecord::Schema.define(version: 20151229112614) do create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type" + t.string "taggable_type", limit: 255 t.integer "tagger_id" - t.string "tagger_type" - t.string "context" + t.string "tagger_type", limit: 255 + t.string "context", limit: 255 t.datetime "created_at" end @@ -787,67 +787,67 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| - t.string "name" - t.integer "taggings_count", default: 0 + t.string "name", limit: 255 + t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false - t.string "reset_password_token" + t.string "email", limit: 255, default: "", null: false + t.string "encrypted_password", limit: 255, default: "", null: false + t.string "reset_password_token", limit: 255 t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0 + t.integer "sign_in_count", default: 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip" - t.string "last_sign_in_ip" - t.datetime "created_at" - t.datetime "updated_at" - t.string "name" - t.boolean "admin", default: false, null: false - t.integer "projects_limit", default: 10 - t.string "skype", default: "", null: false - t.string "linkedin", default: "", null: false - t.string "twitter", default: "", null: false - t.string "authentication_token" - t.integer "theme_id", default: 1, null: false - t.string "bio" - t.integer "failed_attempts", default: 0 + t.string "current_sign_in_ip", limit: 255 + t.string "last_sign_in_ip", limit: 255 + t.datetime "created_at" + t.datetime "updated_at" + t.string "name", limit: 255 + t.boolean "admin", default: false, null: false + t.integer "projects_limit", default: 10 + t.string "skype", limit: 255, default: "", null: false + t.string "linkedin", limit: 255, default: "", null: false + t.string "twitter", limit: 255, default: "", null: false + t.string "authentication_token", limit: 255 + t.integer "theme_id", default: 1, null: false + t.string "bio", limit: 255 + t.integer "failed_attempts", default: 0 t.datetime "locked_at" - t.string "username" - t.boolean "can_create_group", default: true, null: false - t.boolean "can_create_team", default: true, null: false - t.string "state" - t.integer "color_scheme_id", default: 1, null: false - t.integer "notification_level", default: 1, null: false + t.string "username", limit: 255 + t.boolean "can_create_group", default: true, null: false + t.boolean "can_create_team", default: true, null: false + t.string "state", limit: 255 + t.integer "color_scheme_id", default: 1, null: false + t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" t.datetime "last_credential_check_at" - t.string "avatar" - t.string "confirmation_token" + t.string "avatar", limit: 255 + t.string "confirmation_token", limit: 255 t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - t.string "unconfirmed_email" - t.boolean "hide_no_ssh_key", default: false - t.string "website_url", default: "", null: false - t.string "notification_email" - t.boolean "hide_no_password", default: false - t.boolean "password_automatically_set", default: false - t.string "location" - t.string "encrypted_otp_secret" - t.string "encrypted_otp_secret_iv" - t.string "encrypted_otp_secret_salt" - t.boolean "otp_required_for_login", default: false, null: false + t.string "unconfirmed_email", limit: 255 + t.boolean "hide_no_ssh_key", default: false + t.string "website_url", limit: 255, default: "", null: false + t.string "notification_email", limit: 255 + t.boolean "hide_no_password", default: false + t.boolean "password_automatically_set", default: false + t.string "location", limit: 255 + t.string "encrypted_otp_secret", limit: 255 + t.string "encrypted_otp_secret_iv", limit: 255 + t.string "encrypted_otp_secret_salt", limit: 255 + t.boolean "otp_required_for_login", default: false, null: false t.text "otp_backup_codes" - t.string "public_email", default: "", null: false - t.integer "dashboard", default: 0 - t.integer "project_view", default: 0 + t.string "public_email", limit: 255, default: "", null: false + t.integer "dashboard", default: 0 + t.integer "project_view", default: 0 t.integer "consumed_timestep" - t.integer "layout", default: 0 - t.boolean "hide_project_limit", default: false + t.integer "layout", default: 0 + t.boolean "hide_project_limit", default: false t.string "unlock_token" t.datetime "otp_grace_period_started_at" end @@ -874,19 +874,19 @@ ActiveRecord::Schema.define(version: 20151229112614) do add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree create_table "web_hooks", force: :cascade do |t| - t.string "url" + t.string "url", limit: 255 t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", default: "ProjectHook" + t.string "type", limit: 255, default: "ProjectHook" t.integer "service_id" - t.boolean "push_events", default: true, null: false - t.boolean "issues_events", default: false, null: false - t.boolean "merge_requests_events", default: false, null: false - t.boolean "tag_push_events", default: false - t.boolean "note_events", default: false, null: false - t.boolean "enable_ssl_verification", default: true - t.boolean "build_events", default: false, null: false + t.boolean "push_events", default: true, null: false + t.boolean "issues_events", default: false, null: false + t.boolean "merge_requests_events", default: false, null: false + t.boolean "tag_push_events", default: false + t.boolean "note_events", default: false, null: false + t.boolean "enable_ssl_verification", default: true + t.boolean "build_events", default: false, null: false end add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree -- cgit v1.2.1 From 00e8532d89fb04b39e9811c5ec5953dd2d098f6f Mon Sep 17 00:00:00 2001 From: Andriy Dyadyura Date: Tue, 5 Jan 2016 10:51:59 +0100 Subject: markdown fixes --- app/assets/stylesheets/framework/typography.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 05d0c865164..714369d9f15 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -59,7 +59,7 @@ h4 { margin: 24px 0 12px 0; - font-size: 1em; + font-size: 0.98em; } h5 { -- cgit v1.2.1 From 3098500835aad26748d982fa81c84a2deed97931 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 11:01:44 +0100 Subject: Fix ci_trigger_request factory --- spec/factories/ci/trigger_requests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb index 5298d9fa7c3..2c0d004d267 100644 --- a/spec/factories/ci/trigger_requests.rb +++ b/spec/factories/ci/trigger_requests.rb @@ -3,7 +3,7 @@ FactoryGirl.define do factory :ci_trigger_request, class: Ci::TriggerRequest do factory :ci_trigger_request_with_variables do - trigger :ci_trigger + trigger factory: :ci_trigger variables do { -- cgit v1.2.1 From f00607431cd13a952731e36701ebc3b39e64d09b Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 11:27:38 +0100 Subject: Add delete feature to triggers API --- lib/api/triggers.rb | 19 +++++++++++++++++++ spec/requests/api/triggers_spec.rb | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 9a1e3fdc976..3cb7810241f 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -61,6 +61,25 @@ module API present triggers, with: Entities::Trigger end + + # Delete trigger + # + # Parameters: + # id (required) - The ID of a project + # trigger_id - The ID of trigger to delete + # Example Request: + # DELETE /projects/:id/triggers/:trigger_id + delete ':id/triggers/:trigger_id' do + authenticate! + authorize_admin_project + + trigger = user_project.triggers.where(id: params[:trigger_id].to_i).first + return not_found!('Trigger') unless trigger + + trigger.destroy + + present trigger, with: Entities::Trigger + end end end end diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 4b356108c80..4e073a55d9c 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -114,4 +114,37 @@ describe API::API do end end end + + describe 'DELETE /projects/:id/triggets/:trigger_id' do + context 'authenticated user with valid permissions' do + it 'should delete trigger' do + expect do + delete api("/projects/#{project.id}/triggers/#{trigger.id}", user) + end.to change{project.triggers.count}.by(-1) + expect(response.status).to eq(200) + end + + it 'should responde with 404 Not Found if requesting non-existing trigger' do + delete api("/projects/#{project.id}/triggers/9999", user) + + expect(response.status).to eq(404) + end + end + + context 'authenticated user with invalid permissions' do + it 'should not delete trigger' do + delete api("/projects/#{project.id}/triggers/#{trigger.id}", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthentikated user' do + it 'should not delete trigger' do + delete api("/projects/#{project.id}/triggers/#{trigger.id}") + + expect(response.status).to eq(401) + end + end + end end -- cgit v1.2.1 From 49c8bf4e9b510be51859dcc301cb46b29b750cb0 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 11:44:10 +0100 Subject: Add create feature to triggers API --- lib/api/triggers.rb | 16 ++++++++++++++++ spec/requests/api/triggers_spec.rb | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 3cb7810241f..38cf1e9a2e0 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -62,6 +62,22 @@ module API present triggers, with: Entities::Trigger end + # Create trigger + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # POST /projects/:id/triggers + post ':id/triggers' do + authenticate! + authorize_admin_project + + trigger = user_project.triggers.new + trigger.save + + present trigger, with: Entities::Trigger + end + # Delete trigger # # Parameters: diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 4e073a55d9c..316c2ae958d 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -115,6 +115,35 @@ describe API::API do end end + describe 'POST /projects/:id/triggets' do + context 'authenticated user with valid permissions' do + it 'should create trigger' do + expect do + post api("/projects/#{project.id}/triggers", user) + end.to change{project.triggers.count}.by(1) + + expect(response.status).to eq(201) + expect(json_response).to be_a(Hash) + end + end + + context 'authenticated user with invalid permissions' do + it 'should not create trigger' do + post api("/projects/#{project.id}/triggers", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthentikated user' do + it 'should not create trigger' do + post api("/projects/#{project.id}/triggers") + + expect(response.status).to eq(401) + end + end + end + describe 'DELETE /projects/:id/triggets/:trigger_id' do context 'authenticated user with valid permissions' do it 'should delete trigger' do -- cgit v1.2.1 From 8675664655c4e0f1e043afa88ff1fd75ae5a6a9e Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 12:25:16 +0100 Subject: Get show details feature to triggers API --- lib/api/triggers.rb | 26 ++++++++++++++++++ spec/requests/api/triggers_spec.rb | 56 ++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 38cf1e9a2e0..0e548b936cd 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -62,6 +62,32 @@ module API present triggers, with: Entities::Trigger end + # Get specific trigger of a project + # + # Parameters: + # id (required) - The ID of a project + # trigger_id (required) - The ID or `token` of a trigger to show; if trigger_id contains only digits it's + # treated as ID other ways it's reated as `key` + # Example Request: + # GET /projects/:id/triggers/:trigger_id + get ':id/triggers/:trigger_id' do + authenticate! + authorize_admin_project + + trigger_id = params[:trigger_id] + triggers = user_project.triggers + triggers = + if trigger_id.match(/^\d+$/) + triggers.where(id: trigger_id.to_i) + else + triggers.where(token: trigger_id) + end + + return not_found!('Trigger') if triggers.empty? + + present triggers.first, with: Entities::Trigger + end + # Create trigger # # Parameters: diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index 316c2ae958d..c1c2bb04b29 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -5,8 +5,8 @@ describe API::API do let(:user) { create(:user) } let(:user2) { create(:user) } - let!(:trigger_token) { 'secure token' } - let!(:trigger_token_2) { 'secure token 2' } + let!(:trigger_token) { 'secure_token' } + let!(:trigger_token_2) { 'secure_token_2' } let!(:project) { create(:project, creator_id: user.id) } let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) } let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) } @@ -86,7 +86,7 @@ describe API::API do end end - describe 'GET /projects/:id/triggets' do + describe 'GET /projects/:id/triggers' do context 'authenticated user with valid permissions' do it 'should return list of triggers' do get api("/projects/#{project.id}/triggers", user) @@ -115,7 +115,53 @@ describe API::API do end end - describe 'POST /projects/:id/triggets' do + describe 'GET /projects/:id/triggers/:triggers_id' do + context 'authenticated user with valid permissions' do + context 'ID is used as :trigger_id' do + it 'should return trigger details' do + get api("/projects/#{project.id}/triggers/#{trigger.id}", user) + + expect(response.status).to eq(200) + expect(json_response).to be_a(Hash) + expect(json_response['token']).to eq(trigger_token) + end + end + + context '`token` is used as :trigger_id' do + it 'should return trigger details' do + get api("/projects/#{project.id}/triggers/#{trigger.token}", user) + + expect(response.status).to eq(200) + expect(json_response).to be_a(Hash) + expect(json_response['id']).to eq(trigger.id) + end + end + + it 'should responde with 404 Not Found if requesting non-existing trigger' do + get api("/projects/#{project.id}/triggers/9999", user) + + expect(response.status).to eq(404) + end + end + + context 'authenticated user with invalid permissions' do + it 'should not return triggers list' do + get api("/projects/#{project.id}/triggers/#{trigger.id}", user2) + + expect(response.status).to eq(403) + end + end + + context 'unauthentikated user' do + it 'should not return triggers list' do + get api("/projects/#{project.id}/triggers/#{trigger.id}") + + expect(response.status).to eq(401) + end + end + end + + describe 'POST /projects/:id/triggers' do context 'authenticated user with valid permissions' do it 'should create trigger' do expect do @@ -144,7 +190,7 @@ describe API::API do end end - describe 'DELETE /projects/:id/triggets/:trigger_id' do + describe 'DELETE /projects/:id/triggers/:trigger_id' do context 'authenticated user with valid permissions' do it 'should delete trigger' do expect do -- cgit v1.2.1 From d17945bb4ef0750f546a9dea79df1abf4ef140a5 Mon Sep 17 00:00:00 2001 From: Felix Eckhofer Date: Tue, 5 Jan 2016 13:01:23 +0100 Subject: Remove misleading `ssh-dsa` The keytype field is actually `ssh-dss` for DSA keys and they will not be stored as `id_rsa.pub`. Note that newer version of ssh actually also support `ecdsa-sha2-nistp256` and others so it is also misleading to assume the field always starts with `ssh-`. --- doc/ssh/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index fe5b45dd432..64795233e41 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -9,7 +9,7 @@ already has one by running the following command: cat ~/.ssh/id_rsa.pub ``` -If you see a long string starting with `ssh-rsa` or `ssh-dsa`, you can skip the `ssh-keygen` step. +If you see a long string starting with `ssh-rsa`, you can skip the `ssh-keygen` step. 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 @@ -29,7 +29,7 @@ cat ~/.ssh/id_rsa.pub ``` Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your -user profile. Please copy the complete key starting with `ssh-` and ending +user profile. Please copy the complete key starting with `ssh-rsa` and ending with your username and host. To copy your public key to the clipboard, use code below. Depending on your -- cgit v1.2.1 From ca8639a4662c59b15ef4e0ed02b6587ce4049b0d Mon Sep 17 00:00:00 2001 From: Felix Eckhofer Date: Tue, 5 Jan 2016 13:02:45 +0100 Subject: Clarify the key generation step Users must not change the default filename or they will have to create a suitable .ssh/config which is out-of-scope here. --- doc/ssh/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index 64795233e41..c4b1fc3b626 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -20,8 +20,9 @@ To generate a new SSH key, use the following command: ssh-keygen -t rsa -C "$your_email" ``` This command will prompt you for a location and filename to store the key -pair and for a password. When prompted for the location and filename, you -can press enter to use the default. +pair and for a password. When prompted for the location and filename, just +press enter to use the default. If you use a different name, the key will not +be used automatically. Use the command below to show your public key: ```bash -- cgit v1.2.1 From 766a59630f8e004fbd99549dc6d27f7b29fff62f Mon Sep 17 00:00:00 2001 From: Felix Eckhofer Date: Tue, 5 Jan 2016 13:06:23 +0100 Subject: Fix grammar --- doc/ssh/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ssh/README.md b/doc/ssh/README.md index c4b1fc3b626..77eb53427e2 100644 --- a/doc/ssh/README.md +++ b/doc/ssh/README.md @@ -33,7 +33,7 @@ Copy-paste the key to the 'My SSH Keys' section under the 'SSH' tab in your user profile. Please copy the complete key starting with `ssh-rsa` and ending with your username and host. -To copy your public key to the clipboard, use code below. Depending on your +To copy your public key to the clipboard, use the code below. Depending on your OS you'll need to use a different command: **Windows:** -- cgit v1.2.1 From f60bceb988bd629f9adecc070ef5579d264f27c6 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 13:20:06 +0100 Subject: Add CI data to projcet entity --- lib/api/entities.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 26e7c956e8f..e8154e0f383 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -71,6 +71,9 @@ module API expose :avatar_url expose :star_count, :forks_count expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? } + + expose :build_allow_git_fetch, :build_timeout, :build_coverage_regex + expose :runners_token end class ProjectMember < UserBasic -- cgit v1.2.1 From 57a68c722c180be04dfe1682b1fbced11c8d735b Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 5 Jan 2016 13:59:32 +0100 Subject: Update project services to include all supported ones [ci skip] --- doc/project_services/project_services.md | 42 ++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md index 03937d20728..2d3e899383f 100644 --- a/doc/project_services/project_services.md +++ b/doc/project_services/project_services.md @@ -1,20 +1,30 @@ # Project Services - -__Project integrations with external services for continuous integration and more.__ + +Project services allow you to integrate GitLab with other applications. Below +is list of the currently supported ones. Click on the service links to see +further configuration instructions and details. Contributions are welcome. ## Services -- Assembla -- [Atlassian Bamboo CI](bamboo.md) An Atlassian product for continuous integration. -- Build box -- Campfire -- Emails on push -- Flowdock -- Gemnasium -- GitLab CI -- [HipChat](hipchat.md) An Atlassian product for private group chat and instant messaging. -- [Irker](irker.md) An IRC gateway to receive messages on repository updates. -- Pivotal Tracker -- Pushover -- Slack -- TeamCity +| Service | Description | +| ------- | ----------- | +| Asana | Asana - Teamwork without email | +| Assembla | Project Management Software (Source Commits Endpoint) | +| [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server | +| Buildkite | Continuous integration and deployments | +| Builds emails | Email the builds status to a list of recipients | +| Campfire | Simple web-based real-time group chat | +| Custom Issue Tracker | Custom issue tracker | +| Drone CI | Continuous Integration platform built on Docker, written in Go | +| Emails on push | Email the commits and diff of each push to a list of recipients | +| External Wiki | Replaces the link to the internal wiki with a link to an external wiki | +| Flowdock | Flowdock is a collaboration web app for technical teams | +| Gemnasium | Gemnasium monitors your project dependencies and alerts you about updates and security vulnerabilities | +| [HipChat](hipchat.md) | Private group chat and IM | +| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | +| JIRA | Jira issue tracker | +| JetBrains TeamCity CI | A continuous integration and build server | +| PivotalTracker | Project Management Software (Source Commits Endpoint) | +| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop | +| Redmine | Redmine issue tracker | +| Slack | A team communication tool for the 21st century | -- cgit v1.2.1 From 6ce01ca3ece007f135c6b5a9bc258fdd9e8d71de Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 5 Jan 2016 11:00:10 -0200 Subject: Validate README format before displaying MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Take the first previewable README file as project’s README, otherwise if none file is available, or we can’t preview any of them, we assume that project doesn’t have a README file. --- app/models/tree.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app/models/tree.rb b/app/models/tree.rb index 93b3246a668..e0e04d8859f 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -17,18 +17,16 @@ class Tree def readme return @readme if defined?(@readme) - available_readmes = blobs.select(&:readme?) + # Take the first previewable readme, or return nil if none is available or + # we can't preview any of them + readme_tree = blobs.find do |blob| + blob.readme? && (previewable?(blob.name) || plain?(blob.name)) + end - if available_readmes.count == 0 + if readme_tree.nil? return @readme = nil end - # Take the first previewable readme, or the first available readme, if we - # can't preview any of them - readme_tree = available_readmes.find do |readme| - previewable?(readme.name) - end || available_readmes.first - readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name) git_repo = repository.raw_repository -- cgit v1.2.1 From e57b506222f535774059cbb0f986621384c5a8f7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 5 Jan 2016 05:30:01 -0800 Subject: Suggest prefacing find command with sudo when base permissions are wrong Closes #5872 --- lib/tasks/gitlab/check.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 0469c5a61c3..2dc2953e328 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -431,7 +431,7 @@ namespace :gitlab do try_fixing_it( "sudo chmod -R ug+rwX,o-rwx #{repo_base_path}", "sudo chmod -R ug-s #{repo_base_path}", - "find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s" + "sudo find #{repo_base_path} -type d -print0 | sudo xargs -0 chmod g+s" ) for_more_information( see_installation_guide_section "GitLab Shell" -- cgit v1.2.1 From 8e33ec1deea60a176cd43092c913200d28ea04fe Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 5 Jan 2016 11:30:30 -0200 Subject: [ci skip] Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 47ef06bee54..72698e4fe3f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.4.0 (unreleased) - Fix version check image in Safari - Show 'All' tab by default in the builds page - Fix API project lookups when querying with a namespace with dots (Stan Hu) + - Validate README format before displaying v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) -- cgit v1.2.1 From 0a21731e3bc400ceb9898c9efbc2a186f5348e09 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 15:00:25 +0100 Subject: Add ci fields in project create/update feature API --- lib/api/helpers.rb | 2 +- lib/api/projects.rb | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index a4df810e755..563c12e4f74 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -157,7 +157,7 @@ module API def attributes_for_keys(keys, custom_params = nil) attrs = {} keys.each do |key| - if params[key].present? or (params.has_key?(key) and params[key] == false) + if params[key].present? or (params.has_key?(key) and (params[key].empty? or params[key] == false)) attrs[key] = params[key] end end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 0781236cf6d..7dd6b133f9b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -114,7 +114,10 @@ module API :namespace_id, :public, :visibility_level, - :import_url] + :import_url, + :build_allow_git_fetch, + :build_timeout, + :build_coverage_regex] attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(current_user, attrs).execute if @project.saved? @@ -159,7 +162,10 @@ module API :shared_runners_enabled, :public, :visibility_level, - :import_url] + :import_url, + :build_allow_git_fetch, + :build_timeout, + :build_coverage_regex] attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(user, attrs).execute if @project.saved? @@ -215,7 +221,10 @@ module API :snippets_enabled, :shared_runners_enabled, :public, - :visibility_level] + :visibility_level, + :build_allow_git_fetch, + :build_timeout, + :build_coverage_regex] attrs = map_public_to_visibility_level(attrs) authorize_admin_project authorize! :rename_project, user_project if attrs[:name].present? -- cgit v1.2.1 From 097faeb481db2a4956b41049c041d55f5da4e2c1 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 5 Jan 2016 16:24:42 +0100 Subject: Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running --- CHANGELOG | 1 + app/controllers/projects/merge_requests_controller.rb | 2 +- app/models/merge_request.rb | 16 +++++++++++----- app/views/projects/merge_requests/_show.html.haml | 2 +- lib/api/merge_requests.rb | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d841b149615..240fa43d6cb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ v 8.4.0 (unreleased) - Fix API project lookups when querying with a namespace with dots (Stan Hu) v 8.3.3 (unreleased) + - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) - Enable "Add key" button when user fills in a proper key (Stan Hu) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index ab5c953189c..de948d271c8 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -153,7 +153,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_check - @merge_request.check_if_can_be_merged if @merge_request.unchecked? + @merge_request.check_if_can_be_merged render partial: "projects/merge_requests/widget/show.html.haml", layout: false end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ac25d38eb63..30d0c2b5961 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -229,6 +229,8 @@ class MergeRequest < ActiveRecord::Base end def check_if_can_be_merged + return unless unchecked? + can_be_merged = project.repository.can_be_merged?(source_sha, target_branch) @@ -252,7 +254,11 @@ class MergeRequest < ActiveRecord::Base end def mergeable? - open? && !work_in_progress? && can_be_merged? + return false unless open? && !work_in_progress? + + check_if_can_be_merged + + can_be_merged? end def gitlab_merge_status @@ -452,6 +458,10 @@ class MergeRequest < ActiveRecord::Base !source_branch_exists? || !target_branch_exists? end + def broken? + self.commits.blank? || branch_missing? || cannot_be_merged? + end + def can_be_merged_by?(user) ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch) end @@ -507,8 +517,4 @@ class MergeRequest < ActiveRecord::Base def ci_commit @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project end - - def broken? - self.commits.blank? || branch_missing? || cannot_be_merged? - end end diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index ba7c2c01e93..095876450a0 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -38,7 +38,7 @@ = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" - - if @merge_request.open? && @merge_request.source_branch_exists? && @merge_request.can_be_merged? && @merge_request.can_be_merged_by?(current_user) + - if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user) .light.prepend-top-default You can also accept this merge request manually using the = succeed '.' do diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 3c1c6bda260..5c97fe1c88c 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -211,7 +211,7 @@ module API unauthorized! unless merge_request.can_be_merged_by?(current_user) not_allowed! if !merge_request.open? || merge_request.work_in_progress? - merge_request.check_if_can_be_merged if merge_request.unchecked? + merge_request.check_if_can_be_merged render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged? -- cgit v1.2.1 From 0bab4788ef870945feccbb102834fd89433dfef2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 5 Jan 2016 16:31:05 +0100 Subject: Satisfy Rubocop --- spec/models/project_services/asana_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb index 0db48c75d1d..f3d15f3c1ea 100644 --- a/spec/models/project_services/asana_service_spec.rb +++ b/spec/models/project_services/asana_service_spec.rb @@ -41,7 +41,7 @@ describe AsanaService, models: true do let(:project) { create(:project) } def create_data_for_commits(*messages) - data = { + { object_kind: 'push', ref: 'master', user_name: user.name, -- cgit v1.2.1 From a862ade55bf68f56734538b40e02e56036f8a1bd Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Tue, 5 Jan 2016 16:36:14 +0100 Subject: Update ./doc/api/ --- doc/api/README.md | 1 + doc/api/builds.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/builds.rb | 3 +- 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 doc/api/builds.md diff --git a/doc/api/README.md b/doc/api/README.md index 25a31b235cc..43ee24ddb63 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -23,6 +23,7 @@ - [Namespaces](namespaces.md) - [Settings](settings.md) - [Keys](keys.md) +- [Builds](builds.md) ## Clients diff --git a/doc/api/builds.md b/doc/api/builds.md new file mode 100644 index 00000000000..b716499dd36 --- /dev/null +++ b/doc/api/builds.md @@ -0,0 +1,206 @@ +# Builds API + +## List project builds + +Get a list of builds in a project. + +``` +GET /projects/:id/builds +``` + +Parameters: + +- `id` (required) - The ID of a project +- `scope` (optional) - The scope of builds to show (one of: `all`, `finished`, `running`; default: `all`) + +```json +[ + { + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-04T15:41:23.147Z", + "finished_at": null, + "id": 65, + "name": "brakeman", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" + }, + { + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-04T15:41:23.046Z", + "finished_at": null, + "id": 64, + "name": "rubocop", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" + } +] +``` + +## List commit builds + +Get a list of builds for specific commit in a project. + +``` +GET /projects/:id/builds/commit/:sha +``` + +Parameters: + +- `id` (required) - The ID of a project +- `sha` (required) - The SHA id of a commit +- `scope` (optional) - The scope of builds to show (one of: `all`, `finished`, `running`; default: `all`) + + +```json +[ + { + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-04T15:41:23.147Z", + "finished_at": null, + "id": 65, + "name": "brakeman", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" + }, + { + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-04T15:41:23.046Z", + "finished_at": null, + "id": 64, + "name": "rubocop", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" + } +] +``` + +## Get a single build + +Get a single build of a project + +``` +GET /projects/:id/builds/:build_id +``` + +Parameters: + +- `id` (required) - The ID of a project +- `build_id` (required) - The ID of a build + +```json +{ + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-04T15:41:23.046Z", + "finished_at": null, + "id": 64, + "name": "rubocop", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" +} +``` + +## Cancel a build + +Cancel a single build of a project + +``` +POST /projects/:id/builds/:build_id/cancel +``` + +Parameters: + +- `id` (required) - The ID of a project +- `build_id` (required) - The ID of a build + +```json +{ + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-05T15:33:25.936Z", + "finished_at": "2016-01-05T15:33:47.553Z", + "id": 66, + "name": "rubocop", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "canceled" +} +``` + +## Retry a build + +Retry a single build of a project + +``` +POST /projects/:id/builds/:build_id/retry +``` + +Parameters: + +- `id` (required) - The ID of a project +- `build_id` (required) - The ID of a build + +```json +{ + "commit": { + "committed_at": "2015-12-28T14:34:03.814Z", + "id": 2, + "ref": null, + "sha": "6b053ad388c531c21907f022933e5e81598db388" + }, + "created_at": "2016-01-05T15:33:25.936Z", + "finished_at": null, + "id": 66, + "name": "rubocop", + "ref": "master", + "runner": null, + "stage": "test", + "started_at": null, + "status": "pending" +} +``` diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 92bf849824c..6b0edcff820 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -8,7 +8,7 @@ module API # # Parameters: # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one of: all, finished, running) + # scope (optional) - The scope of builds to show (one of: all, finished, running; default: all) # page (optional) - The page number for pagination # per_page (ooptional) - The value of items per page to show # Example Request: @@ -24,6 +24,7 @@ module API # Parameters: # id (required) - The ID of a project # sha (required) - The SHA id of a commit + # scope (optional) - The scope of builds to show (one of: all, finished, running; default: all) # Example Request: # GET /projects/:id/builds/commit/:sha get ':id/builds/commit/:sha' do -- cgit v1.2.1 From 0b661516324862506d5ec30c44cac704346a90ad Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Tue, 5 Jan 2016 16:45:53 +0100 Subject: Remove icon from milestone reference. --- app/models/milestone.rb | 2 +- app/views/shared/issuable/_sidebar.html.haml | 3 ++- lib/banzai/filter/milestone_reference_filter.rb | 2 -- spec/lib/banzai/filter/milestone_reference_filter_spec.rb | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index e47b6440746..eaa2db2e247 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -76,7 +76,7 @@ class Milestone < ActiveRecord::Base end def reference_link_text(from_project = nil) - %Q{ }.html_safe + self.title + self.title end def expired? diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 9d65a621e53..79c5cc7f40a 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -28,7 +28,8 @@ %span.back-to-milestone = link_to namespace_project_milestone_path(@project.namespace, @project, issuable.milestone) do %strong - = issuable.milestone.reference_link_text + = icon('clock-o') + = issuable.milestone.title - else .light None .selectbox diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb index f99202af5e8..e88b27c1fae 100644 --- a/lib/banzai/filter/milestone_reference_filter.rb +++ b/lib/banzai/filter/milestone_reference_filter.rb @@ -3,8 +3,6 @@ require 'banzai' module Banzai module Filter # HTML filter that replaces milestone references with links. - # - # This filter supports cross-project references. class MilestoneReferenceFilter < AbstractReferenceFilter def self.object_class Milestone diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index c53e780d354..86b71210100 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -29,7 +29,7 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do it 'links with adjacent text' do doc = reference_filter("milestone (#{reference}.)") - expect(doc.to_html).to match(/\(<\/i> #{Regexp.escape(milestone.title)}<\/a>\.\)/) + expect(doc.to_html).to match(/\(#{Regexp.escape(milestone.title)}<\/a>\.\)/) end it 'includes a title attribute' do @@ -41,7 +41,7 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do milestone.update_attribute(:title, %{">whatever Date: Tue, 5 Jan 2016 18:17:55 +0100 Subject: Fix notification spec --- spec/services/notification_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index b5c7b01357a..6d219f35895 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -53,7 +53,7 @@ describe NotificationService, services: true do add_users_with_subscription(note.project, issue) # Ensure create SentNotification by noteable = issue 6 times, not noteable = note - expect(SentNotification).to receive(:record).with(issue, any_args).exactly(6).times + expect(SentNotification).to receive(:record).with(issue, any_args).exactly(7).times ActionMailer::Base.deliveries.clear -- cgit v1.2.1 From 95f1fe724a47b12b1d66a722be13d15c5fbd2688 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Dec 2015 16:52:45 -0200 Subject: Import GitHub Pull Requests into GitLab --- lib/gitlab/github_import/importer.rb | 64 ++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index b5720f6e2cb..656fac9ac7e 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -12,7 +12,16 @@ module Gitlab end def execute - #Issues && Comments + import_issues + import_pull_requests + + true + end + + private + + def import_issues + # Issues && Comments client.list_issues(project.import_source, state: :all, sort: :created, direction: :asc).each do |issue| @@ -33,18 +42,59 @@ module Gitlab description: body, title: issue.title, state: issue.state == 'closed' ? 'closed' : 'opened', - author_id: gl_user_id(project, issue.user.id) + author_id: gl_author_id(project, issue.user.id) ) end end end - private + def import_pull_requests + client.pull_requests(project.import_source, state: :all, + sort: :created, + direction: :asc).each do |pull_request| + body = @formatter.author_line(pull_request.user.login) + body += pull_request.body || "" + + source_branch = pull_request.head.ref + target_branch = pull_request.base.ref + + merge_request = MergeRequest.create!( + title: pull_request.title, + description: body, + source_project: project, + source_branch: source_branch, + target_project: project, + target_branch: target_branch, + state: merge_request_state(pull_request), + author_id: gl_author_id(project, pull_request.user.id), + assignee_id: gl_user_id(pull_request.assignee.try(:id)), + created_at: pull_request.created_at, + updated_at: pull_request.updated_at + ) + end + end + + def merge_request_state(pull_request) + case true + when pull_request.state == 'closed' && pull_request.merged_at.present? + 'merged' + when pull_request.state == 'closed' + 'closed' + else + 'opened' + end + end + + def gl_author_id(project, github_id) + gl_user_id(github_id) || project.creator_id + end - def gl_user_id(project, github_id) - user = User.joins(:identities). - find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s) - (user && user.id) || project.creator_id + def gl_user_id(github_id) + if github_id + User.joins(:identities). + find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). + try(:id) + end end end end -- cgit v1.2.1 From 223f7f53453d544a8c46c75ca9c54b8b60620961 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Dec 2015 19:08:54 -0200 Subject: Import comments on GitHub Pull Request into GitLab --- lib/gitlab/github_import/importer.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 656fac9ac7e..35a2930d4a1 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -71,9 +71,23 @@ module Gitlab created_at: pull_request.created_at, updated_at: pull_request.updated_at ) + + client.issue_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end end end + def format_body(author, body) + @formatter.author_line(author) + (body || "") + end + def merge_request_state(pull_request) case true when pull_request.state == 'closed' && pull_request.merged_at.present? -- cgit v1.2.1 From f19bf0eaa73007081f2ab10f6a0fb176d5356e36 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Dec 2015 19:09:09 -0200 Subject: Import comments on the diff of a GitHub Pull Request into GitLab --- lib/gitlab/github_import/importer.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 35a2930d4a1..4b753d24601 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -81,6 +81,18 @@ module Gitlab updated_at: c.updated_at ) end + + client.pull_request_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + commit_id: c.commit_id, + line_code: generate_line_code(c.path, c.position), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end end end @@ -99,6 +111,10 @@ module Gitlab end end + def generate_line_code(file_path, position) + Gitlab::Diff::LineCode.generate(file_path, position, 0) + end + def gl_author_id(project, github_id) gl_user_id(github_id) || project.creator_id end -- cgit v1.2.1 From d72b25811e3f0b722ae1c0906e2fe7dffd312403 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Dec 2015 19:42:56 -0200 Subject: Doesn't import GitHub PR where branches were no longer available --- lib/gitlab/github_import/importer.rb | 82 +++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 4b753d24601..f8a9e0d55ab 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -52,50 +52,56 @@ module Gitlab client.pull_requests(project.import_source, state: :all, sort: :created, direction: :asc).each do |pull_request| - body = @formatter.author_line(pull_request.user.login) - body += pull_request.body || "" - - source_branch = pull_request.head.ref - target_branch = pull_request.base.ref - - merge_request = MergeRequest.create!( - title: pull_request.title, - description: body, - source_project: project, - source_branch: source_branch, - target_project: project, - target_branch: target_branch, - state: merge_request_state(pull_request), - author_id: gl_author_id(project, pull_request.user.id), - assignee_id: gl_user_id(pull_request.assignee.try(:id)), - created_at: pull_request.created_at, - updated_at: pull_request.updated_at - ) - - client.issue_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at + source_branch = find_branch(pull_request.head.ref) + target_branch = find_branch(pull_request.base.ref) + + if source_branch && target_branch + # Pull Request + merge_request = MergeRequest.create!( + title: pull_request.title, + description: format_body(pull_request.user.login, pull_request.body), + source_project: project, + source_branch: source_branch.name, + target_project: project, + target_branch: target_branch.name, + state: merge_request_state(pull_request), + author_id: gl_author_id(project, pull_request.user.id), + assignee_id: gl_user_id(pull_request.assignee.try(:id)), + created_at: pull_request.created_at, + updated_at: pull_request.updated_at ) - end - client.pull_request_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - commit_id: c.commit_id, - line_code: generate_line_code(c.path, c.position), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at - ) + # Comments on Pull Request + client.issue_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end + + # Comments on Pull Request diff + client.pull_request_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + commit_id: c.commit_id, + line_code: generate_line_code(c.path, c.position), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end end end end + def find_branch(name) + project.repository.find_branch(name) + end + def format_body(author, body) @formatter.author_line(author) + (body || "") end -- cgit v1.2.1 From 6c846ef83d51a176002027e89245a4ea62b4f2bf Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 21 Dec 2015 21:17:57 -0200 Subject: Extract methods to import comments on a GitHub Pull Request --- lib/gitlab/github_import/importer.rb | 52 +++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index f8a9e0d55ab..2c64f5cebc7 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -56,7 +56,6 @@ module Gitlab target_branch = find_branch(pull_request.base.ref) if source_branch && target_branch - # Pull Request merge_request = MergeRequest.create!( title: pull_request.title, description: format_body(pull_request.user.login, pull_request.body), @@ -71,33 +70,38 @@ module Gitlab updated_at: pull_request.updated_at ) - # Comments on Pull Request - client.issue_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at - ) - end - - # Comments on Pull Request diff - client.pull_request_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - commit_id: c.commit_id, - line_code: generate_line_code(c.path, c.position), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at - ) - end + import_comments_on_pull_request(merge_request, pull_request) + import_comments_on_pull_request_diff(merge_request, pull_request) end end end + def import_comments_on_pull_request(merge_request, pull_request) + client.issue_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end + end + + def import_comments_on_pull_request_diff(merge_request, pull_request) + client.pull_request_comments(project.import_source, pull_request.number).each do |c| + merge_request.notes.create!( + project: project, + note: format_body(c.user.login, c.body), + commit_id: c.commit_id, + line_code: generate_line_code(c.path, c.position), + author_id: gl_author_id(project, c.user.id), + created_at: c.created_at, + updated_at: c.updated_at + ) + end + end + def find_branch(name) project.repository.find_branch(name) end -- cgit v1.2.1 From dc72a8b30502dd28bf850c2dfdbf31b687fde5d3 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 23 Dec 2015 15:04:46 -0200 Subject: Refactoring GithubImport::Importer --- lib/gitlab/github_import/comment.rb | 59 ++++++++ lib/gitlab/github_import/importer.rb | 73 ++-------- lib/gitlab/github_import/pull_request.rb | 103 ++++++++++++++ spec/lib/gitlab/github_import/comment_spec.rb | 80 +++++++++++ spec/lib/gitlab/github_import/pull_request_spec.rb | 153 +++++++++++++++++++++ 5 files changed, 407 insertions(+), 61 deletions(-) create mode 100644 lib/gitlab/github_import/comment.rb create mode 100644 lib/gitlab/github_import/pull_request.rb create mode 100644 spec/lib/gitlab/github_import/comment_spec.rb create mode 100644 spec/lib/gitlab/github_import/pull_request_spec.rb diff --git a/lib/gitlab/github_import/comment.rb b/lib/gitlab/github_import/comment.rb new file mode 100644 index 00000000000..55de78f889d --- /dev/null +++ b/lib/gitlab/github_import/comment.rb @@ -0,0 +1,59 @@ +module Gitlab + module GithubImport + class Comment + attr_reader :project, :raw_data + + def initialize(project, raw_data) + @project = project + @raw_data = raw_data + @formatter = Gitlab::ImportFormatter.new + end + + def attributes + { + project: project, + note: note, + commit_id: raw_data.commit_id, + line_code: line_code, + author_id: author_id, + created_at: raw_data.created_at, + updated_at: raw_data.updated_at + } + end + + private + + def author + raw_data.user.login + end + + def author_id + gl_user_id(raw_data.user.id) || project.creator_id + end + + def body + raw_data.body || "" + end + + def line_code + if on_diff? + Gitlab::Diff::LineCode.generate(raw_data.path, raw_data.position, 0) + end + end + + def on_diff? + raw_data.path && raw_data.position + end + + def note + @formatter.author_line(author) + body + end + + def gl_user_id(github_id) + User.joins(:identities). + find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). + try(:id) + end + end + end +end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 2c64f5cebc7..7c495655012 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -51,80 +51,31 @@ module Gitlab def import_pull_requests client.pull_requests(project.import_source, state: :all, sort: :created, - direction: :asc).each do |pull_request| - source_branch = find_branch(pull_request.head.ref) - target_branch = find_branch(pull_request.base.ref) + direction: :asc).each do |raw_data| + pull_request = PullRequest.new(project, raw_data) - if source_branch && target_branch - merge_request = MergeRequest.create!( - title: pull_request.title, - description: format_body(pull_request.user.login, pull_request.body), - source_project: project, - source_branch: source_branch.name, - target_project: project, - target_branch: target_branch.name, - state: merge_request_state(pull_request), - author_id: gl_author_id(project, pull_request.user.id), - assignee_id: gl_user_id(pull_request.assignee.try(:id)), - created_at: pull_request.created_at, - updated_at: pull_request.updated_at - ) - - import_comments_on_pull_request(merge_request, pull_request) - import_comments_on_pull_request_diff(merge_request, pull_request) + if pull_request.valid? + merge_request = MergeRequest.create!(pull_request.attributes) + import_comments_on_pull_request(merge_request, raw_data) + import_comments_on_pull_request_diff(merge_request, raw_data) end end end def import_comments_on_pull_request(merge_request, pull_request) - client.issue_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at - ) + client.issue_comments(project.import_source, pull_request.number).each do |raw_data| + comment = Comment.new(project, raw_data) + merge_request.notes.create!(comment.attributes) end end def import_comments_on_pull_request_diff(merge_request, pull_request) - client.pull_request_comments(project.import_source, pull_request.number).each do |c| - merge_request.notes.create!( - project: project, - note: format_body(c.user.login, c.body), - commit_id: c.commit_id, - line_code: generate_line_code(c.path, c.position), - author_id: gl_author_id(project, c.user.id), - created_at: c.created_at, - updated_at: c.updated_at - ) + client.pull_request_comments(project.import_source, pull_request.number).each do |raw_data| + comment = Comment.new(project, raw_data) + merge_request.notes.create!(comment.attributes) end end - def find_branch(name) - project.repository.find_branch(name) - end - - def format_body(author, body) - @formatter.author_line(author) + (body || "") - end - - def merge_request_state(pull_request) - case true - when pull_request.state == 'closed' && pull_request.merged_at.present? - 'merged' - when pull_request.state == 'closed' - 'closed' - else - 'opened' - end - end - - def generate_line_code(file_path, position) - Gitlab::Diff::LineCode.generate(file_path, position, 0) - end - def gl_author_id(project, github_id) gl_user_id(github_id) || project.creator_id end diff --git a/lib/gitlab/github_import/pull_request.rb b/lib/gitlab/github_import/pull_request.rb new file mode 100644 index 00000000000..61e846472f2 --- /dev/null +++ b/lib/gitlab/github_import/pull_request.rb @@ -0,0 +1,103 @@ +module Gitlab + module GithubImport + class PullRequest + attr_reader :project, :raw_data + + def initialize(project, raw_data) + @project = project + @raw_data = raw_data + @formatter = Gitlab::ImportFormatter.new + end + + def attributes + { + title: raw_data.title, + description: description, + source_project: source_project, + source_branch: source_branch.name, + target_project: target_project, + target_branch: target_branch.name, + state: state, + author_id: author_id, + assignee_id: assignee_id, + created_at: raw_data.created_at, + updated_at: updated_at + } + end + + def valid? + source_branch.present? && target_branch.present? + end + + private + + def assigned? + raw_data.assignee.present? + end + + def assignee_id + if assigned? + gl_user_id(raw_data.assignee.id) + end + end + + def author + raw_data.user.login + end + + def author_id + gl_user_id(raw_data.user.id) || project.creator_id + end + + def body + raw_data.body || "" + end + + def description + @formatter.author_line(author) + body + end + + def source_project + project + end + + def source_branch + source_project.repository.find_branch(raw_data.head.ref) + end + + def target_project + project + end + + def target_branch + target_project.repository.find_branch(raw_data.base.ref) + end + + def state + @state ||= case true + when raw_data.state == 'closed' && raw_data.merged_at.present? + 'merged' + when raw_data.state == 'closed' + 'closed' + else + 'opened' + end + end + + def updated_at + case state + when 'merged' then raw_data.merged_at + when 'closed' then raw_data.closed_at + else + raw_data.updated_at + end + end + + def gl_user_id(github_id) + User.joins(:identities). + find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). + try(:id) + end + end + end +end diff --git a/spec/lib/gitlab/github_import/comment_spec.rb b/spec/lib/gitlab/github_import/comment_spec.rb new file mode 100644 index 00000000000..ff6b115574b --- /dev/null +++ b/spec/lib/gitlab/github_import/comment_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::Comment, lib: true do + let(:project) { create(:project) } + let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } + let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } + let(:base_data) do + { + body: "I'm having a problem with this.", + user: octocat, + created_at: created_at, + updated_at: updated_at + } + end + + subject(:comment) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when do not reference a portion of the diff' do + let(:raw_data) { OpenStruct.new(base_data) } + + it 'returns formatted attributes' do + expected = { + project: project, + note: "*Created by: octocat*\n\nI'm having a problem with this.", + commit_id: nil, + line_code: nil, + author_id: project.creator_id, + created_at: created_at, + updated_at: updated_at + } + + expect(comment.attributes).to eq(expected) + end + end + + context 'when on a portion of the diff' do + let(:diff_data) do + { + body: 'Great stuff', + commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', + diff_hunk: '@@ -16,33 +16,40 @@ public class Connection : IConnection...', + path: 'file1.txt', + position: 1 + } + end + + let(:raw_data) { OpenStruct.new(base_data.merge(diff_data)) } + + it 'returns formatted attributes' do + expected = { + project: project, + note: "*Created by: octocat*\n\nGreat stuff", + commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', + line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_0_1', + author_id: project.creator_id, + created_at: created_at, + updated_at: updated_at + } + + expect(comment.attributes).to eq(expected) + end + end + + context 'when author is a GitLab user' do + let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + + it 'returns project#creator_id as author_id when is not a GitLab user' do + expect(comment.attributes.fetch(:author_id)).to eq project.creator_id + end + + it 'returns GitLab user id as author_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(comment.attributes.fetch(:author_id)).to eq gl_user.id + end + end + end +end diff --git a/spec/lib/gitlab/github_import/pull_request_spec.rb b/spec/lib/gitlab/github_import/pull_request_spec.rb new file mode 100644 index 00000000000..6ac32a78955 --- /dev/null +++ b/spec/lib/gitlab/github_import/pull_request_spec.rb @@ -0,0 +1,153 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::PullRequest, lib: true do + let(:project) { create(:project) } + let(:source_branch) { OpenStruct.new(ref: 'feature') } + let(:target_branch) { OpenStruct.new(ref: 'master') } + let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:base_data) do + { + state: 'open', + title: 'New feature', + body: 'Please pull these awesome changes', + head: source_branch, + base: target_branch, + assignee: nil, + user: octocat, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + merged_at: nil + } + end + + subject(:pull_request) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when pull request is open' do + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'opened', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: updated_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when pull request is closed' do + let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'closed', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: closed_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when pull request is merged' do + let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'merged', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: merged_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when it is assigned to someone' do + let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + + it 'returns nil as assigned_id when is not a GitLab user' do + expect(pull_request.attributes.fetch(:assignee_id)).to be_nil + end + + it 'returns GitLab user id as assigned_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id + end + end + + context 'when author is a GitLab user' do + let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + + it 'returns project#creator_id as author_id when is not a GitLab user' do + expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id + end + + it 'returns GitLab user id as author_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id + end + end + end + + describe '#valid?' do + let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } + + context 'when source and target branches exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } + + it 'returns true' do + expect(pull_request.valid?).to eq true + end + end + + context 'when source branch doesn not exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } + + it 'returns false' do + expect(pull_request.valid?).to eq false + end + end + + context 'when target branch doesn not exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } + + it 'returns false' do + expect(pull_request.valid?).to eq false + end + end + end +end -- cgit v1.2.1 From 98909dd12cd27b85921962326bcaf651c092dcd5 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 23 Dec 2015 20:02:59 -0200 Subject: Generate separate comments when importing GitHub Issues into GitLab --- lib/gitlab/github_import/base_formatter.rb | 21 +++ lib/gitlab/github_import/comment.rb | 59 -------- lib/gitlab/github_import/comment_formatter.rb | 45 ++++++ lib/gitlab/github_import/importer.rb | 59 +++----- lib/gitlab/github_import/issue_formatter.rb | 66 +++++++++ lib/gitlab/github_import/pull_request.rb | 103 ------------- lib/gitlab/github_import/pull_request_formatter.rb | 93 ++++++++++++ .../gitlab/github_import/comment_formatter_spec.rb | 80 ++++++++++ spec/lib/gitlab/github_import/comment_spec.rb | 80 ---------- .../gitlab/github_import/issue_formatter_spec.rb | 139 ++++++++++++++++++ .../github_import/pull_request_formatter_spec.rb | 162 +++++++++++++++++++++ spec/lib/gitlab/github_import/pull_request_spec.rb | 153 ------------------- 12 files changed, 625 insertions(+), 435 deletions(-) create mode 100644 lib/gitlab/github_import/base_formatter.rb delete mode 100644 lib/gitlab/github_import/comment.rb create mode 100644 lib/gitlab/github_import/comment_formatter.rb create mode 100644 lib/gitlab/github_import/issue_formatter.rb delete mode 100644 lib/gitlab/github_import/pull_request.rb create mode 100644 lib/gitlab/github_import/pull_request_formatter.rb create mode 100644 spec/lib/gitlab/github_import/comment_formatter_spec.rb delete mode 100644 spec/lib/gitlab/github_import/comment_spec.rb create mode 100644 spec/lib/gitlab/github_import/issue_formatter_spec.rb create mode 100644 spec/lib/gitlab/github_import/pull_request_formatter_spec.rb delete mode 100644 spec/lib/gitlab/github_import/pull_request_spec.rb diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb new file mode 100644 index 00000000000..202263c6742 --- /dev/null +++ b/lib/gitlab/github_import/base_formatter.rb @@ -0,0 +1,21 @@ +module Gitlab + module GithubImport + class BaseFormatter + attr_reader :formatter, :project, :raw_data + + def initialize(project, raw_data) + @project = project + @raw_data = raw_data + @formatter = Gitlab::ImportFormatter.new + end + + private + + def gl_user_id(github_id) + User.joins(:identities). + find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). + try(:id) + end + end + end +end diff --git a/lib/gitlab/github_import/comment.rb b/lib/gitlab/github_import/comment.rb deleted file mode 100644 index 55de78f889d..00000000000 --- a/lib/gitlab/github_import/comment.rb +++ /dev/null @@ -1,59 +0,0 @@ -module Gitlab - module GithubImport - class Comment - attr_reader :project, :raw_data - - def initialize(project, raw_data) - @project = project - @raw_data = raw_data - @formatter = Gitlab::ImportFormatter.new - end - - def attributes - { - project: project, - note: note, - commit_id: raw_data.commit_id, - line_code: line_code, - author_id: author_id, - created_at: raw_data.created_at, - updated_at: raw_data.updated_at - } - end - - private - - def author - raw_data.user.login - end - - def author_id - gl_user_id(raw_data.user.id) || project.creator_id - end - - def body - raw_data.body || "" - end - - def line_code - if on_diff? - Gitlab::Diff::LineCode.generate(raw_data.path, raw_data.position, 0) - end - end - - def on_diff? - raw_data.path && raw_data.position - end - - def note - @formatter.author_line(author) + body - end - - def gl_user_id(github_id) - User.joins(:identities). - find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). - try(:id) - end - end - end -end diff --git a/lib/gitlab/github_import/comment_formatter.rb b/lib/gitlab/github_import/comment_formatter.rb new file mode 100644 index 00000000000..7d58e53991a --- /dev/null +++ b/lib/gitlab/github_import/comment_formatter.rb @@ -0,0 +1,45 @@ +module Gitlab + module GithubImport + class CommentFormatter < BaseFormatter + def attributes + { + project: project, + note: note, + commit_id: raw_data.commit_id, + line_code: line_code, + author_id: author_id, + created_at: raw_data.created_at, + updated_at: raw_data.updated_at + } + end + + private + + def author + raw_data.user.login + end + + def author_id + gl_user_id(raw_data.user.id) || project.creator_id + end + + def body + raw_data.body || "" + end + + def line_code + if on_diff? + Gitlab::Diff::LineCode.generate(raw_data.path, raw_data.position, 0) + end + end + + def on_diff? + raw_data.path && raw_data.position + end + + def note + formatter.author_line(author) + body + end + end + end +end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 7c495655012..38ca7372202 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -21,29 +21,17 @@ module Gitlab private def import_issues - # Issues && Comments client.list_issues(project.import_source, state: :all, sort: :created, - direction: :asc).each do |issue| - if issue.pull_request.nil? + direction: :asc).each do |raw_data| + gh_issue = IssueFormatter.new(project, raw_data) - body = @formatter.author_line(issue.user.login) - body += issue.body || "" + if gh_issue.valid? + issue = Issue.create!(gh_issue.attributes) - if issue.comments > 0 - body += @formatter.comments_header - - client.issue_comments(project.import_source, issue.number).each do |c| - body += @formatter.comment(c.user.login, c.created_at, c.body) - end + if gh_issue.has_comments? + import_comments(gh_issue.number, issue) end - - project.issues.create!( - description: body, - title: issue.title, - state: issue.state == 'closed' ? 'closed' : 'opened', - author_id: gl_author_id(project, issue.user.id) - ) end end end @@ -52,39 +40,30 @@ module Gitlab client.pull_requests(project.import_source, state: :all, sort: :created, direction: :asc).each do |raw_data| - pull_request = PullRequest.new(project, raw_data) + pull_request = PullRequestFormatter.new(project, raw_data) if pull_request.valid? merge_request = MergeRequest.create!(pull_request.attributes) - import_comments_on_pull_request(merge_request, raw_data) - import_comments_on_pull_request_diff(merge_request, raw_data) + import_comments(pull_request.number, merge_request) + import_comments_on_diff(pull_request.number, merge_request) end end end - def import_comments_on_pull_request(merge_request, pull_request) - client.issue_comments(project.import_source, pull_request.number).each do |raw_data| - comment = Comment.new(project, raw_data) - merge_request.notes.create!(comment.attributes) - end - end - - def import_comments_on_pull_request_diff(merge_request, pull_request) - client.pull_request_comments(project.import_source, pull_request.number).each do |raw_data| - comment = Comment.new(project, raw_data) - merge_request.notes.create!(comment.attributes) - end + def import_comments(issue_number, noteable) + comments = client.issue_comments(project.import_source, issue_number) + create_comments(comments, noteable) end - def gl_author_id(project, github_id) - gl_user_id(github_id) || project.creator_id + def import_comments_on_diff(pull_request_number, merge_request) + comments = client.pull_request_comments(project.import_source, pull_request_number) + create_comments(comments, merge_request) end - def gl_user_id(github_id) - if github_id - User.joins(:identities). - find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). - try(:id) + def create_comments(comments, noteable) + comments.each do |raw_data| + comment = CommentFormatter.new(project, raw_data) + noteable.notes.create!(comment.attributes) end end end diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb new file mode 100644 index 00000000000..1e3ba44f27c --- /dev/null +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -0,0 +1,66 @@ +module Gitlab + module GithubImport + class IssueFormatter < BaseFormatter + def attributes + { + project: project, + title: raw_data.title, + description: description, + state: state, + author_id: author_id, + assignee_id: assignee_id, + created_at: raw_data.created_at, + updated_at: updated_at + } + end + + def has_comments? + raw_data.comments > 0 + end + + def number + raw_data.number + end + + def valid? + raw_data.pull_request.nil? + end + + private + + def assigned? + raw_data.assignee.present? + end + + def assignee_id + if assigned? + gl_user_id(raw_data.assignee.id) + end + end + + def author + raw_data.user.login + end + + def author_id + gl_user_id(raw_data.user.id) || project.creator_id + end + + def body + raw_data.body || "" + end + + def description + @formatter.author_line(author) + body + end + + def state + raw_data.state == 'closed' ? 'closed' : 'opened' + end + + def updated_at + state == 'closed' ? raw_data.closed_at : raw_data.updated_at + end + end + end +end diff --git a/lib/gitlab/github_import/pull_request.rb b/lib/gitlab/github_import/pull_request.rb deleted file mode 100644 index 61e846472f2..00000000000 --- a/lib/gitlab/github_import/pull_request.rb +++ /dev/null @@ -1,103 +0,0 @@ -module Gitlab - module GithubImport - class PullRequest - attr_reader :project, :raw_data - - def initialize(project, raw_data) - @project = project - @raw_data = raw_data - @formatter = Gitlab::ImportFormatter.new - end - - def attributes - { - title: raw_data.title, - description: description, - source_project: source_project, - source_branch: source_branch.name, - target_project: target_project, - target_branch: target_branch.name, - state: state, - author_id: author_id, - assignee_id: assignee_id, - created_at: raw_data.created_at, - updated_at: updated_at - } - end - - def valid? - source_branch.present? && target_branch.present? - end - - private - - def assigned? - raw_data.assignee.present? - end - - def assignee_id - if assigned? - gl_user_id(raw_data.assignee.id) - end - end - - def author - raw_data.user.login - end - - def author_id - gl_user_id(raw_data.user.id) || project.creator_id - end - - def body - raw_data.body || "" - end - - def description - @formatter.author_line(author) + body - end - - def source_project - project - end - - def source_branch - source_project.repository.find_branch(raw_data.head.ref) - end - - def target_project - project - end - - def target_branch - target_project.repository.find_branch(raw_data.base.ref) - end - - def state - @state ||= case true - when raw_data.state == 'closed' && raw_data.merged_at.present? - 'merged' - when raw_data.state == 'closed' - 'closed' - else - 'opened' - end - end - - def updated_at - case state - when 'merged' then raw_data.merged_at - when 'closed' then raw_data.closed_at - else - raw_data.updated_at - end - end - - def gl_user_id(github_id) - User.joins(:identities). - find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s). - try(:id) - end - end - end -end diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb new file mode 100644 index 00000000000..42dc09c2ac5 --- /dev/null +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -0,0 +1,93 @@ +module Gitlab + module GithubImport + class PullRequestFormatter < BaseFormatter + def attributes + { + title: raw_data.title, + description: description, + source_project: source_project, + source_branch: source_branch.name, + target_project: target_project, + target_branch: target_branch.name, + state: state, + author_id: author_id, + assignee_id: assignee_id, + created_at: raw_data.created_at, + updated_at: updated_at + } + end + + def number + raw_data.number + end + + def valid? + source_branch.present? && target_branch.present? + end + + private + + def assigned? + raw_data.assignee.present? + end + + def assignee_id + if assigned? + gl_user_id(raw_data.assignee.id) + end + end + + def author + raw_data.user.login + end + + def author_id + gl_user_id(raw_data.user.id) || project.creator_id + end + + def body + raw_data.body || "" + end + + def description + formatter.author_line(author) + body + end + + def source_project + project + end + + def source_branch + source_project.repository.find_branch(raw_data.head.ref) + end + + def target_project + project + end + + def target_branch + target_project.repository.find_branch(raw_data.base.ref) + end + + def state + @state ||= case true + when raw_data.state == 'closed' && raw_data.merged_at.present? + 'merged' + when raw_data.state == 'closed' + 'closed' + else + 'opened' + end + end + + def updated_at + case state + when 'merged' then raw_data.merged_at + when 'closed' then raw_data.closed_at + else + raw_data.updated_at + end + end + end + end +end diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb new file mode 100644 index 00000000000..a324a82e69f --- /dev/null +++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb @@ -0,0 +1,80 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::CommentFormatter, lib: true do + let(:project) { create(:project) } + let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } + let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } + let(:base_data) do + { + body: "I'm having a problem with this.", + user: octocat, + created_at: created_at, + updated_at: updated_at + } + end + + subject(:comment) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when do not reference a portion of the diff' do + let(:raw_data) { OpenStruct.new(base_data) } + + it 'returns formatted attributes' do + expected = { + project: project, + note: "*Created by: octocat*\n\nI'm having a problem with this.", + commit_id: nil, + line_code: nil, + author_id: project.creator_id, + created_at: created_at, + updated_at: updated_at + } + + expect(comment.attributes).to eq(expected) + end + end + + context 'when on a portion of the diff' do + let(:diff_data) do + { + body: 'Great stuff', + commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', + diff_hunk: '@@ -16,33 +16,40 @@ public class Connection : IConnection...', + path: 'file1.txt', + position: 1 + } + end + + let(:raw_data) { OpenStruct.new(base_data.merge(diff_data)) } + + it 'returns formatted attributes' do + expected = { + project: project, + note: "*Created by: octocat*\n\nGreat stuff", + commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', + line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_0_1', + author_id: project.creator_id, + created_at: created_at, + updated_at: updated_at + } + + expect(comment.attributes).to eq(expected) + end + end + + context 'when author is a GitLab user' do + let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + + it 'returns project#creator_id as author_id when is not a GitLab user' do + expect(comment.attributes.fetch(:author_id)).to eq project.creator_id + end + + it 'returns GitLab user id as author_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(comment.attributes.fetch(:author_id)).to eq gl_user.id + end + end + end +end diff --git a/spec/lib/gitlab/github_import/comment_spec.rb b/spec/lib/gitlab/github_import/comment_spec.rb deleted file mode 100644 index ff6b115574b..00000000000 --- a/spec/lib/gitlab/github_import/comment_spec.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'spec_helper' - -describe Gitlab::GithubImport::Comment, lib: true do - let(:project) { create(:project) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } - let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } - let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } - let(:base_data) do - { - body: "I'm having a problem with this.", - user: octocat, - created_at: created_at, - updated_at: updated_at - } - end - - subject(:comment) { described_class.new(project, raw_data)} - - describe '#attributes' do - context 'when do not reference a portion of the diff' do - let(:raw_data) { OpenStruct.new(base_data) } - - it 'returns formatted attributes' do - expected = { - project: project, - note: "*Created by: octocat*\n\nI'm having a problem with this.", - commit_id: nil, - line_code: nil, - author_id: project.creator_id, - created_at: created_at, - updated_at: updated_at - } - - expect(comment.attributes).to eq(expected) - end - end - - context 'when on a portion of the diff' do - let(:diff_data) do - { - body: 'Great stuff', - commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - diff_hunk: '@@ -16,33 +16,40 @@ public class Connection : IConnection...', - path: 'file1.txt', - position: 1 - } - end - - let(:raw_data) { OpenStruct.new(base_data.merge(diff_data)) } - - it 'returns formatted attributes' do - expected = { - project: project, - note: "*Created by: octocat*\n\nGreat stuff", - commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_0_1', - author_id: project.creator_id, - created_at: created_at, - updated_at: updated_at - } - - expect(comment.attributes).to eq(expected) - end - end - - context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } - - it 'returns project#creator_id as author_id when is not a GitLab user' do - expect(comment.attributes.fetch(:author_id)).to eq project.creator_id - end - - it 'returns GitLab user id as author_id when is a GitLab user' do - gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') - - expect(comment.attributes.fetch(:author_id)).to eq gl_user.id - end - end - end -end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb new file mode 100644 index 00000000000..fd05428b322 --- /dev/null +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -0,0 +1,139 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::IssueFormatter, lib: true do + let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } + let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + + let(:base_data) do + { + number: 1347, + state: 'open', + title: 'Found a bug', + body: "I'm having a problem with this.", + assignee: nil, + user: octocat, + comments: 0, + pull_request: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil + } + end + + subject(:issue) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when issue is open' do + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + + it 'returns formatted attributes' do + expected = { + project: project, + title: 'Found a bug', + description: "*Created by: octocat*\n\nI'm having a problem with this.", + state: 'opened', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: updated_at + } + + expect(issue.attributes).to eq(expected) + end + end + + context 'when issue is closed' do + let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + + it 'returns formatted attributes' do + expected = { + project: project, + title: 'Found a bug', + description: "*Created by: octocat*\n\nI'm having a problem with this.", + state: 'closed', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: closed_at + } + + expect(issue.attributes).to eq(expected) + end + end + + context 'when it is assigned to someone' do + let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + + it 'returns nil as assignee_id when is not a GitLab user' do + expect(issue.attributes.fetch(:assignee_id)).to be_nil + end + + it 'returns GitLab user id as assignee_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(issue.attributes.fetch(:assignee_id)).to eq gl_user.id + end + end + + context 'when author is a GitLab user' do + let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + + it 'returns project#creator_id as author_id when is not a GitLab user' do + expect(issue.attributes.fetch(:author_id)).to eq project.creator_id + end + + it 'returns GitLab user id as author_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(issue.attributes.fetch(:author_id)).to eq gl_user.id + end + end + end + + describe '#has_comments?' do + context 'when number of comments is greater than zero' do + let(:raw_data) { OpenStruct.new(base_data.merge(comments: 1)) } + + it 'returns true' do + expect(issue.has_comments?).to eq true + end + end + + context 'when number of comments is equal to zero' do + let(:raw_data) { OpenStruct.new(base_data.merge(comments: 0)) } + + it 'returns false' do + expect(issue.has_comments?).to eq false + end + end + end + + describe '#number' do + let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + + it 'returns pull request number' do + expect(issue.number).to eq 1347 + end + end + + describe '#valid?' do + context 'when mention a pull request' do + let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: OpenStruct.new)) } + + it 'returns false' do + expect(issue.valid?).to eq false + end + end + + context 'when does not mention a pull request' do + let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: nil)) } + + it 'returns true' do + expect(issue.valid?).to eq true + end + end + end +end diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb new file mode 100644 index 00000000000..b4465ef3743 --- /dev/null +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -0,0 +1,162 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::PullRequestFormatter, lib: true do + let(:project) { create(:project) } + let(:source_branch) { OpenStruct.new(ref: 'feature') } + let(:target_branch) { OpenStruct.new(ref: 'master') } + let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:base_data) do + { + number: 1347, + state: 'open', + title: 'New feature', + body: 'Please pull these awesome changes', + head: source_branch, + base: target_branch, + assignee: nil, + user: octocat, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + merged_at: nil + } + end + + subject(:pull_request) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when pull request is open' do + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'opened', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: updated_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when pull request is closed' do + let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'closed', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: closed_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when pull request is merged' do + let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } + let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } + + it 'returns formatted attributes' do + expected = { + title: 'New feature', + description: "*Created by: octocat*\n\nPlease pull these awesome changes", + source_project: project, + source_branch: 'feature', + target_project: project, + target_branch: 'master', + state: 'merged', + author_id: project.creator_id, + assignee_id: nil, + created_at: created_at, + updated_at: merged_at + } + + expect(pull_request.attributes).to eq(expected) + end + end + + context 'when it is assigned to someone' do + let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + + it 'returns nil as assignee_id when is not a GitLab user' do + expect(pull_request.attributes.fetch(:assignee_id)).to be_nil + end + + it 'returns GitLab user id as assignee_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id + end + end + + context 'when author is a GitLab user' do + let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + + it 'returns project#creator_id as author_id when is not a GitLab user' do + expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id + end + + it 'returns GitLab user id as author_id when is a GitLab user' do + gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') + + expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id + end + end + end + + describe '#number' do + let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + + it 'returns pull request number' do + expect(pull_request.number).to eq 1347 + end + end + + describe '#valid?' do + let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } + + context 'when source and target branches exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } + + it 'returns true' do + expect(pull_request.valid?).to eq true + end + end + + context 'when source branch doesn not exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } + + it 'returns false' do + expect(pull_request.valid?).to eq false + end + end + + context 'when target branch doesn not exists' do + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } + + it 'returns false' do + expect(pull_request.valid?).to eq false + end + end + end +end diff --git a/spec/lib/gitlab/github_import/pull_request_spec.rb b/spec/lib/gitlab/github_import/pull_request_spec.rb deleted file mode 100644 index 6ac32a78955..00000000000 --- a/spec/lib/gitlab/github_import/pull_request_spec.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'spec_helper' - -describe Gitlab::GithubImport::PullRequest, lib: true do - let(:project) { create(:project) } - let(:source_branch) { OpenStruct.new(ref: 'feature') } - let(:target_branch) { OpenStruct.new(ref: 'master') } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } - let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } - let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } - let(:base_data) do - { - state: 'open', - title: 'New feature', - body: 'Please pull these awesome changes', - head: source_branch, - base: target_branch, - assignee: nil, - user: octocat, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - merged_at: nil - } - end - - subject(:pull_request) { described_class.new(project, raw_data)} - - describe '#attributes' do - context 'when pull request is open' do - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } - - it 'returns formatted attributes' do - expected = { - title: 'New feature', - description: "*Created by: octocat*\n\nPlease pull these awesome changes", - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master', - state: 'opened', - author_id: project.creator_id, - assignee_id: nil, - created_at: created_at, - updated_at: updated_at - } - - expect(pull_request.attributes).to eq(expected) - end - end - - context 'when pull request is closed' do - let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } - - it 'returns formatted attributes' do - expected = { - title: 'New feature', - description: "*Created by: octocat*\n\nPlease pull these awesome changes", - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master', - state: 'closed', - author_id: project.creator_id, - assignee_id: nil, - created_at: created_at, - updated_at: closed_at - } - - expect(pull_request.attributes).to eq(expected) - end - end - - context 'when pull request is merged' do - let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } - - it 'returns formatted attributes' do - expected = { - title: 'New feature', - description: "*Created by: octocat*\n\nPlease pull these awesome changes", - source_project: project, - source_branch: 'feature', - target_project: project, - target_branch: 'master', - state: 'merged', - author_id: project.creator_id, - assignee_id: nil, - created_at: created_at, - updated_at: merged_at - } - - expect(pull_request.attributes).to eq(expected) - end - end - - context 'when it is assigned to someone' do - let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } - - it 'returns nil as assigned_id when is not a GitLab user' do - expect(pull_request.attributes.fetch(:assignee_id)).to be_nil - end - - it 'returns GitLab user id as assigned_id when is a GitLab user' do - gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') - - expect(pull_request.attributes.fetch(:assignee_id)).to eq gl_user.id - end - end - - context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } - - it 'returns project#creator_id as author_id when is not a GitLab user' do - expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id - end - - it 'returns GitLab user id as author_id when is a GitLab user' do - gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') - - expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id - end - end - end - - describe '#valid?' do - let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } - - context 'when source and target branches exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } - - it 'returns true' do - expect(pull_request.valid?).to eq true - end - end - - context 'when source branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } - - it 'returns false' do - expect(pull_request.valid?).to eq false - end - end - - context 'when target branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } - - it 'returns false' do - expect(pull_request.valid?).to eq false - end - end - end -end -- cgit v1.2.1 From 2dc74b48a32c64b18ca684e3adfb51ab9d87cf2b Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Wed, 23 Dec 2015 20:04:44 -0200 Subject: Update CHANGELOG --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index b0972ceab68..6c04f3967b0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -33,6 +33,7 @@ v 8.3.1 - Fix LDAP identity and user retrieval when special characters are used - Move Sidekiq-cron configuration to gitlab.yml - Enable forcing Two-Factor authentication sitewide, with optional grace period + - Import GitHub Pull Requests into GitLab v 8.3.0 - Bump rack-attack to 4.3.1 for security fix (Stan Hu) -- cgit v1.2.1 From 837a9065f0ff192d2efd55edcc2658a92c127b21 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 5 Jan 2016 15:15:36 -0200 Subject: Ensure that we're only importing local pull requests --- lib/gitlab/github_import/importer.rb | 2 +- lib/gitlab/github_import/pull_request_formatter.rb | 8 ++++++++ .../github_import/pull_request_formatter_spec.rb | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index 38ca7372202..2b0afbc7b39 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -42,7 +42,7 @@ module Gitlab direction: :asc).each do |raw_data| pull_request = PullRequestFormatter.new(project, raw_data) - if pull_request.valid? + if !pull_request.cross_project? && pull_request.valid? merge_request = MergeRequest.create!(pull_request.attributes) import_comments(pull_request.number, merge_request) import_comments_on_diff(pull_request.number, merge_request) diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index 42dc09c2ac5..b7c47958cc7 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -17,6 +17,10 @@ module Gitlab } end + def cross_project? + source_repo.fork == true + end + def number raw_data.number end @@ -57,6 +61,10 @@ module Gitlab project end + def source_repo + raw_data.head.repo + end + def source_branch source_project.repository.find_branch(raw_data.head.ref) end diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index b4465ef3743..9aefec77f6d 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -124,6 +124,28 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end end + describe '#cross_project?' do + context 'when source repo is not a fork' do + let(:local_repo) { OpenStruct.new(fork: false) } + let(:source_branch) { OpenStruct.new(ref: 'feature', repo: local_repo) } + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch)) } + + it 'returns false' do + expect(pull_request.cross_project?).to eq false + end + end + + context 'when source repo is a fork' do + let(:forked_repo) { OpenStruct.new(fork: true) } + let(:source_branch) { OpenStruct.new(ref: 'feature', repo: forked_repo) } + let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch)) } + + it 'returns true' do + expect(pull_request.cross_project?).to eq true + end + end + end + describe '#number' do let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } -- cgit v1.2.1 From e194b962d3cd638e72d3ea7144e20fe8a9093574 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Tue, 5 Jan 2016 15:23:48 -0200 Subject: Update doc for importing existing GitHub projects to GitLab --- doc/workflow/importing/import_projects_from_github.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md index 2d77c6d1172..2027a055c37 100644 --- a/doc/workflow/importing/import_projects_from_github.md +++ b/doc/workflow/importing/import_projects_from_github.md @@ -14,7 +14,7 @@ If you want to import from a GitHub Enterprise instance, you need to use GitLab ![Importer page](github_importer/importer.png) -* To import a project, you can simple click "Add". The importer will import your repository and issues. Once the importer is done, a new GitLab project will be created with your imported data. +* To import a project, you can simple click "Add". The importer will import your repository, issues, and pull requests. Once the importer is done, a new GitLab project will be created with your imported data. ### Note -When you import your projects from GitHub, it is not possible to keep your labels and milestones. We are working on improving this in the near future. +When you import your projects from GitHub, it is not possible to keep your labels, milestones, and cross-repository pull requests. We are working on improving this in the near future. -- cgit v1.2.1 From 8393e3e04b8b336ec80cb0da49d273f709043120 Mon Sep 17 00:00:00 2001 From: Michi302 Date: Tue, 5 Jan 2016 19:05:41 +0100 Subject: Add missing changelog entry --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 57f0b9f30d5..698f4e5f1cf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.4.0 (unreleased) - Add CAS support (tduehr) - Add link to merge request on build detail page. - Revert back upvote and downvote button to the issue and MR pages + - Change single user API endpoint to return more detailed data (Michael Potthoff) v 8.3.2 (unreleased) - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) -- cgit v1.2.1 From 045e8cc38c608b46924ce1ef4de3e8c2adefb1ba Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 29 Dec 2015 20:23:07 -0500 Subject: Update version check images to use SVG --- CHANGELOG | 1 + lib/version_check.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index b0972ceab68..ed45042eb13 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ v 8.4.0 (unreleased) - Fix version check image in Safari - Show 'All' tab by default in the builds page - Fix API project lookups when querying with a namespace with dots (Stan Hu) + - Update version check images to use SVG v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/lib/version_check.rb b/lib/version_check.rb index ea23344948c..91ad07feee5 100644 --- a/lib/version_check.rb +++ b/lib/version_check.rb @@ -13,6 +13,6 @@ class VersionCheck end def host - 'https://version.gitlab.com/check.png' + 'https://version.gitlab.com/check.svg' end end -- cgit v1.2.1 From db2d067eecc5d40e5f5b4e50a9d8ab505b207e54 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 5 Jan 2016 20:38:35 +0100 Subject: Fix project destroy callback See gitlab-org/gitlab-ee!107. --- app/models/ci/build.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 3e67b2771c1..d7fccb2197d 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -54,6 +54,8 @@ module Ci # To prevent db load megabytes of data from trace default_scope -> { select(Ci::Build.columns_without_lazy) } + before_destroy { project } + class << self def columns_without_lazy (column_names - LAZY_ATTRIBUTES).map do |column_name| @@ -145,10 +147,6 @@ module Ci end end - def project - commit.project - end - def project_id commit.project.id end -- cgit v1.2.1 From a298f694327b1241fc0d06618228e3750c20c5a1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jan 2016 14:50:52 -0500 Subject: Use `User#avatar_url` instead of `avatar_icon` helper --- app/helpers/page_layout_helper.rb | 10 +++---- spec/helpers/page_layout_helper_spec.rb | 51 +++++++++++---------------------- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index b84644d6996..f2a4afebbd1 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -49,12 +49,10 @@ module PageLayoutHelper def page_image default = image_url('gitlab_logo.png') - if @project - @project.avatar_url || default - elsif @user - avatar_icon(@user) - elsif @group - @group.avatar_url || default + subject = @project || @user || @group + + if subject.present? + subject.avatar_url || default else default end diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 300dccf50ec..83aeafcf31a 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -70,46 +70,27 @@ describe PageLayoutHelper do expect(helper.page_image).to end_with 'assets/gitlab_logo.png' end - context 'with @project' do - it 'uses Project avatar if available' do - project = double(avatar_url: 'http://example.com/uploads/avatar.png') - assign(:project, project) + %w(project user group).each do |type| + context "with @#{type} assigned" do + it "uses #{type.titlecase} avatar if available" do + object = double(avatar_url: 'http://example.com/uploads/avatar.png') + assign(type, object) - expect(helper.page_image).to eq project.avatar_url - end - - it 'falls back to the default' do - project = double(avatar_url: nil) - assign(:project, project) - - expect(helper.page_image).to end_with 'assets/gitlab_logo.png' - end - end - - context 'with @user' do - it 'delegates to avatar_icon helper' do - user = double('User') - assign(:user, user) + expect(helper.page_image).to eq object.avatar_url + end - expect(helper).to receive(:avatar_icon).with(user) + it 'falls back to the default when avatar_url is nil' do + object = double(avatar_url: nil) + assign(type, object) - helper.page_image + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end end - end - - context 'with @group' do - it 'uses Group avatar if available' do - group = double(avatar_url: 'http://example.com/uploads/avatar.png') - assign(:group, group) - - expect(helper.page_image).to eq group.avatar_url - end - - it 'falls back to the default' do - group = double(avatar_url: nil) - assign(:group, group) - expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + context "with no assignments" do + it 'falls back to the default' do + expect(helper.page_image).to end_with 'assets/gitlab_logo.png' + end end end end -- cgit v1.2.1 From 43053c2e6f03ad60f85728f36c46588979f68024 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jan 2016 14:54:59 -0500 Subject: Make `page_description` less magical :sparkles: --- app/helpers/page_layout_helper.rb | 12 +----------- app/views/layouts/group.html.haml | 7 ++++--- app/views/layouts/project.html.haml | 7 ++++--- spec/helpers/page_layout_helper_spec.rb | 27 ++------------------------- 4 files changed, 11 insertions(+), 42 deletions(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index f2a4afebbd1..5c0dd36252e 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -27,7 +27,7 @@ module PageLayoutHelper # # Returns an HTML-safe String. def page_description(description = nil) - @page_description ||= page_description_default + @page_description ||= brand_title if description.present? @page_description = description.squish @@ -36,16 +36,6 @@ module PageLayoutHelper end end - # Default value for page_description when one hasn't been defined manually by - # a view - def page_description_default - if @project - @project.description || brand_title - else - brand_title - end - end - def page_image default = image_url('gitlab_logo.png') diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 31888c5580e..1ce8d0ef7b5 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,5 +1,6 @@ -- page_title @group.name -- header_title group_title(@group) unless header_title -- sidebar "group" unless sidebar +- page_title @group.name +- page_description @group.description +- header_title group_title(@group) unless header_title +- sidebar "group" unless sidebar = render template: "layouts/application" diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index abf73bcc709..f81283a5ddb 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -1,6 +1,7 @@ -- page_title @project.name_with_namespace -- header_title project_title(@project) unless header_title -- sidebar "project" unless sidebar +- page_title @project.name_with_namespace +- page_description @project.description +- header_title project_title(@project) unless header_title +- sidebar "project" unless sidebar - content_for :scripts_body_top do - project = @target_project || @project diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 83aeafcf31a..a097786ba6d 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -2,8 +2,8 @@ require 'rails_helper' describe PageLayoutHelper do describe 'page_description' do - it 'defaults to value returned by page_description_default helper' do - allow(helper).to receive(:page_description_default).and_return('Foo') + it 'defaults to value returned by brand_title helper' do + allow(helper).to receive(:brand_title).and_return('Foo') expect(helper.page_description).to eq 'Foo' end @@ -42,29 +42,6 @@ describe PageLayoutHelper do end end - describe 'page_description_default' do - it 'uses Project description when available' do - project = double(description: 'Project Description') - assign(:project, project) - - expect(helper.page_description_default).to eq 'Project Description' - end - - it 'uses brand_title when Project description is nil' do - project = double(description: nil) - assign(:project, project) - - expect(helper).to receive(:brand_title).and_return('Brand Title') - expect(helper.page_description_default).to eq 'Brand Title' - end - - it 'falls back to brand_title' do - allow(helper).to receive(:brand_title).and_return('Brand Title') - - expect(helper.page_description_default).to eq 'Brand Title' - end - end - describe 'page_image' do it 'defaults to the GitLab logo' do expect(helper.page_image).to end_with 'assets/gitlab_logo.png' -- cgit v1.2.1 From a0793d69c538cbb6a2b9ff4389192862f6d16962 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jan 2016 15:02:42 -0500 Subject: Remove now-redundant `page_description` call from Projects#show --- app/views/projects/show.html.haml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index a8f924bbb7c..8436be433b1 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,5 +1,3 @@ -- page_description @project.description - = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity") @@ -70,4 +68,4 @@ = render 'projects/last_commit', commit: @repository.commit, project: @project %div{class: "project-show-#{default_project_view}"} - = render default_project_view \ No newline at end of file + = render default_project_view -- cgit v1.2.1 From 6d3b5ea2a9611dc7d87bd48043f34f9e0930e052 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jan 2016 16:47:09 -0500 Subject: Remove now-redundant `page_description` call from Groups#show --- app/views/groups/show.html.haml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index e7f619d2d6b..a607d860d7d 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,5 +1,3 @@ -- page_description @group.description - - unless can?(current_user, :read_group, @group) - @disable_search_panel = true -- cgit v1.2.1 From 1e6fc0c6a440ad707d990282ab7a93c178e35cfa Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Dec 2015 16:44:22 -0500 Subject: Define a limited set of filters for SingleLinePipeline Removes the following filters from its parent GfmPipeline: - SyntaxHighlightFilter - UploadLinkFilter - TableOfContentsFilter - LabelReferenceFilter - TaskListFilter Closes #1697 --- lib/banzai/pipeline/single_line_pipeline.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb index 6725c9039a9..a3c9d4f43aa 100644 --- a/lib/banzai/pipeline/single_line_pipeline.rb +++ b/lib/banzai/pipeline/single_line_pipeline.rb @@ -3,7 +3,23 @@ require 'banzai' module Banzai module Pipeline class SingleLinePipeline < GfmPipeline + def self.filters + @filters ||= [ + Filter::SanitizationFilter, + Filter::EmojiFilter, + Filter::AutolinkFilter, + Filter::ExternalLinkFilter, + + Filter::UserReferenceFilter, + Filter::IssueReferenceFilter, + Filter::ExternalIssueReferenceFilter, + Filter::MergeRequestReferenceFilter, + Filter::SnippetReferenceFilter, + Filter::CommitRangeReferenceFilter, + Filter::CommitReferenceFilter, + ] + end end end end -- cgit v1.2.1 From 52e41dcac4d0f76de77029aae07fce60b61d86ef Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 5 Jan 2016 18:02:12 -0500 Subject: Fix the abuse report detail URL in the HTML email template --- app/views/abuse_report_mailer/notify.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml index 619533e09a7..2741eb44357 100644 --- a/app/views/abuse_report_mailer/notify.html.haml +++ b/app/views/abuse_report_mailer/notify.html.haml @@ -8,4 +8,4 @@ = @abuse_report.message %p - = link_to "View details", abuse_reports_url + = link_to "View details", admin_abuse_reports_url -- cgit v1.2.1 From cbdc8dd46ff2b74e5817053f774cc8f17db55f77 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Tue, 5 Jan 2016 20:42:25 -0500 Subject: adds ajax open and close merges, with discussion/comments working as well --- app/assets/javascripts/merge_request.js.coffee | 43 ++++++++++++++++++++++ app/helpers/merge_requests_helper.rb | 4 ++ app/models/merge_request.rb | 10 ----- .../projects/merge_requests/_discussion.html.haml | 9 ++--- .../merge_requests/show/_mr_title.html.haml | 26 ++++++++----- 5 files changed, 67 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 9047587db81..8c321319b30 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -22,6 +22,7 @@ class @MergeRequest if $("a.btn-close").length @initTaskList() + @initMergeRequestBtnEventListeners() # Local jQuery finder $: (selector) -> @@ -35,6 +36,48 @@ class @MergeRequest # Show the first tab (Commits) $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show') + initMergeRequestBtnEventListeners: -> + _this = @ + mergeRequestFailMessage = 'Unable to update this merge request at this time.' + $('a.btn-close, a.btn-reopen').on 'click', (e) -> + e.preventDefault() + e.stopImmediatePropagation() + $this = $(this) + isClose = $this.hasClass('btn-close') + shouldSubmit = $this.hasClass('btn-comment') + if shouldSubmit + _this.submitNoteForm($this.closest('form')) + $this.prop('disabled', true) + url = $this.attr('href') + $.ajax + type: 'PUT', + url: url, + error: (jqXHR, textStatus, errorThrown) -> + mergeRequestStatus = if isClose then 'close' else 'open' + new Flash(mergeRequestFailMessage, 'alert') + success: (data, textStatus, jqXHR) -> + if data.saved + if isClose + $('a.btn-close').addClass('hidden') + $('a.issuable-edit').addClass('hidden') + $('a.btn-reopen').removeClass('hidden') + $('div.status-box-closed').removeClass('hidden') + $('div.status-box-open').addClass('hidden') + else + $('a.btn-reopen').addClass('hidden') + $('a.issuable-edit').removeClass('hidden') + $('a.btn-close').removeClass('hidden') + $('div.status-box-closed').addClass('hidden') + $('div.status-box-open').removeClass('hidden') + else + new Flash(mergeRequestFailMessage, 'alert') + $this.prop('disabled', false) + + submitNoteForm: (form) => + noteText = form.find("textarea.js-note-text").val() + if noteText.trim().length > 0 + form.submit() + showAllCommits: -> this.$('.first-commits').remove() this.$('.all-commits').removeClass 'hide' diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 1dd07a2a220..fafe2acd538 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -19,6 +19,10 @@ module MergeRequestsHelper } end + def merge_request_button_visibility(mr, closed) + return 'hidden' if mr.closed? == closed + end + def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ac25d38eb63..f013018ec08 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -456,16 +456,6 @@ class MergeRequest < ActiveRecord::Base ::Gitlab::GitAccess.new(user, project).can_push_to_branch?(target_branch) end - def state_human_name - if merged? - "Merged" - elsif closed? - "Closed" - else - "Open" - end - end - def target_sha @target_sha ||= target_project. repository.commit(target_branch).sha diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index bff3c3b283d..ed3628a36a3 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -1,8 +1,7 @@ - content_for :note_actions do - if can?(current_user, :update_merge_request, @merge_request) - - if @merge_request.open? - = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" - - if @merge_request.closed? - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" + = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close}, format: 'json'), data: {no_turbolinks: true}, method: :put, class: "btn btn-nr btn-grouped btn-close close-mr-link btn-comment js-note-target-close #{merge_request_button_visibility(@merge_request, true)}", title: "Close merge request" + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen}, format: 'json'), data: {no_turbolinks: true}, method: :put, class: "btn btn-nr btn-grouped btn-reopen reopen-mr-link btn-comment js-note-target-reopen #{merge_request_button_visibility(@merge_request, false)}", title: "Reopen merge request" -#notes= render "projects/notes/notes_with_form" +#notes + = render "projects/notes/notes_with_form" diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index fc6fb2a0d42..7996d35c462 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,6 +1,12 @@ .detail-page-header - .status-box{ class: status_box_class(@merge_request) } - = @merge_request.state_human_name + - if @merge_request.merged? + .status-box{ class: "status-box-merged" } + Merged + - else + .status-box{ class: "status-box-closed #{merge_request_button_visibility(@merge_request, false)}"} + Closed + .status-box{ class: "status-box-open #{merge_request_button_visibility(@merge_request, true)}"} + Open %span.identifier Merge Request ##{@merge_request.iid} %span.creator @@ -15,11 +21,11 @@ = time_ago_with_tooltip(@merge_request.updated_at, placement: 'bottom') .issue-btn-group.pull-right - - if can?(current_user, :update_merge_request, @merge_request) - - if @merge_request.open? - = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request' - = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do - %i.fa.fa-pencil-square-o - Edit - - if @merge_request.closed? - = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' + - if can?(current_user, :update_merge_request, @merge_request) && !@merge_request.merged? + + = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }, format: 'json'), data: {no_turbolink: true}, method: :put, class: "btn btn-nr btn-grouped btn-reopen reopen-mr-link #{merge_request_button_visibility(@merge_request, false)}", title: 'Reopen merge request' + = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }, format: 'json'), data: {no_turbolink: true}, method: :put, class: "btn btn-nr btn-grouped btn-close #{merge_request_button_visibility(@merge_request, true)}", title: 'Close merge request' + + = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn btn-nr btn-grouped issuable-edit #{merge_request_button_visibility(@merge_request, true)}", id: 'edit_merge_request' do + %i.fa.fa-pencil-square-o + Edit \ No newline at end of file -- cgit v1.2.1 From 987989b632d4f610f0ec17b65f1c7d24530c99ff Mon Sep 17 00:00:00 2001 From: Kelvin Date: Wed, 6 Jan 2016 14:41:26 +0300 Subject: Remove block button on abuse reports if user is already blocked --- app/views/admin/abuse_reports/_abuse_report.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml index cf50a376e11..8aa34c0ffac 100644 --- a/app/views/admin/abuse_reports/_abuse_report.html.haml +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -23,6 +23,6 @@ data: { confirm: "USER #{user.name} WILL BE REMOVED! Are you sure?" }, remote: true, method: :delete, class: "btn btn-xs btn-remove js-remove-tr" %td - - if user + - if user && !user.blocked? = link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs" = link_to 'Remove report', [:admin, abuse_report], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr" -- cgit v1.2.1 From e16d9edf9995735da7a900c35bc7be73cc481473 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Wed, 6 Jan 2016 14:48:52 +0300 Subject: Show that user is already blocked on Abuse Reports --- app/views/admin/abuse_reports/_abuse_report.html.haml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml index 8aa34c0ffac..853a780c576 100644 --- a/app/views/admin/abuse_reports/_abuse_report.html.haml +++ b/app/views/admin/abuse_reports/_abuse_report.html.haml @@ -25,4 +25,7 @@ %td - if user && !user.blocked? = link_to 'Block user', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs" + - else + .btn.btn-xs + Already Blocked = link_to 'Remove report', [:admin, abuse_report], remote: true, method: :delete, class: "btn btn-xs btn-close js-remove-tr" -- cgit v1.2.1 From 384445eca6249363c0da6d8b96e7ee030dc6fab3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 6 Jan 2016 13:02:51 +0100 Subject: Don't override issue page description in project layout. --- app/helpers/page_layout_helper.rb | 11 +++-------- app/views/layouts/_head.html.haml | 2 ++ app/views/layouts/group.html.haml | 2 +- app/views/layouts/project.html.haml | 2 +- spec/helpers/page_layout_helper_spec.rb | 6 ++---- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 5c0dd36252e..82f805fa444 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -27,11 +27,9 @@ module PageLayoutHelper # # Returns an HTML-safe String. def page_description(description = nil) - @page_description ||= brand_title - if description.present? @page_description = description.squish - else + elsif @page_description.present? sanitize(@page_description, tags: []).truncate_words(30) end end @@ -41,11 +39,8 @@ module PageLayoutHelper subject = @project || @user || @group - if subject.present? - subject.avatar_url || default - else - default - end + image = subject.avatar_url if subject.present? + image || default end # Define or get attributes to be used as Twitter card metadata diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 1a2187e551b..38ca4f91c4d 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -1,3 +1,5 @@ +- page_description brand_title unless page_description + - site_name = "GitLab" %head{prefix: "og: http://ogp.me/ns#"} %meta{charset: "utf-8"} diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index 1ce8d0ef7b5..2e483b7148d 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,5 +1,5 @@ - page_title @group.name -- page_description @group.description +- page_description @group.description unless page_description - header_title group_title(@group) unless header_title - sidebar "group" unless sidebar diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index f81283a5ddb..ab527e8e438 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -1,5 +1,5 @@ - page_title @project.name_with_namespace -- page_description @project.description +- page_description @project.description unless page_description - header_title project_title(@project) unless header_title - sidebar "project" unless sidebar diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index a097786ba6d..cf632f594c7 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -2,10 +2,8 @@ require 'rails_helper' describe PageLayoutHelper do describe 'page_description' do - it 'defaults to value returned by brand_title helper' do - allow(helper).to receive(:brand_title).and_return('Foo') - - expect(helper.page_description).to eq 'Foo' + it 'defaults to nil' do + expect(helper.page_description).to eq nil end it 'returns the last-pushed description' do -- cgit v1.2.1 From b9ed3961b55cf3dbc1a6d4c841d295dd23161c90 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 6 Jan 2016 13:25:13 +0100 Subject: Revert "Add DEBUG_BANZAI_CACHE env var to debug Banzai cache issue." This reverts commit 4b027bc93a7875c3937f6b90ac1049b4a4d72da5. --- lib/banzai/renderer.rb | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb index 910e1c6994e..115ae914524 100644 --- a/lib/banzai/renderer.rb +++ b/lib/banzai/renderer.rb @@ -1,5 +1,7 @@ module Banzai module Renderer + CACHE_ENABLED = false + # Convert a Markdown String into an HTML-safe String of HTML # # Note that while the returned HTML will have been sanitized of dangerous @@ -18,22 +20,13 @@ module Banzai cache_key = context.delete(:cache_key) cache_key = full_cache_key(cache_key, context[:pipeline]) - cacheless = cacheless_render(text, context) - - if cache_key && ENV["DEBUG_BANZAI_CACHE"] - cached = Rails.cache.fetch(cache_key) { cacheless } - - if cached != cacheless - Rails.logger.warn "Banzai cache mismatch" - Rails.logger.warn "Text: #{text.inspect}" - Rails.logger.warn "Context: #{context.inspect}" - Rails.logger.warn "Cache key: #{cache_key.inspect}" - Rails.logger.warn "Cacheless: #{cacheless.inspect}" - Rails.logger.warn "With cache: #{cached.inspect}" + if cache_key && CACHE_ENABLED + Rails.cache.fetch(cache_key) do + cacheless_render(text, context) end + else + cacheless_render(text, context) end - - cacheless end def self.render_result(text, context = {}) -- cgit v1.2.1 From cf19efec3ac0ab4510359dd71df3d511762230c3 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 6 Jan 2016 13:26:02 +0100 Subject: Revert "Temporarily disable Markdown caching" This reverts commit d337d5e7137d9b3fd0f9b8890a3ba9296323acc7. --- lib/banzai/renderer.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb index 115ae914524..891c0fd7749 100644 --- a/lib/banzai/renderer.rb +++ b/lib/banzai/renderer.rb @@ -1,7 +1,5 @@ module Banzai module Renderer - CACHE_ENABLED = false - # Convert a Markdown String into an HTML-safe String of HTML # # Note that while the returned HTML will have been sanitized of dangerous @@ -20,7 +18,7 @@ module Banzai cache_key = context.delete(:cache_key) cache_key = full_cache_key(cache_key, context[:pipeline]) - if cache_key && CACHE_ENABLED + if cache_key Rails.cache.fetch(cache_key) do cacheless_render(text, context) end -- cgit v1.2.1 From 37ce5f312eabf95deff7aac68f6bce6ba6e106b9 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 6 Jan 2016 13:33:11 +0100 Subject: Fix mentionable reference extraction caching. --- app/models/concerns/mentionable.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index 6316ee208b5..98f71ae8cb0 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -51,8 +51,11 @@ module Mentionable else self.class.mentionable_attrs.each do |attr, options| text = send(attr) - options[:cache_key] = [self, attr] if options.delete(:cache) && self.persisted? - ext.analyze(text, options) + + context = options.dup + context[:cache_key] = [self, attr] if context.delete(:cache) && self.persisted? + + ext.analyze(text, context) end end -- cgit v1.2.1 From 18b17072c6cc7eb199d1da34a3ea481dcd53a8cf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Wed, 6 Jan 2016 13:33:47 +0100 Subject: Add regression test. --- spec/models/note_spec.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index 593d8f76215..151a29e974b 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -125,6 +125,19 @@ describe Note, models: true do let(:set_mentionable_text) { ->(txt) { subject.note = txt } } end + describe "#all_references" do + let!(:note1) { create(:note) } + let!(:note2) { create(:note) } + + it "reads the rendered note body from the cache" do + expect(Banzai::Renderer).to receive(:render).with(note1.note, pipeline: :note, cache_key: [note1, "note"], project: note1.project) + expect(Banzai::Renderer).to receive(:render).with(note2.note, pipeline: :note, cache_key: [note2, "note"], project: note2.project) + + note1.all_references + note2.all_references + end + end + describe :search do let!(:note) { create(:note, note: "WoW") } @@ -164,7 +177,7 @@ describe Note, models: true do expect(note.editable?).to be_falsy end end - + describe "set_award!" do let(:issue) { create :issue } -- cgit v1.2.1 From f71642017ebfd409e20735b621dd3a9fe09add12 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 6 Jan 2016 07:33:50 -0500 Subject: adds tests (and passes them) for ajax open and close merge requests. --- .../fixtures/merge_requests_show.html.haml | 12 ++- spec/javascripts/issue_spec.js.coffee | 2 +- spec/javascripts/merge_request_spec.js.coffee | 88 ++++++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/fixtures/merge_requests_show.html.haml b/spec/javascripts/fixtures/merge_requests_show.html.haml index 8447dfdda32..fdfa8a273e2 100644 --- a/spec/javascripts/fixtures/merge_requests_show.html.haml +++ b/spec/javascripts/fixtures/merge_requests_show.html.haml @@ -1,4 +1,14 @@ -%a.btn-close +:css + .hidden { display: none !important } + +.flash-container + .flash-alert + .flash-notice + +.status-box.status-box-open Open +.status-box.status-box-closed.hidden Closed +%a.btn-close{"href" => "http://gitlab.com/merge_requests/6/close"} Close +%a.btn-reopen.hidden{"href" => "http://gitlab.com/merge_requests/6/reopen"} Reopen .detail-page-description .description.js-task-list-container diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index 7e67c778861..e1c22860da7 100644 --- a/spec/javascripts/issue_spec.js.coffee +++ b/spec/javascripts/issue_spec.js.coffee @@ -44,7 +44,7 @@ describe 'reopen/close issue', -> expect($('div.status-box-closed')).toBeVisible() expect($('div.status-box-open')).toBeHidden() - it 'fails to closes an issue with success:false', -> + it 'fails to close an issue with success:false', -> $.ajax = (obj) -> expect(obj.type).toBe('PUT') diff --git a/spec/javascripts/merge_request_spec.js.coffee b/spec/javascripts/merge_request_spec.js.coffee index 22ebc7039d1..e21bfde38ad 100644 --- a/spec/javascripts/merge_request_spec.js.coffee +++ b/spec/javascripts/merge_request_spec.js.coffee @@ -21,3 +21,91 @@ describe 'MergeRequest', -> expect(req.data.merge_request.description).not.toBe(null) $('.js-task-list-field').trigger('tasklist:changed') + + describe 'reopen/close merge request', -> + fixture.preload('merge_requests_show.html') + beforeEach -> + fixture.load('merge_requests_show.html') + @merge_request = new MergeRequest({}) + it 'closes a merge request', -> + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://gitlab.com/merge_requests/6/close') + obj.success saved:true + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeVisible() + + expect($btnClose).toBeHidden() + expect($('div.status-box-closed')).toBeVisible() + expect($('div.status-box-open')).toBeHidden() + + it 'fails to close a merge request with success:false', -> + + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://goesnowhere.nothing/whereami') + obj.success saved:false + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + $btnClose.attr('href','http://goesnowhere.nothing/whereami') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-closed')).toBeHidden() + expect($('div.status-box-open')).toBeVisible() + expect($('div.flash-alert')).toBeVisible() + expect($('div.flash-alert').text()).toBe('Unable to update this merge request at this time.') + + it 'fails to closes an issue with HTTP error', -> + + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://goesnowhere.nothing/whereami') + obj.error() + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + $btnClose.attr('href','http://goesnowhere.nothing/whereami') + expect($btnReopen).toBeHidden() + expect($btnClose.text()).toBe('Close') + expect(typeof $btnClose.prop('disabled')).toBe('undefined') + + $btnClose.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-closed')).toBeHidden() + expect($('div.status-box-open')).toBeVisible() + expect($('div.flash-alert')).toBeVisible() + expect($('div.flash-alert').text()).toBe('Unable to update this merge request at this time.') + + it 'reopens a merge request', -> + $.ajax = (obj) -> + expect(obj.type).toBe('PUT') + expect(obj.url).toBe('http://gitlab.com/merge_requests/6/reopen') + obj.success saved: true + + $btnClose = $('a.btn-close') + $btnReopen = $('a.btn-reopen') + expect($btnReopen.text()).toBe('Reopen') + + $btnReopen.trigger('click') + + expect($btnReopen).toBeHidden() + expect($btnClose).toBeVisible() + expect($('div.status-box-open')).toBeVisible() + expect($('div.status-box-closed')).toBeHidden() \ No newline at end of file -- cgit v1.2.1 From 79c0e7212af0a6b0243bc0512a75eb936fb8d862 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jan 2016 02:30:59 +0000 Subject: Annotate models --- app/models/application_setting.rb | 15 ++- app/models/ci/build.rb | 1 + app/models/ci/runner_project.rb | 11 +- app/models/ci/trigger.rb | 13 +-- app/models/ci/variable.rb | 3 +- app/models/commit_status.rb | 55 +++++----- app/models/generic_commit_status.rb | 1 + app/models/group.rb | 1 - app/models/hooks/project_hook.rb | 1 + app/models/hooks/service_hook.rb | 1 + app/models/hooks/system_hook.rb | 1 + app/models/hooks/web_hook.rb | 1 + app/models/merge_request.rb | 44 ++++---- app/models/namespace.rb | 1 - app/models/project.rb | 7 ++ app/models/project_services/asana_service.rb | 2 + app/models/project_services/assembla_service.rb | 1 + app/models/project_services/bamboo_service.rb | 1 + app/models/project_services/buildkite_service.rb | 1 + .../project_services/builds_email_service.rb | 1 + app/models/project_services/campfire_service.rb | 1 + app/models/project_services/ci_service.rb | 1 + .../custom_issue_tracker_service.rb | 1 + app/models/project_services/drone_ci_service.rb | 1 + .../project_services/emails_on_push_service.rb | 1 + .../project_services/external_wiki_service.rb | 1 + app/models/project_services/flowdock_service.rb | 1 + app/models/project_services/gemnasium_service.rb | 1 + app/models/project_services/gitlab_ci_service.rb | 1 + .../gitlab_issue_tracker_service.rb | 1 + app/models/project_services/hipchat_service.rb | 1 + app/models/project_services/irker_service.rb | 1 + .../project_services/issue_tracker_service.rb | 1 + app/models/project_services/jira_service.rb | 1 + .../project_services/pivotaltracker_service.rb | 1 + app/models/project_services/pushover_service.rb | 1 + app/models/project_services/redmine_service.rb | 1 + app/models/project_services/slack_service.rb | 1 + app/models/project_services/teamcity_service.rb | 1 + app/models/service.rb | 1 + app/models/user.rb | 113 +++++++++++---------- spec/factories/merge_requests.rb | 41 ++++---- spec/factories/projects.rb | 7 ++ spec/models/application_setting_spec.rb | 65 +++++++----- spec/models/ci/commit_spec.rb | 2 +- spec/models/ci/runner_project_spec.rb | 11 +- spec/models/ci/trigger_spec.rb | 13 +-- spec/models/ci/variable_spec.rb | 3 +- spec/models/commit_status_spec.rb | 1 + spec/models/external_wiki_service_spec.rb | 1 + spec/models/generic_commit_status_spec.rb | 1 + spec/models/group_spec.rb | 1 - spec/models/merge_request_spec.rb | 41 ++++---- spec/models/namespace_spec.rb | 1 - spec/models/project_spec.rb | 7 ++ spec/models/service_spec.rb | 1 + spec/models/user_spec.rb | 113 +++++++++++---------- 57 files changed, 350 insertions(+), 254 deletions(-) diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index be69d317d73..6c6c2468374 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -27,9 +27,20 @@ # admin_notification_email :string(255) # shared_runners_enabled :boolean default(TRUE), not null # max_artifacts_size :integer default(100), not null -# runners_registration_token :string(255) -# require_two_factor_authentication :boolean default(TRUE) +# runners_registration_token :string +# require_two_factor_authentication :boolean default(FALSE) # two_factor_grace_period :integer default(48) +# metrics_enabled :boolean default(FALSE) +# metrics_host :string default("localhost") +# metrics_username :string +# metrics_password :string +# metrics_pool_size :integer default(16) +# metrics_timeout :integer default(10) +# metrics_method_call_threshold :integer default(10) +# recaptcha_enabled :boolean default(FALSE) +# recaptcha_site_key :string +# recaptcha_private_key :string +# metrics_port :integer default(8089) # class ApplicationSetting < ActiveRecord::Base diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d7fccb2197d..30f79fd3bfa 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -29,6 +29,7 @@ # target_url :string(255) # description :string(255) # artifacts_file :text +# gl_project_id :integer # module Ci diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 93d9be144e8..7b16f207a26 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -2,11 +2,12 @@ # # Table name: ci_runner_projects # -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer +# created_at :datetime +# updated_at :datetime +# gl_project_id :integer # module Ci diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 23516709a41..bb98cd5c7da 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -2,12 +2,13 @@ # # 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 +# id :integer not null, primary key +# token :string(255) +# project_id :integer +# deleted_at :datetime +# created_at :datetime +# updated_at :datetime +# gl_project_id :integer # module Ci diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 56759d3e50f..7f6f497f325 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -3,12 +3,13 @@ # Table name: ci_variables # # id :integer not null, primary key -# project_id :integer not null +# project_id :integer # key :string(255) # value :text # encrypted_value :text # encrypted_value_salt :string(255) # encrypted_value_iv :string(255) +# gl_project_id :integer # module Ci diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 21c5c87bc3d..ff479493474 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,30 +1,35 @@ # == Schema Information # -# project_id integer -# status string -# finished_at datetime -# trace text -# created_at datetime -# updated_at datetime -# started_at datetime -# runner_id integer -# coverage float -# commit_id integer -# commands text -# job_id integer -# name string -# deploy boolean default: false -# options text -# allow_failure boolean default: false, null: false -# stage string -# trigger_request_id integer -# stage_idx integer -# tag boolean -# ref string -# user_id integer -# type string -# target_url string -# description string +# 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 +# gl_project_id :integer # class CommitStatus < ActiveRecord::Base diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb index 12c934e2494..97f4f03a9a5 100644 --- a/app/models/generic_commit_status.rb +++ b/app/models/generic_commit_status.rb @@ -29,6 +29,7 @@ # target_url :string(255) # description :string(255) # artifacts_file :text +# gl_project_id :integer # class GenericCommitStatus < CommitStatus diff --git a/app/models/group.rb b/app/models/group.rb index b8f2ab6ae5d..5a31b46920c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,7 +11,6 @@ # 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 22638057773..fa18ba5dbbe 100644 --- a/app/models/hooks/project_hook.rb +++ b/app/models/hooks/project_hook.rb @@ -15,6 +15,7 @@ # tag_push_events :boolean default(FALSE) # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) +# build_events :boolean default(FALSE), not null # class ProjectHook < WebHook diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb index 09bb3ee52a2..b333a337347 100644 --- a/app/models/hooks/service_hook.rb +++ b/app/models/hooks/service_hook.rb @@ -15,6 +15,7 @@ # tag_push_events :boolean default(FALSE) # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) +# build_events :boolean default(FALSE), not null # class ServiceHook < WebHook diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb index 2f63c59b07e..d81512fae5d 100644 --- a/app/models/hooks/system_hook.rb +++ b/app/models/hooks/system_hook.rb @@ -15,6 +15,7 @@ # tag_push_events :boolean default(FALSE) # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) +# build_events :boolean default(FALSE), not null # class SystemHook < WebHook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 40eb0e20b4b..7164c0e1e90 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -15,6 +15,7 @@ # tag_push_events :boolean default(FALSE) # note_events :boolean default(FALSE), not null # enable_ssl_verification :boolean default(TRUE) +# build_events :boolean default(FALSE), not null # class WebHook < ActiveRecord::Base diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ac25d38eb63..1b98474fc55 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2,28 +2,28 @@ # # Table name: merge_requests # -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) -# merge_params :text (serialized to hash) -# merge_when_build_succeeds :boolean default(false), not null -# merge_user_id :integer +# id :integer not null, primary key +# target_branch :string(255) not null +# source_branch :string(255) not null +# source_project_id :integer not null +# author_id :integer +# assignee_id :integer +# title :string(255) +# created_at :datetime +# updated_at :datetime +# milestone_id :integer +# state :string(255) +# merge_status :string(255) +# target_project_id :integer not null +# iid :integer +# description :text +# position :integer default(0) +# locked_at :datetime +# updated_by_id :integer +# merge_error :string(255) +# merge_params :text +# merge_when_build_succeeds :boolean default(FALSE), not null +# merge_user_id :integer # require Rails.root.join("app/models/commit") diff --git a/app/models/namespace.rb b/app/models/namespace.rb index adafabbec07..bdb33f37495 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -11,7 +11,6 @@ # 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.rb b/app/models/project.rb index b1a6cfa86af..7626c698816 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -29,6 +29,13 @@ # import_source :string(255) # commit_count :integer default(0) # import_error :text +# ci_id :integer +# builds_enabled :boolean default(TRUE), not null +# shared_runners_enabled :boolean default(TRUE), not null +# runners_token :string +# build_coverage_regex :string +# build_allow_git_fetch :boolean default(TRUE), not null +# build_timeout :integer default(3600), not null # require 'carrierwave/orm/activerecord' diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index 7d367e40037..792ad804575 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -16,7 +16,9 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # + require 'asana' class AsanaService < Service diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index fb7e0c0fb0d..29d841faed8 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class AssemblaService < Service diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index aa8746beb80..9e7f642180e 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class BambooService < CiService diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index 199ee3a9d0d..3efbfd2eec3 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require "addressable/uri" diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 8247c79fc33..92c9b13c9b9 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class BuildsEmailService < Service diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index e591afdda64..6e8f0842524 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class CampfireService < Service diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index 88186113c68..c3f70d1f972 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # # Base class for CI services diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 7c2027c18e6..88a3e9218cb 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class CustomIssueTrackerService < IssueTrackerService diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 08e5ccb3855..b4724bb647e 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class DroneCiService < CiService diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index 8f5d8b086eb..b831577cd97 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class EmailsOnPushService < Service diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index 74c57949b4d..b402b68665a 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class ExternalWikiService < Service diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 15c7c907f7e..8605ce66e48 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require "flowdock-git-hook" diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index 202fee042e3..61babe9cfe5 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require "gemnasium/gitlab_service" diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index b64d97ce75d..33f0d7ea01a 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # # TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index 9558292fea3..7aa04309f54 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class GitlabIssueTrackerService < IssueTrackerService diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 1e1686a11c6..32a81808930 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class HipchatService < Service diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index d24aa317cf3..bd9b580038f 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require 'uri' diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 936e574cccd..ed201979d39 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class IssueTrackerService < Service diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index e216f406e1c..a1b77c61576 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class JiraService < IssueTrackerService diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index ade9ee97873..c9a890c7e3f 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class PivotaltrackerService < Service diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index 53edf522e9a..3d7e8bbee61 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class PushoverService < Service diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index dd9ba97ee1f..de974354c77 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class RedmineService < IssueTrackerService diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 375b4534d07..d89cf6d17b2 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class SlackService < Service diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index a63700693d7..b8e9416131a 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # class TeamcityService < CiService diff --git a/app/models/service.rb b/app/models/service.rb index d3bf7f0ebd1..24f4bf7646e 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # # To add new service you should build a class inherited from Service diff --git a/app/models/user.rb b/app/models/user.rb index 20f907e4347..46b36c605b0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,62 +2,63 @@ # # Table name: users # -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# unlock_token :string(255) -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# last_credential_check_at :datetime -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# notification_email :string(255) -# hide_no_password :boolean default(FALSE) -# password_automatically_set :boolean default(FALSE) -# location :string(255) -# encrypted_otp_secret :string(255) -# encrypted_otp_secret_iv :string(255) -# encrypted_otp_secret_salt :string(255) -# otp_required_for_login :boolean default(FALSE), not null -# otp_backup_codes :text -# public_email :string(255) default(""), not null -# dashboard :integer default(0) -# project_view :integer default(0) -# consumed_timestep :integer -# layout :integer default(0) -# hide_project_limit :boolean default(FALSE) +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer default(10) +# skype :string(255) default(""), not null +# linkedin :string(255) default(""), not null +# twitter :string(255) default(""), not null +# authentication_token :string(255) +# theme_id :integer default(1), not null +# bio :string(255) +# failed_attempts :integer default(0) +# locked_at :datetime +# username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null +# state :string(255) +# color_scheme_id :integer default(1), not null +# notification_level :integer default(1), not null +# password_expires_at :datetime +# created_by_id :integer +# last_credential_check_at :datetime +# avatar :string(255) +# confirmation_token :string(255) +# confirmed_at :datetime +# confirmation_sent_at :datetime +# unconfirmed_email :string(255) +# hide_no_ssh_key :boolean default(FALSE) +# website_url :string(255) default(""), not null +# notification_email :string(255) +# hide_no_password :boolean default(FALSE) +# password_automatically_set :boolean default(FALSE) +# location :string(255) +# encrypted_otp_secret :string(255) +# encrypted_otp_secret_iv :string(255) +# encrypted_otp_secret_salt :string(255) +# otp_required_for_login :boolean default(FALSE), not null +# otp_backup_codes :text +# public_email :string(255) default(""), not null +# dashboard :integer default(0) +# project_view :integer default(0) +# consumed_timestep :integer +# layout :integer default(0) +# hide_project_limit :boolean default(FALSE) +# unlock_token :string +# otp_grace_period_started_at :datetime # require 'carrierwave/orm/activerecord' diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 5b4d7f41bc4..0c6a881f868 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -2,25 +2,28 @@ # # Table name: merge_requests # -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) +# id :integer not null, primary key +# target_branch :string(255) not null +# source_branch :string(255) not null +# source_project_id :integer not null +# author_id :integer +# assignee_id :integer +# title :string(255) +# created_at :datetime +# updated_at :datetime +# milestone_id :integer +# state :string(255) +# merge_status :string(255) +# target_project_id :integer not null +# iid :integer +# description :text +# position :integer default(0) +# locked_at :datetime +# updated_by_id :integer +# merge_error :string(255) +# merge_params :text +# merge_when_build_succeeds :boolean default(FALSE), not null +# merge_user_id :integer # FactoryGirl.define do diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 112213377ff..c14b99606ba 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -29,6 +29,13 @@ # import_source :string(255) # commit_count :integer default(0) # import_error :text +# ci_id :integer +# builds_enabled :boolean default(TRUE), not null +# shared_runners_enabled :boolean default(TRUE), not null +# runners_token :string +# build_coverage_regex :string +# build_allow_git_fetch :boolean default(TRUE), not null +# build_timeout :integer default(3600), not null # FactoryGirl.define do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 35d8220ae54..91b250265e6 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -2,32 +2,45 @@ # # Table name: application_settings # -# id :integer not null, primary key -# default_projects_limit :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string(255) -# default_branch_protection :integer default(2) -# twitter_sharing_enabled :boolean default(TRUE) -# restricted_visibility_levels :text -# version_check_enabled :boolean default(TRUE) -# max_attachment_size :integer default(10), not null -# default_project_visibility :integer -# default_snippet_visibility :integer -# restricted_signup_domains :text -# user_oauth_applications :boolean default(TRUE) -# 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 -# runners_registration_token :string(255) +# id :integer not null, primary key +# default_projects_limit :integer +# signup_enabled :boolean +# signin_enabled :boolean +# gravatar_enabled :boolean +# sign_in_text :text +# created_at :datetime +# updated_at :datetime +# home_page_url :string(255) +# default_branch_protection :integer default(2) +# twitter_sharing_enabled :boolean default(TRUE) +# restricted_visibility_levels :text +# version_check_enabled :boolean default(TRUE) +# max_attachment_size :integer default(10), not null +# default_project_visibility :integer +# default_snippet_visibility :integer +# restricted_signup_domains :text +# user_oauth_applications :boolean default(TRUE) +# 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 +# runners_registration_token :string +# require_two_factor_authentication :boolean default(FALSE) +# two_factor_grace_period :integer default(48) +# metrics_enabled :boolean default(FALSE) +# metrics_host :string default("localhost") +# metrics_username :string +# metrics_password :string +# metrics_pool_size :integer default(16) +# metrics_timeout :integer default(10) +# metrics_method_call_threshold :integer default(10) +# recaptcha_enabled :boolean default(FALSE) +# recaptcha_site_key :string +# recaptcha_private_key :string +# metrics_port :integer default(8089) # require 'spec_helper' diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index b193e16e7f8..dfc0cc3be1c 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -13,7 +13,7 @@ # tag :boolean default(FALSE) # yaml_errors :text # committed_at :datetime -# project_id :integer +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb index da8491357a5..000a732db77 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -2,11 +2,12 @@ # # Table name: ci_runner_projects # -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime +# id :integer not null, primary key +# runner_id :integer not null +# project_id :integer +# created_at :datetime +# updated_at :datetime +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index cb2f51e2011..159be939300 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -2,12 +2,13 @@ # # 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 +# id :integer not null, primary key +# token :string(255) +# project_id :integer +# deleted_at :datetime +# created_at :datetime +# updated_at :datetime +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 31b56953a13..71e84091cb7 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -3,12 +3,13 @@ # Table name: ci_variables # # id :integer not null, primary key -# project_id :integer not null +# project_id :integer # key :string(255) # value :text # encrypted_value :text # encrypted_value_salt :string(255) # encrypted_value_iv :string(255) +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index b8f901b3433..82c68ff6cb1 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -29,6 +29,7 @@ # target_url :string(255) # description :string(255) # artifacts_file :text +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/external_wiki_service_spec.rb index b198aa77526..d37978720bf 100644 --- a/spec/models/external_wiki_service_spec.rb +++ b/spec/models/external_wiki_service_spec.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require 'spec_helper' diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index d61c1c96bde..5b0883d8702 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -29,6 +29,7 @@ # target_url :string(255) # description :string(255) # artifacts_file :text +# gl_project_id :integer # require 'spec_helper' diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index ba5acceadff..3c995053eec 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -11,7 +11,6 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) -# public :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index e0653a8327d..291e6200a5b 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -2,25 +2,28 @@ # # Table name: merge_requests # -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) +# id :integer not null, primary key +# target_branch :string(255) not null +# source_branch :string(255) not null +# source_project_id :integer not null +# author_id :integer +# assignee_id :integer +# title :string(255) +# created_at :datetime +# updated_at :datetime +# milestone_id :integer +# state :string(255) +# merge_status :string(255) +# target_project_id :integer not null +# iid :integer +# description :text +# position :integer default(0) +# locked_at :datetime +# updated_by_id :integer +# merge_error :string(255) +# merge_params :text +# merge_when_build_succeeds :boolean default(FALSE), not null +# merge_user_id :integer # require 'spec_helper' diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 4fa2d2bc4d2..e0b3290e416 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -11,7 +11,6 @@ # type :string(255) # description :string(255) default(""), not null # avatar :string(255) -# public :boolean default(FALSE) # require 'spec_helper' diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 400bdf2d962..a3de23369e1 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -29,6 +29,13 @@ # import_source :string(255) # commit_count :integer default(0) # import_error :text +# ci_id :integer +# builds_enabled :boolean default(TRUE), not null +# shared_runners_enabled :boolean default(TRUE), not null +# runners_token :string +# build_coverage_regex :string +# build_allow_git_fetch :boolean default(TRUE), not null +# build_timeout :integer default(3600), not null # require 'spec_helper' diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 0ca82365b98..173628c08d0 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -16,6 +16,7 @@ # merge_requests_events :boolean default(TRUE) # tag_push_events :boolean default(TRUE) # note_events :boolean default(TRUE), not null +# build_events :boolean default(FALSE), not null # require 'spec_helper' diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a16161e673e..3cd63b2b0e8 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,62 +2,63 @@ # # Table name: users # -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# unlock_token :string(255) -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# last_credential_check_at :datetime -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# notification_email :string(255) -# hide_no_password :boolean default(FALSE) -# password_automatically_set :boolean default(FALSE) -# location :string(255) -# encrypted_otp_secret :string(255) -# encrypted_otp_secret_iv :string(255) -# encrypted_otp_secret_salt :string(255) -# otp_required_for_login :boolean default(FALSE), not null -# otp_backup_codes :text -# public_email :string(255) default(""), not null -# dashboard :integer default(0) -# project_view :integer default(0) -# consumed_timestep :integer -# layout :integer default(0) -# hide_project_limit :boolean default(FALSE) +# id :integer not null, primary key +# email :string(255) default(""), not null +# encrypted_password :string(255) default(""), not null +# reset_password_token :string(255) +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0) +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string(255) +# last_sign_in_ip :string(255) +# created_at :datetime +# updated_at :datetime +# name :string(255) +# admin :boolean default(FALSE), not null +# projects_limit :integer default(10) +# skype :string(255) default(""), not null +# linkedin :string(255) default(""), not null +# twitter :string(255) default(""), not null +# authentication_token :string(255) +# theme_id :integer default(1), not null +# bio :string(255) +# failed_attempts :integer default(0) +# locked_at :datetime +# username :string(255) +# can_create_group :boolean default(TRUE), not null +# can_create_team :boolean default(TRUE), not null +# state :string(255) +# color_scheme_id :integer default(1), not null +# notification_level :integer default(1), not null +# password_expires_at :datetime +# created_by_id :integer +# last_credential_check_at :datetime +# avatar :string(255) +# confirmation_token :string(255) +# confirmed_at :datetime +# confirmation_sent_at :datetime +# unconfirmed_email :string(255) +# hide_no_ssh_key :boolean default(FALSE) +# website_url :string(255) default(""), not null +# notification_email :string(255) +# hide_no_password :boolean default(FALSE) +# password_automatically_set :boolean default(FALSE) +# location :string(255) +# encrypted_otp_secret :string(255) +# encrypted_otp_secret_iv :string(255) +# encrypted_otp_secret_salt :string(255) +# otp_required_for_login :boolean default(FALSE), not null +# otp_backup_codes :text +# public_email :string(255) default(""), not null +# dashboard :integer default(0) +# project_view :integer default(0) +# consumed_timestep :integer +# layout :integer default(0) +# hide_project_limit :boolean default(FALSE) +# unlock_token :string +# otp_grace_period_started_at :datetime # require 'spec_helper' -- cgit v1.2.1 From da53fcba2d0e46e47a8fd6a79591a6367e863d57 Mon Sep 17 00:00:00 2001 From: Janis Meybohm Date: Wed, 23 Dec 2015 11:17:25 +0100 Subject: Enable Microsoft Azure OAuth2 support --- CHANGELOG | 1 + Gemfile | 1 + Gemfile.lock | 5 ++ app/assets/images/auth_buttons/azure_64.png | Bin 0 -> 986 bytes app/helpers/auth_helper.rb | 2 +- doc/integration/azure.md | 83 ++++++++++++++++++++++++++++ doc/integration/omniauth.md | 1 + 7 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/auth_buttons/azure_64.png create mode 100644 doc/integration/azure.md diff --git a/CHANGELOG b/CHANGELOG index cd745d3746a..8ee32013772 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,7 @@ v 8.4.0 (unreleased) - Fix API project lookups when querying with a namespace with dots (Stan Hu) - Update version check images to use SVG - Validate README format before displaying + - Enable Microsoft Azure OAuth2 support (Janis Meybohm) v 8.3.3 (unreleased) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) diff --git a/Gemfile b/Gemfile index 3ce4ba4a2a5..6145745b6f3 100644 --- a/Gemfile +++ b/Gemfile @@ -33,6 +33,7 @@ gem 'omniauth-saml', '~> 1.4.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth_crowd' +gem 'omniauth-azure-oauth2' gem 'rack-oauth2', '~> 1.2.1' # reCAPTCHA protection diff --git a/Gemfile.lock b/Gemfile.lock index ffb7cef0aba..2b42f325503 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -488,6 +488,10 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -927,6 +931,7 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd + omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) diff --git a/app/assets/images/auth_buttons/azure_64.png b/app/assets/images/auth_buttons/azure_64.png new file mode 100644 index 00000000000..a82c751e001 Binary files /dev/null and b/app/assets/images/auth_buttons/azure_64.png differ diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb index 0cfc0565e84..de669e529a7 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 facebook).freeze + PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2).freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze def ldap_enabled? diff --git a/doc/integration/azure.md b/doc/integration/azure.md new file mode 100644 index 00000000000..48dddf7df44 --- /dev/null +++ b/doc/integration/azure.md @@ -0,0 +1,83 @@ +# Microsoft Azure OAuth2 OmniAuth Provider + +To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your application with Azure. Azure will generate a client ID and secret key for you to use. + +1. Sign in to the [Azure Management Portal](https://manage.windowsazure.com>). + +1. Select "Active Directory" on the left and choose the directory you want to use to register GitLab. + +1. Select "Applications" at the top bar and click the "Add" button the bottom. + +1. Select "Add an application my organization is developing". + +1. Provide the project information and click the "Next" button. + - Name: 'GitLab' works just fine here. + - Type: 'WEB APPLICATION AND/OR WEB API' + +1. On the "App properties" page enter the needed URI's and click the "Complete" button. + - SIGN-IN URL: Enter the URL of your GitLab installation (e.g 'https://gitlab.mycompany.com/') + - APP ID URI: Enter the endpoint URL for Microsoft to use, just has to be unique (e.g 'https://mycompany.onmicrosoft.com/gitlab') + +1. Select "Configure" in the top menu. + +1. Add a "Reply URL" pointing to the Azure OAuth callback of your GitLab installation (e.g. https://gitlab.mycompany.com/users/auth/azure_oauth2/callback). + +1. Create a "Client secret" by selecting a duration, the secret will be generated as soon as you click the "Save" button in the bottom menu.. + +1. Note the "CLIENT ID" and the "CLIENT SECRET". + +1. Select "View endpoints" from the bottom menu. + +1. You will see lots of endpoint URLs in the form 'https://login.microsoftonline.com/TENANT ID/...', note down the TENANT ID part of one of those endpoints. + +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" => "azure_oauth2", + "args" => { + "client_id" => "CLIENT ID", + "client_secret" => "CLIENT SECRET", + "tenant_id" => "TENANT ID", + } + } + ] + ``` + + For installations from source: + + ``` + - { name: 'azure_oauth2', + args: { client_id: "CLIENT ID", + client_secret: "CLIENT SECRET", + tenant_id: "TENANT ID" } } + ``` + +1. Replace 'CLIENT ID', 'CLIENT SECRET' and 'TENANT ID' with the values you got above. + +1. Save the configuration file. + +1. Restart GitLab for the changes to take effect. + +On the sign in page there should now be a Microsoft icon below the regular sign in form. Click the icon to begin the authentication process. Microsoft 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/omniauth.md b/doc/integration/omniauth.md index f2b1721fc03..e9e17eb4165 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -78,6 +78,7 @@ Now we can choose one or more of the Supported Providers below to continue confi - [Shibboleth](shibboleth.md) - [SAML](saml.md) - [Crowd](crowd.md) +- [Azure](azure.md) ## Enable OmniAuth for an Existing User -- cgit v1.2.1 From fe19169836e1d7855d0ad5351b3f666dce8fcda1 Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Wed, 6 Jan 2016 15:51:10 +0100 Subject: Use panel-default and .form-actions --- app/views/projects/edit.html.haml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index ef758b5fb7a..31e752c6649 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -174,6 +174,19 @@ .danger-settings + .panel.panel-default + .panel-heading Housekeeping + .errors-holder + .panel-body + %p + Runs a number of housekeeping tasks within the current repository, + such as compressing file revisions and removing unreachable objects. + %br + + .form-actions + = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), + method: :post, class: "btn btn-default" + - if can? current_user, :archive_project, @project - if @project.archived? .panel.panel-success @@ -210,17 +223,6 @@ - else .nothing-here-block Only the project owner can archive a project - .panel.panel-default.panel.panel-warning - .panel-heading Housekeeping - .errors-holder - .panel-body - %p - Runs a number of housekeeping tasks within the current repository, - such as compressing file revisions and removing unreachable objects. - %br - = link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project), - method: :post, class: "btn btn-warning" - .panel.panel-default.panel.panel-warning .panel-heading Rename repository .errors-holder -- cgit v1.2.1 From 0b2fa3bfa4bea00973ad6b8c3dfb8302d84fe4de Mon Sep 17 00:00:00 2001 From: Jose Corcuera Date: Wed, 6 Jan 2016 09:54:43 -0500 Subject: Fix problem with projects ending with .keys #3076 --- CHANGELOG | 1 + config/routes.rb | 6 +++--- spec/routing/project_routing_spec.rb | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cd745d3746a..c0b03a3ed31 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,7 @@ v 8.4.0 (unreleased) - Add "Frequently used" category to emoji picker - Add CAS support (tduehr) - Add link to merge request on build detail page + - Fix: Problem with projects ending with .keys (Jose Corcuera) - Revert back upvote and downvote button to the issue and MR pages - Swap position of Assignee and Author selector on Issuables (Zeger-Jan van de Weg) - Add system hook messages for project rename and transfer (Steve Norman) diff --git a/config/routes.rb b/config/routes.rb index 3e7d9f78710..d7a9df10eba 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -52,9 +52,6 @@ Rails.application.routes.draw do API::API.logger Rails.logger mount API::API => '/api' - # Get all keys of user - get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ } - constraint = lambda { |request| request.env['warden'].authenticate? and request.env['warden'].user.admin? } constraints constraint do mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq @@ -668,5 +665,8 @@ Rails.application.routes.draw do end end + # Get all keys of user + get ':username.keys' => 'profiles/keys#get_keys' , constraints: { username: /.*/ } + get ':id' => 'namespaces#show', constraints: { id: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 82f62a8709c..f0f0fbe619c 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -80,6 +80,7 @@ describe ProjectsController, 'routing' do it 'to #show' do expect(get('/gitlab/gitlabhq')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq') + expect(get('/gitlab/gitlabhq.keys')).to route_to('projects#show', namespace_id: 'gitlab', id: 'gitlabhq.keys') end it 'to #update' do -- cgit v1.2.1 From bde76f62fc2bd259dcc37ca649a01a84035ddcd9 Mon Sep 17 00:00:00 2001 From: Jeroen Nijhof Date: Wed, 6 Jan 2016 15:57:49 +0100 Subject: Added CHANGELOG for housekeeping and changed GITLAB_SHELL_VERSION to 2.6.10 --- CHANGELOG | 1 + GITLAB_SHELL_VERSION | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index cd745d3746a..394a25cfa06 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.4.0 (unreleased) + - Add housekeeping function to project settings page - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Don't notify users twice if they are both project watchers and subscribers (Stan Hu) - Implement new UI for group page diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index d48d3702aed..a04abec9149 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -2.6.9 +2.6.10 -- cgit v1.2.1 From 7549102bb727daecc51da84af39956b32fc41537 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 15:30:02 +0100 Subject: Store SQL/view timings in milliseconds Transaction timings are also already stored in milliseconds, this keeps things consistent. --- lib/gitlab/metrics/subscribers/action_view.rb | 8 ++++++-- lib/gitlab/metrics/subscribers/active_record.rb | 6 +++++- spec/lib/gitlab/metrics/subscribers/action_view_spec.rb | 4 ++-- spec/lib/gitlab/metrics/subscribers/active_record_spec.rb | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 7c0105d543a..84d9e383625 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -19,7 +19,7 @@ module Gitlab values = values_for(event) tags = tags_for(event) - current_transaction.increment(:view_duration, event.duration) + current_transaction.increment(:view_duration, duration(event)) current_transaction.add_metric(SERIES, values, tags) end @@ -28,7 +28,7 @@ module Gitlab end def values_for(event) - { duration: event.duration } + { duration: duration(event) } end def tags_for(event) @@ -48,6 +48,10 @@ module Gitlab def current_transaction Transaction.current end + + def duration(event) + event.duration * 1000.0 + end end end end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 8008b3bc895..6fa73e7a3be 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -8,7 +8,7 @@ module Gitlab def sql(event) return unless current_transaction - current_transaction.increment(:sql_duration, event.duration) + current_transaction.increment(:sql_duration, duration(event)) end private @@ -16,6 +16,10 @@ module Gitlab def current_transaction Transaction.current end + + def duration(event) + event.duration * 1000.0 + end end end end diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb index 05e4fbbeb51..0a4cc5e929b 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Metrics::Subscribers::ActionView do describe '#render_template' do it 'tracks rendering of a template' do - values = { duration: 2.1 } + values = { duration: 2100 } tags = { view: 'app/views/x.html.haml', file: 'app/views/x.html.haml', @@ -29,7 +29,7 @@ describe Gitlab::Metrics::Subscribers::ActionView do } expect(transaction).to receive(:increment). - with(:view_duration, 2.1) + with(:view_duration, 2100) expect(transaction).to receive(:add_metric). with(described_class::SERIES, values, tags) diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 7bc070a4d09..ca86142a2f4 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do and_return(transaction) expect(transaction).to receive(:increment). - with(:sql_duration, 0.2) + with(:sql_duration, 200) subscriber.sql(event) end -- cgit v1.2.1 From 8fdc00bd4c59183a20a60a6b93228268230bbd2e Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 17:49:02 +0100 Subject: Remove InfluxDB username/password InfluxDB over UDP doesn't use authentication, thus there's no need for these settings. --- .../admin/application_settings_controller.rb | 2 -- app/views/admin/application_settings/_form.html.haml | 8 -------- .../20160106164438_remove_influxdb_credentials.rb | 6 ++++++ db/schema.rb | 20 +++++++++----------- lib/gitlab/metrics.rb | 6 +----- 5 files changed, 16 insertions(+), 26 deletions(-) create mode 100644 db/migrate/20160106164438_remove_influxdb_credentials.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 10e736fd362..44d06b6a647 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -70,8 +70,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_enabled, :metrics_host, :metrics_port, - :metrics_username, - :metrics_password, :metrics_pool_size, :metrics_timeout, :metrics_method_call_threshold, diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 89b38a0dad0..81337432ab7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -179,14 +179,6 @@ your server configuration specifies a database to store data in when sending messages to this port, without it metrics data will not be saved. - .form-group - = f.label :metrics_username, 'InfluxDB username', class: 'control-label col-sm-2' - .col-sm-10 - = f.text_field :metrics_username, class: 'form-control' - .form-group - = f.label :metrics_password, 'InfluxDB password', class: 'control-label col-sm-2' - .col-sm-10 - = f.text_field :metrics_password, class: 'form-control' .form-group = f.label :metrics_pool_size, 'Connection pool size', class: 'control-label col-sm-2' .col-sm-10 diff --git a/db/migrate/20160106164438_remove_influxdb_credentials.rb b/db/migrate/20160106164438_remove_influxdb_credentials.rb new file mode 100644 index 00000000000..47e74400b97 --- /dev/null +++ b/db/migrate/20160106164438_remove_influxdb_credentials.rb @@ -0,0 +1,6 @@ +class RemoveInfluxdbCredentials < ActiveRecord::Migration + def change + remove_column :application_settings, :metrics_username, :string + remove_column :application_settings, :metrics_password, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 48e6983684a..2ded8a45e18 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: 20151229112614) do +ActiveRecord::Schema.define(version: 20160106164438) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -50,16 +50,14 @@ ActiveRecord::Schema.define(version: 20151229112614) do t.boolean "shared_runners_enabled", default: true, null: false t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" - t.boolean "require_two_factor_authentication", default: false - t.integer "two_factor_grace_period", default: 48 - t.boolean "metrics_enabled", default: false - t.string "metrics_host", default: "localhost" - t.string "metrics_username" - t.string "metrics_password" - t.integer "metrics_pool_size", default: 16 - t.integer "metrics_timeout", default: 10 - t.integer "metrics_method_call_threshold", default: 10 - t.boolean "recaptcha_enabled", default: false + t.boolean "require_two_factor_authentication", default: false + t.integer "two_factor_grace_period", default: 48 + t.boolean "metrics_enabled", default: false + t.string "metrics_host", default: "localhost" + t.integer "metrics_pool_size", default: 16 + t.integer "metrics_timeout", default: 10 + t.integer "metrics_method_call_threshold", default: 10 + t.boolean "recaptcha_enabled", default: false t.string "recaptcha_site_key" t.string "recaptcha_private_key" t.integer "metrics_port", default: 8089 diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index ee88ab34d6c..44356a0e42c 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -13,8 +13,6 @@ module Gitlab timeout: current_application_settings[:metrics_timeout], method_call_threshold: current_application_settings[:metrics_method_call_threshold], host: current_application_settings[:metrics_host], - username: current_application_settings[:metrics_username], - password: current_application_settings[:metrics_password], port: current_application_settings[:metrics_port] } end @@ -90,12 +88,10 @@ module Gitlab if enabled? @pool = ConnectionPool.new(size: settings[:pool_size], timeout: settings[:timeout]) do host = settings[:host] - user = settings[:username] - pw = settings[:password] port = settings[:port] InfluxDB::Client. - new(udp: { host: host, port: port }, username: user, password: pw) + new(udp: { host: host, port: port }) end end end -- cgit v1.2.1 From 4d41294d71f2a8910a3aa5d475f8eb3923ca3531 Mon Sep 17 00:00:00 2001 From: Tommy Beadle Date: Wed, 6 Jan 2016 11:42:31 -0500 Subject: Include the user_id in user_*_team system hooks. This fixes an issue where the user_id is not included in the data for user_add_to_team and user_remove_from_team system hooks. The documentation already states that the user_id should be included. --- app/services/system_hooks_service.rb | 1 + spec/services/system_hooks_service_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 6dc854ec33d..9f677444f1f 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -101,6 +101,7 @@ class SystemHooksService project_id: model.project.id, user_name: model.user.name, user_email: model.user.email, + user_id: model.user.id, access_level: model.human_access, project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase } diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 4455ae7b321..41df4951d16 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -13,8 +13,8 @@ describe SystemHooksService, services: true do it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :user_id, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :user_id, :access_level, :project_visibility) } it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } -- cgit v1.2.1 From 12e32224c17253f9616cd05d0bce806881fa8db9 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 6 Jan 2016 20:05:22 +0100 Subject: Add documentation on enabling/disabling GitLab CI [ci skip] --- doc/README.md | 1 + doc/ci/README.md | 1 + doc/ci/enable_or_disable_ci.md | 70 +++++++++++++++++++++++++++++++++++++++ doc/ci/img/features_settings.png | Bin 0 -> 18691 bytes 4 files changed, 72 insertions(+) create mode 100644 doc/ci/enable_or_disable_ci.md create mode 100644 doc/ci/img/features_settings.png diff --git a/doc/README.md b/doc/README.md index f4553a899d3..25fe3abcb9a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -19,6 +19,7 @@ ## CI Documentation - [Quick Start](ci/quick_start/README.md) +- [Enable or disable GitLab CI](ci/enable_or_disable_ci.md) - [Configuring project (.gitlab-ci.yml)](ci/yaml/README.md) - [Configuring runner](ci/runners/README.md) - [Configuring deployment](ci/deployment/README.md) diff --git a/doc/ci/README.md b/doc/ci/README.md index a1f5513d88e..4cdd2e1ad33 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -3,6 +3,7 @@ ### User documentation * [Quick Start](quick_start/README.md) +* [Enable or disable GitLab CI](enable_or_disable_ci.md) * [Configuring project (.gitlab-ci.yml)](yaml/README.md) * [Configuring runner](runners/README.md) * [Configuring deployment](deployment/README.md) diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md new file mode 100644 index 00000000000..2803bb5f34a --- /dev/null +++ b/doc/ci/enable_or_disable_ci.md @@ -0,0 +1,70 @@ +## Enable or disable GitLab CI + +_To effectively use GitLab CI, you need a valid [`.gitlab-ci.yml`](yaml/README.md) +file present at the root directory of your project and a +[runner](runners/README.md) properly set up. You can read our +[quick start guide](quick_start/README.md) to get you started._ + +If you are using an external CI server like Jenkins or Drone CI, it is advised +to disable GitLab CI in order to not have any conflicts with the commits status +API. + +--- + +As of GitLab 8.2, GitLab CI is mainly exposed via the `/builds` page of a +project. Disabling GitLab CI in a project does not delete any previous builds. +In fact, the `/builds` page can still be accessed, although it's hidden from +the left sidebar menu. + +GitLab CI is enabled by default on new installations and can be disabled either +individually under each project's settings, or site wide by modifying the +settings in `gitlab.yml` and `gitlab.rb` for source and Omnibus installations +respectively. + +### Per project user setting + +The setting to enable or disable GitLab CI can be found with the name **Builds** +under the **Features** area of a project's settings along with **Issues**, +**Merge Requests**, **Wiki** and **Snippets**. Select or deselect the checkbox +and hit **Save** for the settings to take effect. + +![Features settings](img/features_settings.png) + +--- + +### Site wide administrator setting + +You can disable GitLab CI site wide, by modifying the settings in `gitlab.yml` +and `gitlab.rb` for source and Omnibus installations respectively. + +Two things to note. + +1. Disabling GitLab CI, will affect only newly created projects. Projects that + had it enabled prior this modification, will work as before. +1. Even if you disable GitLab CI, users will still be able to enable it in the + project's settings. + +--- + +For installations from source, open `gitlab.yml` with your editor and set +`builds` to `false`: + +```yaml +## Default project features settings +default_projects_features: + issues: true + merge_requests: true + wiki: true + snippets: false + builds: false +``` + +Save the file and restart GitLab: `sudo service gitlab restart`. + +For Omnibus installations, edit `/etc/gitlab/gitlab.rb` and add the line: + +``` +gitlab-rails['gitlab_default_projects_features_builds'] = false +``` + +Save the file and reconfigure GitLab: `sudo gitlab-ctl reconfigure`. diff --git a/doc/ci/img/features_settings.png b/doc/ci/img/features_settings.png new file mode 100644 index 00000000000..17aba5d14d8 Binary files /dev/null and b/doc/ci/img/features_settings.png differ -- cgit v1.2.1 From 7d013f7848ea46e68665a8efcd9d4453584945e1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 6 Jan 2016 21:05:46 -0800 Subject: Fix missing date of month in network graph when commits span a month Closes #3635, #1383 --- CHANGELOG | 1 + app/assets/javascripts/branch-graph.js.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index cd745d3746a..35577e21415 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.4.0 (unreleased) + - Fix missing date of month in network graph when commits span a month (Stan Hu) - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Don't notify users twice if they are both project watchers and subscribers (Stan Hu) - Implement new UI for group page diff --git a/app/assets/javascripts/branch-graph.js.coffee b/app/assets/javascripts/branch-graph.js.coffee index 917228bd276..f2fd2a775a4 100644 --- a/app/assets/javascripts/branch-graph.js.coffee +++ b/app/assets/javascripts/branch-graph.js.coffee @@ -66,7 +66,7 @@ class @BranchGraph r.rect(40, 0, 30, @barHeight).attr fill: "#444" for day, mm in @days - if cuday isnt day[0] + if cuday isnt day[0] || cumonth isnt day[1] # Dates r.text(55, @offsetY + @unitTime * mm, day[0]) .attr( -- cgit v1.2.1 From a8e23bd1b5029fb04ecb22f9c60fe7a8f9d45091 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 7 Jan 2016 10:10:46 +0100 Subject: Fix hyphenation typos [ci skip] --- doc/ci/enable_or_disable_ci.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md index 2803bb5f34a..9bd2f5aff22 100644 --- a/doc/ci/enable_or_disable_ci.md +++ b/doc/ci/enable_or_disable_ci.md @@ -17,11 +17,11 @@ In fact, the `/builds` page can still be accessed, although it's hidden from the left sidebar menu. GitLab CI is enabled by default on new installations and can be disabled either -individually under each project's settings, or site wide by modifying the +individually under each project's settings, or site-wide by modifying the settings in `gitlab.yml` and `gitlab.rb` for source and Omnibus installations respectively. -### Per project user setting +### Per-project user setting The setting to enable or disable GitLab CI can be found with the name **Builds** under the **Features** area of a project's settings along with **Issues**, @@ -32,15 +32,15 @@ and hit **Save** for the settings to take effect. --- -### Site wide administrator setting +### Site-wide administrator setting -You can disable GitLab CI site wide, by modifying the settings in `gitlab.yml` +You can disable GitLab CI site-wide, by modifying the settings in `gitlab.yml` and `gitlab.rb` for source and Omnibus installations respectively. -Two things to note. +Two things to note: -1. Disabling GitLab CI, will affect only newly created projects. Projects that - had it enabled prior this modification, will work as before. +1. Disabling GitLab CI, will affect only newly-created projects. Projects that + had it enabled prior to this modification, will work as before. 1. Even if you disable GitLab CI, users will still be able to enable it in the project's settings. -- cgit v1.2.1 From 8884505350c8594e663c5af6feb8eb1b06f3a0c8 Mon Sep 17 00:00:00 2001 From: koreamic Date: Sun, 13 Dec 2015 02:27:48 +0900 Subject: Add new feature to find file Using the fuzzy filter, develop "file finder" feature. - feedback(http://feedback.gitlab.com/forums/176466-general/suggestions/4987909-add-file-finder-fuzzy-input-in-files-tab-to-ju ) - fuzzy filter(https://github.com/jeancroy/fuzzaldrin-plus) - shortcuts(when "t" was hitted at tree view, go to 'file find' page and 'esc' is to go back) - depends on gitlab_git 7.2.22 --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 2 +- app/assets/javascripts/application.js.coffee | 1 + app/assets/javascripts/dispatcher.js.coffee | 4 +- app/assets/javascripts/project_find_file.js.coffee | 125 +++++++++++++++++++++ .../javascripts/shortcuts_find_file.js.coffee | 19 ++++ app/assets/javascripts/shortcuts_tree.coffee | 4 + app/assets/stylesheets/pages/tree.scss | 8 ++ app/controllers/projects/find_file_controller.rb | 26 +++++ app/controllers/projects/refs_controller.rb | 2 + app/models/repository.rb | 5 + app/views/help/_shortcuts.html.haml | 26 +++++ app/views/layouts/nav/_project.html.haml | 3 +- app/views/projects/_find_file_link.html.haml | 3 + app/views/projects/find_file/show.html.haml | 27 +++++ app/views/projects/tree/show.html.haml | 8 +- config/routes.rb | 18 +++ doc/workflow/shortcuts.png | Bin 78736 -> 48782 bytes features/project/find_file.feature | 42 +++++++ features/steps/project/project_find_file.rb | 73 ++++++++++++ features/steps/shared/paths.rb | 4 + .../projects/find_file_controller_spec.rb | 66 +++++++++++ spec/routing/project_routing_spec.rb | 12 ++ vendor/assets/javascripts/fuzzaldrin-plus.min.js | 1 + 25 files changed, 473 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/project_find_file.js.coffee create mode 100644 app/assets/javascripts/shortcuts_find_file.js.coffee create mode 100644 app/assets/javascripts/shortcuts_tree.coffee create mode 100644 app/controllers/projects/find_file_controller.rb create mode 100644 app/views/projects/_find_file_link.html.haml create mode 100644 app/views/projects/find_file/show.html.haml create mode 100644 features/project/find_file.feature create mode 100644 features/steps/project/project_find_file.rb create mode 100644 spec/controllers/projects/find_file_controller_spec.rb create mode 100644 vendor/assets/javascripts/fuzzaldrin-plus.min.js diff --git a/CHANGELOG b/CHANGELOG index e7f1d2b67da..22fb91baaf0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -106,6 +106,7 @@ v 8.3.0 - Fix online editor should not remove newlines at the end of the file - Expose Git's version in the admin area - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) + - Add file finder feature in tree view v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/Gemfile b/Gemfile index 6145745b6f3..6b0bc241494 100644 --- a/Gemfile +++ b/Gemfile @@ -49,7 +49,7 @@ gem "browser", '~> 1.0.0' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 7.2.20' +gem "gitlab_git", '~> 7.2.22' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes diff --git a/Gemfile.lock b/Gemfile.lock index 2b42f325503..a1168ed3b7a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -887,7 +887,7 @@ DEPENDENCIES github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) gitlab_emoji (~> 0.2.0) - gitlab_git (~> 7.2.20) + gitlab_git (~> 7.2.22) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.1.0) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index b9b095e004a..c095e5ae2b1 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -40,6 +40,7 @@ #= require shortcuts_network #= require jquery.nicescroll.min #= require_tree . +#= require fuzzaldrin-plus.min window.slugify = (text) -> text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 69e061ce6e9..58d6b9d4060 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -87,7 +87,9 @@ class Dispatcher new GroupAvatar() when 'projects:tree:show' new TreeView() - shortcut_handler = new ShortcutsNavigation() + shortcut_handler = new ShortcutsTree() + when 'projects:find_file:show' + shortcut_handler = true when 'projects:blob:show' new LineHighlighter() shortcut_handler = new ShortcutsNavigation() diff --git a/app/assets/javascripts/project_find_file.js.coffee b/app/assets/javascripts/project_find_file.js.coffee new file mode 100644 index 00000000000..0dd32352c34 --- /dev/null +++ b/app/assets/javascripts/project_find_file.js.coffee @@ -0,0 +1,125 @@ +class @ProjectFindFile + constructor: (@element, @options)-> + @filePaths = {} + @inputElement = @element.find(".file-finder-input") + + # init event + @initEvent() + + # focus text input box + @inputElement.focus() + + # load file list + @load(@options.url) + + # init event + initEvent: -> + @inputElement.off "keyup" + @inputElement.on "keyup", (event) => + target = $(event.target) + value = target.val() + oldValue = target.data("oldValue") ? "" + + if value != oldValue + target.data("oldValue", value) + @findFile() + @element.find("tr.tree-item").eq(0).addClass("selected").focus() + + @element.find(".tree-content-holder .tree-table").on "click", (event) -> + if (event.target.nodeName != "A") + path = @element.find(".tree-item-file-name a", this).attr("href") + location.href = path if path + + # find file + findFile: -> + searchText = @inputElement.val() + result = if searchText.length > 0 then fuzzaldrinPlus.filter(@filePaths, searchText) else @filePaths + @renderList result, searchText + + # files pathes load + load: (url) -> + $.ajax + url: url + method: "get" + dataType: "json" + success: (data) => + @element.find(".loading").hide() + @filePaths = data + @findFile() + @element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus() + + # render result + renderList: (filePaths, searchText) -> + @element.find(".tree-table > tbody").empty() + + for filePath, i in filePaths + break if i == 20 + + if searchText + matches = fuzzaldrinPlus.match(filePath, searchText) + + blobItemUrl = "#{@options.blobUrlTemplate}/#{filePath}" + + html = @makeHtml filePath, matches, blobItemUrl + @element.find(".tree-table > tbody").append(html) + + # highlight text(awefwbwgtc -> awefwbwgtc ) + highlighter = (element, text, matches) -> + lastIndex = 0 + highlightText = "" + matchedChars = [] + + for matchIndex in matches + unmatched = text.substring(lastIndex, matchIndex) + + if unmatched + element.append(matchedChars.join("").bold()) if matchedChars.length + matchedChars = [] + element.append(document.createTextNode(unmatched)) + + matchedChars.push(text[matchIndex]) + lastIndex = matchIndex + 1 + + element.append(matchedChars.join("").bold()) if matchedChars.length + element.append(document.createTextNode(text.substring(lastIndex))) + + # make tbody row html + makeHtml: (filePath, matches, blobItemUrl) -> + $tr = $("") + if matches + $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)) + else + $tr.find("a").attr("href", blobItemUrl).text(filePath) + + return $tr + + selectRow: (type) -> + rows = @element.find(".files-slider tr.tree-item") + selectedRow = @element.find(".files-slider tr.tree-item.selected") + + if rows && rows.length > 0 + if selectedRow && selectedRow.length > 0 + if type == "UP" + next = selectedRow.prev() + else if type == "DOWN" + next = selectedRow.next() + + if next.length > 0 + selectedRow.removeClass "selected" + selectedRow = next + else + selectedRow = rows.eq(0) + selectedRow.addClass("selected").focus() + + selectRowUp: => + @selectRow "UP" + + selectRowDown: => + @selectRow "DOWN" + + goToTree: => + location.href = @options.treeUrl + + goToBlob: => + path = @element.find(".tree-item.selected .tree-item-file-name a").attr("href") + location.href = path if path diff --git a/app/assets/javascripts/shortcuts_find_file.js.coffee b/app/assets/javascripts/shortcuts_find_file.js.coffee new file mode 100644 index 00000000000..311e80bae19 --- /dev/null +++ b/app/assets/javascripts/shortcuts_find_file.js.coffee @@ -0,0 +1,19 @@ +#= require shortcuts_navigation + +class @ShortcutsFindFile extends ShortcutsNavigation + constructor: (@projectFindFile) -> + super() + _oldStopCallback = Mousetrap.stopCallback + # override to fire shortcuts action when focus in textbox + Mousetrap.stopCallback = (event, element, combo) => + if element == @projectFindFile.inputElement[0] and (combo == 'up' or combo == 'down' or combo == 'esc' or combo == 'enter') + # when press up/down key in textbox, cusor prevent to move to home/end + event.preventDefault() + return false + + return _oldStopCallback(event, element, combo) + + Mousetrap.bind('up', @projectFindFile.selectRowUp) + Mousetrap.bind('down', @projectFindFile.selectRowDown) + Mousetrap.bind('esc', @projectFindFile.goToTree) + Mousetrap.bind('enter', @projectFindFile.goToBlob) diff --git a/app/assets/javascripts/shortcuts_tree.coffee b/app/assets/javascripts/shortcuts_tree.coffee new file mode 100644 index 00000000000..ba0839c9fc0 --- /dev/null +++ b/app/assets/javascripts/shortcuts_tree.coffee @@ -0,0 +1,4 @@ +class @ShortcutsTree extends ShortcutsNavigation + constructor: -> + super() + Mousetrap.bind('t', -> ShortcutsTree.findAndFollowLink('.shortcuts-find-file')) diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index d4ab6967ccd..97505edeabf 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,5 +1,13 @@ .tree-holder { + .file-finder { + width: 50%; + .file-finder-input { + width: 95%; + display: inline-block; + } + } + .tree-table { margin-bottom: 0; diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb new file mode 100644 index 00000000000..54a0c447aee --- /dev/null +++ b/app/controllers/projects/find_file_controller.rb @@ -0,0 +1,26 @@ +# Controller for viewing a repository's file structure +class Projects::FindFileController < Projects::ApplicationController + include ExtractsPath + include ActionView::Helpers::SanitizeHelper + include TreeHelper + + before_action :require_non_empty_project + before_action :assign_ref_vars + before_action :authorize_download_code! + + def show + return render_404 unless @repository.commit(@ref) + + respond_to do |format| + format.html + end + end + + def list + file_paths = @repo.ls_files(@ref) + + respond_to do |format| + format.json { render json: file_paths } + end + end +end diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index c4e18c17077..a8f091819ca 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -20,6 +20,8 @@ class Projects::RefsController < Projects::ApplicationController namespace_project_network_path(@project.namespace, @project, @id, @options) when "graphs" namespace_project_graph_path(@project.namespace, @project, @id) + when "find_file" + namespace_project_find_file_path(@project.namespace, @project, @id) when "graphs_commits" commits_namespace_project_graph_path(@project.namespace, @project, @id) else diff --git a/app/models/repository.rb b/app/models/repository.rb index 6ecd2d2f27e..9deb08d93b8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -681,6 +681,11 @@ class Repository end end + def ls_files(ref) + actual_ref = ref || root_ref + raw_repository.ls_files(actual_ref) + end + private def cache diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index e8e331dd109..9ee6f07b26b 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -40,6 +40,32 @@ %td.shortcut .key enter %td Open Selection + %tr + %td.shortcut + .key t + %td Go to finding file + %tbody + %tr + %th + %th Finding Project File + %tr + %td.shortcut + .key + %i.fa.fa-arrow-up + %td Move selection up + %tr + %td.shortcut + .key + %i.fa.fa-arrow-down + %td Move selection down + %tr + %td.shortcut + .key enter + %td Open Selection + %tr + %td.shortcut + .key esc + %td Go back .col-lg-4 %table.shortcut-mappings diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index d3eaf0f3209..270ccfd387f 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -25,7 +25,7 @@ %span Activity - if project_nav_tab? :files - = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do + = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do = icon('files-o fw') %span @@ -117,4 +117,3 @@ %li.hidden = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do Network - diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml new file mode 100644 index 00000000000..08e2fc48be7 --- /dev/null +++ b/app/views/projects/_find_file_link.html.haml @@ -0,0 +1,3 @@ += link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do + = icon('search') + %span Find File diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml new file mode 100644 index 00000000000..2930209fb56 --- /dev/null +++ b/app/views/projects/find_file/show.html.haml @@ -0,0 +1,27 @@ +- page_title "Find File", @ref +- header_title project_title(@project, "Files", project_files_path(@project)) + +.file-finder-holder.tree-holder.clearfix + .gray-content-block.top-block + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'find_file', path: @path + %ul.breadcrumb.repo-breadcrumb + %li + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = @project.path + %li.file-finder + %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path'} + + %div.tree-content-holder + .table-holder + %table.table.files-slider{class: "table_#{@hex_path} tree-table table-striped" } + %tbody + = spinner nil, true + +:coffeescript + projectFindFile = new ProjectFindFile($(".file-finder-holder"), { + url: "#{escape_javascript(namespace_project_files_path(@project.namespace, @project, @ref, @options.merge(format: :json)))}" + treeUrl: "#{escape_javascript(namespace_project_tree_path(@project.namespace, @project, @ref))}" + blobUrlTemplate: "#{escape_javascript(namespace_project_blob_path(@project.namespace, @project, @id || @commit.id))}" + }) + new ShortcutsFindFile(projectFindFile) diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index ec14bd7f65a..c57570afa09 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -3,12 +3,12 @@ = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") - = render 'projects/last_push' -- if can? current_user, :download_code, @project - .tree-download-holder - = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true +.pull-right + = render 'projects/find_file_link' + - if can? current_user, :download_code, @project + = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped', split_button: true #tree-holder.tree-holder.clearfix .gray-content-block.top-block diff --git a/config/routes.rb b/config/routes.rb index 3e7d9f78710..5b69d06eb76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -440,6 +440,24 @@ Rails.application.routes.draw do ) end + scope do + get( + '/find_file/*id', + to: 'find_file#show', + constraints: { id: /.+/, format: /html/ }, + as: :find_file + ) + end + + scope do + get( + '/files/*id', + to: 'find_file#list', + constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ }, + as: :files + ) + end + scope do post( '/create_dir/*id', diff --git a/doc/workflow/shortcuts.png b/doc/workflow/shortcuts.png index 68756ed1f98..e5914aa8e67 100644 Binary files a/doc/workflow/shortcuts.png and b/doc/workflow/shortcuts.png differ diff --git a/features/project/find_file.feature b/features/project/find_file.feature new file mode 100644 index 00000000000..ae8fa245923 --- /dev/null +++ b/features/project/find_file.feature @@ -0,0 +1,42 @@ +@dashboard +Feature: Project Find File + Background: + Given I sign in as a user + And I own a project + And I visit my project's files page + + @javascript + Scenario: Navigate to find file by shortcut + Given I press "t" + Then I should see "find file" page + + Scenario: Navigate to find file + Given I click Find File button + Then I should see "find file" page + + @javascript + Scenario: I search file + Given I visit project find file page + And I fill in file find with "change" + Then I should not see ".gitignore" in files + And I should not see ".gitmodules" in files + And I should see "CHANGELOG" in files + And I should not see "VERSION" in files + + @javascript + Scenario: I search file that not exist + Given I visit project find file page + And I fill in file find with "asdfghjklqwertyuizxcvbnm" + Then I should not see ".gitignore" in files + And I should not see ".gitmodules" in files + And I should not see "CHANGELOG" in files + And I should not see "VERSION" in files + + @javascript + Scenario: I search file that partially matches + Given I visit project find file page + And I fill in file find with "git" + Then I should see ".gitignore" in files + And I should see ".gitmodules" in files + And I should not see "CHANGELOG" in files + And I should not see "VERSION" in files diff --git a/features/steps/project/project_find_file.rb b/features/steps/project/project_find_file.rb new file mode 100644 index 00000000000..8c1d09d6cc6 --- /dev/null +++ b/features/steps/project/project_find_file.rb @@ -0,0 +1,73 @@ +class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + include SharedProjectTab + + step 'I press "t"' do + find('body').native.send_key('t') + end + + step 'I click Find File button' do + click_link 'Find File' + end + + step 'I should see "find file" page' do + ensure_active_main_tab('Files') + expect(page).to have_selector('.file-finder-holder', count: 1) + end + + step 'I fill in Find by path with "git"' do + ensure_active_main_tab('Files') + expect(page).to have_selector('.file-finder-holder', count: 1) + end + + step 'I fill in file find with "git"' do + find_file "git" + end + + step 'I fill in file find with "change"' do + find_file "change" + end + + step 'I fill in file find with "asdfghjklqwertyuizxcvbnm"' do + find_file "asdfghjklqwertyuizxcvbnm" + end + + step 'I should see "VERSION" in files' do + expect(page).to have_content("VERSION") + end + + step 'I should not see "VERSION" in files' do + expect(page).not_to have_content("VERSION") + end + + step 'I should see "CHANGELOG" in files' do + expect(page).to have_content("CHANGELOG") + end + + step 'I should not see "CHANGELOG" in files' do + expect(page).not_to have_content("CHANGELOG") + end + + step 'I should see ".gitmodules" in files' do + expect(page).to have_content(".gitmodules") + end + + step 'I should not see ".gitmodules" in files' do + expect(page).not_to have_content(".gitmodules") + end + + step 'I should see ".gitignore" in files' do + expect(page).to have_content(".gitignore") + end + + step 'I should not see ".gitignore" in files' do + expect(page).not_to have_content(".gitignore") + end + + + def find_file(text) + fill_in 'file_find', with: text + end +end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index b33bd332655..4264c9c6f1a 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -259,6 +259,10 @@ module SharedPaths visit namespace_project_deploy_keys_path(@project.namespace, @project) end + step 'I visit project find file page' do + visit namespace_project_find_file_path(@project.namespace, @project, root_ref) + end + # ---------------------------------------- # "Shop" Project # ---------------------------------------- diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb new file mode 100644 index 00000000000..038dfeb8466 --- /dev/null +++ b/spec/controllers/projects/find_file_controller_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Projects::FindFileController do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + sign_in(user) + + project.team << [user, :master] + controller.instance_variable_set(:@project, project) + end + + describe "GET #show" do + # Make sure any errors accessing the tree in our views bubble up to this spec + render_views + + before do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: id) + end + + context "valid branch" do + let(:id) { 'master' } + it { is_expected.to respond_with(:success) } + end + + context "invalid branch" do + let(:id) { 'invalid-branch' } + it { is_expected.to respond_with(:not_found) } + end + end + + describe "GET #list" do + def go(format: 'json') + get :list, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: id, + format: format + end + + context "valid branch" do + let(:id) { 'master' } + it 'returns an array of file path list' do + go + + json = JSON.parse(response.body) + is_expected.to respond_with(:success) + expect(json).not_to eq(nil) + expect(json.length).to be >= 0 + end + end + + context "invalid branch" do + let(:id) { 'invalid-branch' } + + it 'responds with status 404' do + go + is_expected.to respond_with(:not_found) + end + end + end +end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 82f62a8709c..2a70c190337 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -434,6 +434,18 @@ describe Projects::TreeController, 'routing' do end end +# project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/.+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?/html/} +# project_files GET /:namespace_id/:project_id/files/*id(.:format) projects/find_file#list {:id=>/(?:[^.]|\.(?!json$))+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?/json/} +describe Projects::FindFileController, 'routing' do + it 'to #show' do + expect(get('/gitlab/gitlabhq/find_file/master')).to route_to('projects/find_file#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + end + + it 'to #list' do + expect(get('/gitlab/gitlabhq/files/master.json')).to route_to('projects/find_file#list', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') + end +end + describe Projects::BlobController, 'routing' do it 'to #edit' do expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to( diff --git a/vendor/assets/javascripts/fuzzaldrin-plus.min.js b/vendor/assets/javascripts/fuzzaldrin-plus.min.js new file mode 100644 index 00000000000..3f25c2d8373 --- /dev/null +++ b/vendor/assets/javascripts/fuzzaldrin-plus.min.js @@ -0,0 +1 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0?maxInners:candidates.length;bAllowErrors=!!allowErrors;bKey=key!=null;prepQuery=scorer.prepQuery(query);if(!legacy){for(i=0,len=candidates.length;i0){scoredCandidates.push({candidate:candidate,score:score});if(!--spotLeft){break}}}}else{queryHasSlashes=prepQuery.depth>0;coreQuery=prepQuery.core;for(j=0,len1=candidates.length;j0){scoredCandidates.push({candidate:candidate,score:score})}}}scoredCandidates.sort(sortCandidates);candidates=scoredCandidates.map(pluckCandidates);if(maxResults!=null){candidates=candidates.slice(0,maxResults)}return candidates}}).call(this)},{"./legacy":4,"./scorer":6,"path":7}],2:[function(require,module,exports){(function(){var PathSeparator,filter,legacy_scorer,matcher,prepQueryCache,scorer;scorer=require('./scorer');legacy_scorer=require('./legacy');filter=require('./filter');matcher=require('./matcher');PathSeparator=require('path').sep;prepQueryCache=null;module.exports={filter:function(candidates,query,options){if(!((query!=null?query.length:void 0)&&(candidates!=null?candidates.length:void 0))){return[]}return filter(candidates,query,options)},prepQuery:function(query){return scorer.prepQuery(query)},score:function(string,query,prepQuery,arg){var allowErrors,coreQuery,legacy,queryHasSlashes,ref,score;ref=arg!=null?arg:{},allowErrors=ref.allowErrors,legacy=ref.legacy;if(!((string!=null?string.length:void 0)&&(query!=null?query.length:void 0))){return 0}if(prepQuery==null){prepQuery=prepQueryCache&&prepQueryCache.query===query?prepQueryCache:(prepQueryCache=scorer.prepQuery(query))}if(!legacy){score=scorer.score(string,query,prepQuery,!!allowErrors)}else{queryHasSlashes=prepQuery.depth>0;coreQuery=prepQuery.core;score=legacy_scorer.score(string,coreQuery,queryHasSlashes);if(!queryHasSlashes){score=legacy_scorer.basenameScore(string,coreQuery,score)}}return score},match:function(string,query,prepQuery,arg){var allowErrors,baseMatches,i,matches,query_lw,ref,results,string_lw;allowErrors=(arg!=null?arg:{}).allowErrors;if(!string){return[]}if(!query){return[]}if(string===query){return(function(){results=[];for(var i=0,ref=string.length;0<=ref?iref;0<=ref?i++:i--){results.push(i)}return results}).apply(this)}if(prepQuery==null){prepQuery=prepQueryCache&&prepQueryCache.query===query?prepQueryCache:(prepQueryCache=scorer.prepQuery(query))}if(!(allowErrors||scorer.isMatch(string,prepQuery.core_lw,prepQuery.core_up))){return[]}string_lw=string.toLowerCase();query_lw=prepQuery.query_lw;matches=matcher.match(string,string_lw,prepQuery);if(matches.length===0){return matches}if(string.indexOf(PathSeparator)>-1){baseMatches=matcher.basenameMatch(string,string_lw,prepQuery);matches=matcher.mergeMatches(matches,baseMatches)}return matches}}}).call(this)},{"./filter":1,"./legacy":4,"./matcher":5,"./scorer":6,"path":7}],3:[function(require,module,exports){fuzzaldrinPlus=require('./fuzzaldrin')},{"./fuzzaldrin":2}],4:[function(require,module,exports){(function(){var PathSeparator,queryIsLastPathSegment;PathSeparator=require('path').sep;exports.basenameScore=function(string,query,score){var base,depth,index,lastCharacter,segmentCount,slashCount;index=string.length-1;while(string[index]===PathSeparator){index--}slashCount=0;lastCharacter=index;base=null;while(index>=0){if(string[index]===PathSeparator){slashCount++;if(base==null){base=string.substring(index+1,lastCharacter+1)}}else if(index===0){if(lastCharacterref;stringOffset<=ref?i++:i--){results.push(i)}return results}).apply(this)}queryLength=query.length;stringLength=string.length;indexInQuery=0;indexInString=0;matches=[];while(indexInQuery0){basePos=subject.lastIndexOf(PathSeparator,basePos-1);if(basePos===-1){return[]}}basePos++;end++;return exports.match(subject.slice(basePos,end),subject_lw.slice(basePos,end),prepQuery,basePos)};exports.mergeMatches=function(a,b){var ai,bj,i,j,m,n,out;m=a.length;n=b.length;if(n===0){return a.slice()}if(m===0){return b.slice()}i=-1;j=0;bj=b[j];out=[];while(++i0?csc_diag:scorer.scoreConsecutives(subject,subject_lw,query,query_lw,i,j,start);align=score_diag+scorer.scoreCharacter(i,j,start,acro_score,csc_score)}score_up=score_row[j];csc_diag=csc_row[j];if(score>score_up){move=LEFT}else{score=score_up;move=UP}if(align>score){score=align;move=DIAGONAL}else{csc_score=0}score_row[j]=score;csc_row[j]=csc_score;trace[++pos]=score>0?move:STOP}}i=m-1;j=n-1;pos=i*n+j;backtrack=true;matches=[];while(backtrack&&i>=0&&j>=0){switch(trace[pos]){case UP:i--;pos-=n;break;case LEFT:j--;pos--;break;case DIAGONAL:matches.push(i+offset);j--;i--;pos-=n+1;break;default:backtrack=false}}matches.reverse();return matches}}).call(this)},{"./scorer":6,"path":7}],6:[function(require,module,exports){(function(){var AcronymResult,PathSeparator,Query,basenameScore,coreChars,countDir,doScore,emptyAcronymResult,file_coeff,isMatch,isSeparator,isWordEnd,isWordStart,miss_coeff,opt_char_re,pos_bonus,scoreAcronyms,scoreCharacter,scoreConsecutives,scoreExact,scoreExactMatch,scorePattern,scorePosition,scoreSize,tau_depth,tau_size,truncatedUpperCase,wm;PathSeparator=require('path').sep;wm=150;pos_bonus=20;tau_depth=13;tau_size=85;file_coeff=1.2;miss_coeff=0.75;opt_char_re=/[ _\-:\/\\]/g;exports.coreChars=coreChars=function(query){return query.replace(opt_char_re,'')};exports.score=function(string,query,prepQuery,allowErrors){var score,string_lw;if(prepQuery==null){prepQuery=new Query(query)}if(allowErrors==null){allowErrors=false}if(!(allowErrors||isMatch(string,prepQuery.core_lw,prepQuery.core_up))){return 0}string_lw=string.toLowerCase();score=doScore(string,string_lw,prepQuery);return Math.ceil(basenameScore(string,string_lw,prepQuery,score))};Query=(function(){function Query(query){if(!(query!=null?query.length:void 0)){return null}this.query=query;this.query_lw=query.toLowerCase();this.core=coreChars(query);this.core_lw=this.core.toLowerCase();this.core_up=truncatedUpperCase(this.core);this.depth=countDir(query,query.length)}return Query})();exports.prepQuery=function(query){return new Query(query)};exports.isMatch=isMatch=function(subject,query_lw,query_up){var i,j,m,n,qj_lw,qj_up,si;m=subject.length;n=query_lw.length;if(!m||!n||n>m){return false}i=-1;j=-1;while(++j-1){return scoreExactMatch(subject,subject_lw,query,query_lw,pos,n,m)}score_row=new Array(n);csc_row=new Array(n);sz=scoreSize(n,m);miss_budget=Math.ceil(miss_coeff*n)+5;miss_left=miss_budget;j=-1;while(++j-1){i--}mm=subject_lw.lastIndexOf(query_lw[n-1],m);if(mm>i){m=mm+1}while(++iscore){score=score_up}csc_score=0;if(query_lw[j]===si_lw){start=isWordStart(i,subject,subject_lw);csc_score=csc_diag>0?csc_diag:scoreConsecutives(subject,subject_lw,query,query_lw,i,j,start);align=score_diag+scoreCharacter(i,j,start,acro_score,csc_score);if(align>score){score=align;miss_left=miss_budget}else{if(record_miss&&--miss_left<=0){return score_row[n-1]*sz}record_miss=false}}score_diag=score_up;csc_diag=csc_row[j];csc_row[j]=csc_score;score_row[j]=score}}return score*sz};exports.isWordStart=isWordStart=function(pos,subject,subject_lw){var curr_s,prev_s;if(pos===0){return true}curr_s=subject[pos];prev_s=subject[pos-1];return isSeparator(curr_s)||isSeparator(prev_s)||(curr_s!==subject_lw[pos]&&prev_s===subject_lw[pos-1])};exports.isWordEnd=isWordEnd=function(pos,subject,subject_lw,len){var curr_s,next_s;if(pos===len-1){return true}curr_s=subject[pos];next_s=subject[pos+1];return isSeparator(curr_s)||isSeparator(next_s)||(curr_s===subject_lw[pos]&&next_s!==subject_lw[pos+1])};isSeparator=function(c){return c===' '||c==='.'||c==='-'||c==='_'||c==='/'||c==='\\'};scorePosition=function(pos){var sc;if(poscsc_score?acro_score:csc_score)+10)}return posBonus+wm*csc_score};exports.scoreConsecutives=scoreConsecutives=function(subject,subject_lw,query,query_lw,i,j,start){var k,m,mi,n,nj,sameCase,startPos,sz;m=subject.length;n=query.length;mi=m-i;nj=n-j;k=mi-1){start=isWordStart(pos2,subject,subject_lw);if(start){pos=pos2}}}i=-1;sameCase=0;while(++i1&&n>1)){return emptyAcronymResult}count=0;pos=0;sameCase=0;i=-1;j=-1;while(++j0){basePos=subject.lastIndexOf(PathSeparator,basePos-1);if(basePos===-1){return fullPathScore}}basePos++;end++;basePathScore=doScore(subject.slice(basePos,end),subject_lw.slice(basePos,end),prepQuery);alpha=0.5*tau_depth/(tau_depth+countDir(subject,end+1));return alpha*basePathScore+(1-alpha)*fullPathScore*scoreSize(0,file_coeff*(end-basePos))};exports.countDir=countDir=function(path,end){var count,i;if(end<1){return 0}count=0;i=-1;while(++i=0;i--){var last=parts[i];if(last==='.'){parts.splice(i,1)}else if(last==='..'){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift('..')}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath='',resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=(i>=0)?arguments[i]:process.cwd();if(typeof path!=='string'){throw new TypeError('Arguments to path.resolve must be strings');}else if(!path){continue}resolvedPath=path+'/'+resolvedPath;resolvedAbsolute=path.charAt(0)==='/'}resolvedPath=normalizeArray(filter(resolvedPath.split('/'),function(p){return!!p}),!resolvedAbsolute).join('/');return((resolvedAbsolute?'/':'')+resolvedPath)||'.'};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==='/';path=normalizeArray(filter(path.split('/'),function(p){return!!p}),!isAbsolute).join('/');if(!path&&!isAbsolute){path='.'}if(path&&trailingSlash){path+='/'}return(isAbsolute?'/':'')+path};exports.isAbsolute=function(path){return path.charAt(0)==='/'};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=='string'){throw new TypeError('Arguments to path.join must be strings');}return p}).join('/'))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=='')break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split('/'));var toParts=trim(to.split('/'));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i Date: Thu, 7 Jan 2016 10:39:02 +0100 Subject: Update ./doc/api --- doc/api/projects.md | 36 +++++++++++++++++++++++++++++++++--- lib/api/projects.rb | 9 +++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/doc/api/projects.md b/doc/api/projects.md index 0ca81ffd49e..96a3f08490c 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -76,7 +76,14 @@ Parameters: "updated_at": "2013-09-30T13: 46: 02Z" }, "archived": false, - "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png" + "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 0, + "build_allow_git_fetch": true, + "build_coverage_regex": null, + "build_timeout": 3600, + "runners_token": "4f9e77be0eed5ef29548fccda3b371" }, { "id": 6, @@ -129,7 +136,14 @@ Parameters: } }, "archived": false, - "avatar_url": null + "avatar_url": null, + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 0, + "build_allow_git_fetch": true, + "build_coverage_regex": null, + "build_timeout": 3600, + "runners_token": "b8547b1dc37721d05889db52fa2f02" } ] ``` @@ -244,7 +258,14 @@ Parameters: } }, "archived": false, - "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png" + "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 0, + "build_allow_git_fetch": true, + "build_coverage_regex": null, + "build_timeout": 3600, + "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b" } ``` @@ -409,6 +430,9 @@ Parameters: - `public` (optional) - if `true` same as setting visibility_level = 20 - `visibility_level` (optional) - `import_url` (optional) +- `build_allow_git_fetch` (optional) +- `build_timeout` (optional) +- `build_coverage_regex` (optional) ### Create project for user @@ -431,6 +455,9 @@ Parameters: - `public` (optional) - if `true` same as setting visibility_level = 20 - `visibility_level` (optional) - `import_url` (optional) +- `build_allow_git_fetch` (optional) +- `build_timeout` (optional) +- `build_coverage_regex` (optional) ### Edit project @@ -454,6 +481,9 @@ Parameters: - `snippets_enabled` (optional) - `public` (optional) - if `true` same as setting visibility_level = 20 - `visibility_level` (optional) +- `build_allow_git_fetch` (optional) +- `build_timeout` (optional) +- `build_coverage_regex` (optional) On success, method returns 200 with the updated project. If parameters are invalid, 400 is returned. diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 7dd6b133f9b..31b081266a8 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -98,6 +98,9 @@ module API # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - 0 by default # import_url (optional) + # build_allow_git_fetch (optional) + # build_timeout (optional) + # build_coverage_regex (optional) # Example Request # POST /projects post do @@ -146,6 +149,9 @@ module API # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) # import_url (optional) + # build_allow_git_fetch (optional) + # build_timeout (optional) + # build_coverage_regex (optional) # Example Request # POST /projects/user/:user_id post "user/:user_id" do @@ -207,6 +213,9 @@ module API # shared_runners_enabled (optional) # public (optional) - if true same as setting visibility_level = 20 # visibility_level (optional) - visibility level of a project + # build_allow_git_fetch (optional) + # build_timeout (optional) + # build_coverage_regex (optional) # Example Request # PUT /projects/:id put ':id' do -- cgit v1.2.1 From b0a77a224857ed45afaf642b26ce3ba87d9828a7 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jan 2016 11:04:25 +0100 Subject: Update ./doc/api --- doc/api/README.md | 1 + doc/api/triggers.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 doc/api/triggers.md diff --git a/doc/api/README.md b/doc/api/README.md index 25a31b235cc..1838bcc0bb9 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -23,6 +23,7 @@ - [Namespaces](namespaces.md) - [Settings](settings.md) - [Keys](keys.md) +- [Triggers](triggers.md) ## Clients diff --git a/doc/api/triggers.md b/doc/api/triggers.md new file mode 100644 index 00000000000..6adcc8fe3b8 --- /dev/null +++ b/doc/api/triggers.md @@ -0,0 +1,94 @@ +# Triggers + +## List project triggers + +Get a list of project triggers + +``` +GET /projects/:id/triggers +``` + +Parameters: + +- `id` (required) - The ID of a project + +```json +[ + { + "created_at": "2015-12-23T16:24:34.716Z", + "deleted_at": null, + "id": 1, + "last_used": "2016-01-04T15:41:21.986Z", + "token": "fbdb730c2fbdb095a0862dbd8ab88b", + "updated_at": "2015-12-23T16:24:34.716Z" + }, + { + "created_at": "2015-12-23T16:25:56.760Z", + "deleted_at": null, + "id": 2, + "last_used": null, + "token": "7b9148c158980bbd9bcea92c17522d", + "updated_at": "2015-12-23T16:25:56.760Z" + } +] +``` + +## Get trigger details + +Get details of trigger of a project + +``` +GET /projects/:id/triggers/:trigger_id +``` + +Parameters: + +- `id` (required) - The ID of a project +- `trigger_id` (required) - The ID of a trigger + +```json +{ + "created_at": "2015-12-23T16:25:56.760Z", + "deleted_at": null, + "id": 2, + "last_used": null, + "token": "7b9148c158980bbd9bcea92c17522d", + "updated_at": "2015-12-23T16:25:56.760Z" +} +``` + +## Create a project trigger + +Create a trigger for a project + +``` +POST /projects/:id/triggers +``` + +Parameters: + +- `id` (required) - The ID of a project + +```json +{ + "created_at": "2016-01-07T09:53:58.235Z", + "deleted_at": null, + "id": 5, + "last_used": null, + "token": "6d056f63e50fe6f8c5f8f4aa10edb7", + "updated_at": "2016-01-07T09:53:58.235Z" +} +``` + +## Remove a project trigger + +Remove a trigger of a project + +``` +DELETE /projects/:id/triggers/:trigger_id +``` + +Parameters: + +- `id` (required) - The ID of a project +- `trigger_id` (required) - The ID of a trigger -- cgit v1.2.1 From 7ed3a5a240e4997b24d11b96e27126dfaa575abe Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 11:47:06 +0100 Subject: Revert "Store SQL/view timings in milliseconds" This reverts commit 7549102bb727daecc51da84af39956b32fc41537. Apparently I was wrong about ActiveSupport::Notifications::Event#duration returning the duration in seconds, instead it returns it in milliseconds already. --- lib/gitlab/metrics/subscribers/action_view.rb | 8 ++------ lib/gitlab/metrics/subscribers/active_record.rb | 6 +----- spec/lib/gitlab/metrics/subscribers/action_view_spec.rb | 4 ++-- spec/lib/gitlab/metrics/subscribers/active_record_spec.rb | 2 +- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 84d9e383625..7c0105d543a 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -19,7 +19,7 @@ module Gitlab values = values_for(event) tags = tags_for(event) - current_transaction.increment(:view_duration, duration(event)) + current_transaction.increment(:view_duration, event.duration) current_transaction.add_metric(SERIES, values, tags) end @@ -28,7 +28,7 @@ module Gitlab end def values_for(event) - { duration: duration(event) } + { duration: event.duration } end def tags_for(event) @@ -48,10 +48,6 @@ module Gitlab def current_transaction Transaction.current end - - def duration(event) - event.duration * 1000.0 - end end end end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 6fa73e7a3be..8008b3bc895 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -8,7 +8,7 @@ module Gitlab def sql(event) return unless current_transaction - current_transaction.increment(:sql_duration, duration(event)) + current_transaction.increment(:sql_duration, event.duration) end private @@ -16,10 +16,6 @@ module Gitlab def current_transaction Transaction.current end - - def duration(event) - event.duration * 1000.0 - end end end end diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb index 0a4cc5e929b..05e4fbbeb51 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -21,7 +21,7 @@ describe Gitlab::Metrics::Subscribers::ActionView do describe '#render_template' do it 'tracks rendering of a template' do - values = { duration: 2100 } + values = { duration: 2.1 } tags = { view: 'app/views/x.html.haml', file: 'app/views/x.html.haml', @@ -29,7 +29,7 @@ describe Gitlab::Metrics::Subscribers::ActionView do } expect(transaction).to receive(:increment). - with(:view_duration, 2100) + with(:view_duration, 2.1) expect(transaction).to receive(:add_metric). with(described_class::SERIES, values, tags) diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index ca86142a2f4..7bc070a4d09 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -26,7 +26,7 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do and_return(transaction) expect(transaction).to receive(:increment). - with(:sql_duration, 200) + with(:sql_duration, 0.2) subscriber.sql(event) end -- cgit v1.2.1 From b38eabdaf69c4da19f39c26b7626e9ce4b51158b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 12:10:35 +0100 Subject: Fix changelog --- CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index ed839b8fc82..de8d2c43424 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,7 +8,6 @@ v 8.4.0 (unreleased) - Add API support for looking up a user by username (Stan Hu) - Add project permissions to all project API endpoints (Stan Hu) - Link to milestone in "Milestone changed" system note - - Expose Git's version in the admin area - Only allow group/project members to mention `@all` - Expose Git's version in the admin area (Trey Davis) - Add "Frequently used" category to emoji picker -- cgit v1.2.1 From 539b41929bddf0e82d986f9e823208dd92707a21 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 12:26:05 +0100 Subject: Milestone reference is a Markdown link --- app/models/milestone.rb | 6 +++++- spec/fixtures/markdown.md.erb | 1 - spec/lib/banzai/filter/milestone_reference_filter_spec.rb | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index eaa2db2e247..550d14d4c39 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -71,8 +71,12 @@ class Milestone < ActiveRecord::Base end def to_reference(from_project = nil) + escaped_title = self.title.gsub("]", "\\]") + h = Gitlab::Application.routes.url_helpers - h.namespace_project_milestone_url(self.project.namespace, self.project, self) + url = h.namespace_project_milestone_url(self.project.namespace, self.project, self) + + "[#{escaped_title}](#{url})" end def reference_link_text(from_project = nil) diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 302b750aee5..0620096d689 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -219,7 +219,6 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - Milestone: <%= milestone.to_reference %> - Milestone in another project: <%= xmilestone.to_reference(project) %> - Ignored in code: `<%= milestone.to_reference %>` -- Ignored in links: [Link to <%= milestone.to_reference %>](#milestone-link) - Link to milestone by URL: [Milestone](<%= urls.namespace_project_milestone_url(milestone.project.namespace, milestone.project, milestone) %>) ### Task Lists diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index 86b71210100..ebf3d7489b5 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -18,7 +18,9 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do end context 'internal reference' do - let(:reference) { milestone.to_reference } + # Convert the Markdown link to only the URL, since these tests aren't run through the regular Markdown pipeline. + # Milestone reference behavior in the full Markdown pipeline is tested elsewhere. + let(:reference) { milestone.to_reference.gsub(/\[([^\]]+)\]\(([^)]+)\)/, '\2') } it 'links to a valid reference' do doc = reference_filter("See #{reference}") -- cgit v1.2.1 From 364b07cff0183956ea11962b94c70448767351d3 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 12:44:15 +0100 Subject: Removed UUIDs from metrics transactions While useful for finding out what methods/views belong to a transaction this might result in too much data being stored in InfluxDB. --- lib/gitlab/metrics/transaction.rb | 4 +--- spec/lib/gitlab/metrics/transaction_spec.rb | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 68b86de0655..306656d30fe 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -4,7 +4,7 @@ module Gitlab class Transaction THREAD_KEY = :_gitlab_metrics_transaction - attr_reader :uuid, :tags + attr_reader :tags def self.current Thread.current[THREAD_KEY] @@ -12,7 +12,6 @@ module Gitlab def initialize @metrics = [] - @uuid = SecureRandom.uuid @started_at = nil @finished_at = nil @@ -38,7 +37,6 @@ module Gitlab end def add_metric(series, values, tags = {}) - tags = tags.merge(transaction_id: @uuid) prefix = sidekiq? ? 'sidekiq_' : 'rails_' @metrics << Metric.new("#{prefix}#{series}", values, tags) diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index b9b94947afa..0c98b8f0127 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -30,9 +30,9 @@ describe Gitlab::Metrics::Transaction do end describe '#add_metric' do - it 'adds a metric tagged with the transaction UUID' do + it 'adds a metric to the transaction' do expect(Gitlab::Metrics::Metric).to receive(:new). - with('rails_foo', { number: 10 }, { transaction_id: transaction.uuid }) + with('rails_foo', { number: 10 }, {}) transaction.add_metric('foo', number: 10) end -- cgit v1.2.1 From 41b8a238ce4bd7f091d46fb9b89b7456fde17ddf Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 7 Jan 2016 12:56:18 +0100 Subject: Merge branch 'master' of github.com:gitlabhq/gitlabhq --- CHANGELOG | 1 + Gemfile | 2 +- Gemfile.lock | 2 +- app/assets/javascripts/application.js.coffee | 1 + app/assets/javascripts/dispatcher.js.coffee | 4 +- app/assets/javascripts/project_find_file.js.coffee | 125 +++++++++++++++++++++ .../javascripts/shortcuts_find_file.js.coffee | 19 ++++ app/assets/javascripts/shortcuts_tree.coffee | 4 + app/assets/stylesheets/pages/tree.scss | 8 ++ app/controllers/projects/find_file_controller.rb | 26 +++++ app/controllers/projects/refs_controller.rb | 2 + app/models/repository.rb | 5 + app/views/help/_shortcuts.html.haml | 26 +++++ app/views/layouts/nav/_project.html.haml | 3 +- app/views/projects/_find_file_link.html.haml | 3 + app/views/projects/find_file/show.html.haml | 27 +++++ app/views/projects/tree/show.html.haml | 8 +- config/routes.rb | 18 +++ doc/workflow/shortcuts.png | Bin 78736 -> 48782 bytes features/project/find_file.feature | 42 +++++++ features/steps/project/project_find_file.rb | 73 ++++++++++++ features/steps/shared/paths.rb | 4 + .../projects/find_file_controller_spec.rb | 66 +++++++++++ spec/routing/project_routing_spec.rb | 12 ++ vendor/assets/javascripts/fuzzaldrin-plus.min.js | 1 + 25 files changed, 473 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/project_find_file.js.coffee create mode 100644 app/assets/javascripts/shortcuts_find_file.js.coffee create mode 100644 app/assets/javascripts/shortcuts_tree.coffee create mode 100644 app/controllers/projects/find_file_controller.rb create mode 100644 app/views/projects/_find_file_link.html.haml create mode 100644 app/views/projects/find_file/show.html.haml create mode 100644 features/project/find_file.feature create mode 100644 features/steps/project/project_find_file.rb create mode 100644 spec/controllers/projects/find_file_controller_spec.rb create mode 100644 vendor/assets/javascripts/fuzzaldrin-plus.min.js diff --git a/CHANGELOG b/CHANGELOG index e7f1d2b67da..22fb91baaf0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -106,6 +106,7 @@ v 8.3.0 - Fix online editor should not remove newlines at the end of the file - Expose Git's version in the admin area - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) + - Add file finder feature in tree view v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) diff --git a/Gemfile b/Gemfile index 6145745b6f3..6b0bc241494 100644 --- a/Gemfile +++ b/Gemfile @@ -49,7 +49,7 @@ gem "browser", '~> 1.0.0' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '~> 7.2.20' +gem "gitlab_git", '~> 7.2.22' # LDAP Auth # GitLab fork with several improvements to original library. For full list of changes diff --git a/Gemfile.lock b/Gemfile.lock index 2b42f325503..a1168ed3b7a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -887,7 +887,7 @@ DEPENDENCIES github-markup (~> 1.3.1) gitlab-flowdock-git-hook (~> 1.0.1) gitlab_emoji (~> 0.2.0) - gitlab_git (~> 7.2.20) + gitlab_git (~> 7.2.22) gitlab_meta (= 7.0) gitlab_omniauth-ldap (~> 1.2.1) gollum-lib (~> 4.1.0) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index b9b095e004a..c095e5ae2b1 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -40,6 +40,7 @@ #= require shortcuts_network #= require jquery.nicescroll.min #= require_tree . +#= require fuzzaldrin-plus.min window.slugify = (text) -> text.replace(/[^-a-zA-Z0-9]+/g, '_').toLowerCase() diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 69e061ce6e9..58d6b9d4060 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -87,7 +87,9 @@ class Dispatcher new GroupAvatar() when 'projects:tree:show' new TreeView() - shortcut_handler = new ShortcutsNavigation() + shortcut_handler = new ShortcutsTree() + when 'projects:find_file:show' + shortcut_handler = true when 'projects:blob:show' new LineHighlighter() shortcut_handler = new ShortcutsNavigation() diff --git a/app/assets/javascripts/project_find_file.js.coffee b/app/assets/javascripts/project_find_file.js.coffee new file mode 100644 index 00000000000..0dd32352c34 --- /dev/null +++ b/app/assets/javascripts/project_find_file.js.coffee @@ -0,0 +1,125 @@ +class @ProjectFindFile + constructor: (@element, @options)-> + @filePaths = {} + @inputElement = @element.find(".file-finder-input") + + # init event + @initEvent() + + # focus text input box + @inputElement.focus() + + # load file list + @load(@options.url) + + # init event + initEvent: -> + @inputElement.off "keyup" + @inputElement.on "keyup", (event) => + target = $(event.target) + value = target.val() + oldValue = target.data("oldValue") ? "" + + if value != oldValue + target.data("oldValue", value) + @findFile() + @element.find("tr.tree-item").eq(0).addClass("selected").focus() + + @element.find(".tree-content-holder .tree-table").on "click", (event) -> + if (event.target.nodeName != "A") + path = @element.find(".tree-item-file-name a", this).attr("href") + location.href = path if path + + # find file + findFile: -> + searchText = @inputElement.val() + result = if searchText.length > 0 then fuzzaldrinPlus.filter(@filePaths, searchText) else @filePaths + @renderList result, searchText + + # files pathes load + load: (url) -> + $.ajax + url: url + method: "get" + dataType: "json" + success: (data) => + @element.find(".loading").hide() + @filePaths = data + @findFile() + @element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus() + + # render result + renderList: (filePaths, searchText) -> + @element.find(".tree-table > tbody").empty() + + for filePath, i in filePaths + break if i == 20 + + if searchText + matches = fuzzaldrinPlus.match(filePath, searchText) + + blobItemUrl = "#{@options.blobUrlTemplate}/#{filePath}" + + html = @makeHtml filePath, matches, blobItemUrl + @element.find(".tree-table > tbody").append(html) + + # highlight text(awefwbwgtc -> awefwbwgtc ) + highlighter = (element, text, matches) -> + lastIndex = 0 + highlightText = "" + matchedChars = [] + + for matchIndex in matches + unmatched = text.substring(lastIndex, matchIndex) + + if unmatched + element.append(matchedChars.join("").bold()) if matchedChars.length + matchedChars = [] + element.append(document.createTextNode(unmatched)) + + matchedChars.push(text[matchIndex]) + lastIndex = matchIndex + 1 + + element.append(matchedChars.join("").bold()) if matchedChars.length + element.append(document.createTextNode(text.substring(lastIndex))) + + # make tbody row html + makeHtml: (filePath, matches, blobItemUrl) -> + $tr = $("") + if matches + $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl)) + else + $tr.find("a").attr("href", blobItemUrl).text(filePath) + + return $tr + + selectRow: (type) -> + rows = @element.find(".files-slider tr.tree-item") + selectedRow = @element.find(".files-slider tr.tree-item.selected") + + if rows && rows.length > 0 + if selectedRow && selectedRow.length > 0 + if type == "UP" + next = selectedRow.prev() + else if type == "DOWN" + next = selectedRow.next() + + if next.length > 0 + selectedRow.removeClass "selected" + selectedRow = next + else + selectedRow = rows.eq(0) + selectedRow.addClass("selected").focus() + + selectRowUp: => + @selectRow "UP" + + selectRowDown: => + @selectRow "DOWN" + + goToTree: => + location.href = @options.treeUrl + + goToBlob: => + path = @element.find(".tree-item.selected .tree-item-file-name a").attr("href") + location.href = path if path diff --git a/app/assets/javascripts/shortcuts_find_file.js.coffee b/app/assets/javascripts/shortcuts_find_file.js.coffee new file mode 100644 index 00000000000..311e80bae19 --- /dev/null +++ b/app/assets/javascripts/shortcuts_find_file.js.coffee @@ -0,0 +1,19 @@ +#= require shortcuts_navigation + +class @ShortcutsFindFile extends ShortcutsNavigation + constructor: (@projectFindFile) -> + super() + _oldStopCallback = Mousetrap.stopCallback + # override to fire shortcuts action when focus in textbox + Mousetrap.stopCallback = (event, element, combo) => + if element == @projectFindFile.inputElement[0] and (combo == 'up' or combo == 'down' or combo == 'esc' or combo == 'enter') + # when press up/down key in textbox, cusor prevent to move to home/end + event.preventDefault() + return false + + return _oldStopCallback(event, element, combo) + + Mousetrap.bind('up', @projectFindFile.selectRowUp) + Mousetrap.bind('down', @projectFindFile.selectRowDown) + Mousetrap.bind('esc', @projectFindFile.goToTree) + Mousetrap.bind('enter', @projectFindFile.goToBlob) diff --git a/app/assets/javascripts/shortcuts_tree.coffee b/app/assets/javascripts/shortcuts_tree.coffee new file mode 100644 index 00000000000..ba0839c9fc0 --- /dev/null +++ b/app/assets/javascripts/shortcuts_tree.coffee @@ -0,0 +1,4 @@ +class @ShortcutsTree extends ShortcutsNavigation + constructor: -> + super() + Mousetrap.bind('t', -> ShortcutsTree.findAndFollowLink('.shortcuts-find-file')) diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index d4ab6967ccd..97505edeabf 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -1,5 +1,13 @@ .tree-holder { + .file-finder { + width: 50%; + .file-finder-input { + width: 95%; + display: inline-block; + } + } + .tree-table { margin-bottom: 0; diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb new file mode 100644 index 00000000000..54a0c447aee --- /dev/null +++ b/app/controllers/projects/find_file_controller.rb @@ -0,0 +1,26 @@ +# Controller for viewing a repository's file structure +class Projects::FindFileController < Projects::ApplicationController + include ExtractsPath + include ActionView::Helpers::SanitizeHelper + include TreeHelper + + before_action :require_non_empty_project + before_action :assign_ref_vars + before_action :authorize_download_code! + + def show + return render_404 unless @repository.commit(@ref) + + respond_to do |format| + format.html + end + end + + def list + file_paths = @repo.ls_files(@ref) + + respond_to do |format| + format.json { render json: file_paths } + end + end +end diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index c4e18c17077..a8f091819ca 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -20,6 +20,8 @@ class Projects::RefsController < Projects::ApplicationController namespace_project_network_path(@project.namespace, @project, @id, @options) when "graphs" namespace_project_graph_path(@project.namespace, @project, @id) + when "find_file" + namespace_project_find_file_path(@project.namespace, @project, @id) when "graphs_commits" commits_namespace_project_graph_path(@project.namespace, @project, @id) else diff --git a/app/models/repository.rb b/app/models/repository.rb index 6ecd2d2f27e..9deb08d93b8 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -681,6 +681,11 @@ class Repository end end + def ls_files(ref) + actual_ref = ref || root_ref + raw_repository.ls_files(actual_ref) + end + private def cache diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index e8e331dd109..9ee6f07b26b 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -40,6 +40,32 @@ %td.shortcut .key enter %td Open Selection + %tr + %td.shortcut + .key t + %td Go to finding file + %tbody + %tr + %th + %th Finding Project File + %tr + %td.shortcut + .key + %i.fa.fa-arrow-up + %td Move selection up + %tr + %td.shortcut + .key + %i.fa.fa-arrow-down + %td Move selection down + %tr + %td.shortcut + .key enter + %td Open Selection + %tr + %td.shortcut + .key esc + %td Go back .col-lg-4 %table.shortcut-mappings diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index d3eaf0f3209..270ccfd387f 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -25,7 +25,7 @@ %span Activity - if project_nav_tab? :files - = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do + = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do = link_to project_files_path(@project), title: 'Files', class: 'shortcuts-tree' do = icon('files-o fw') %span @@ -117,4 +117,3 @@ %li.hidden = link_to namespace_project_network_path(@project.namespace, @project, current_ref), title: 'Network', class: 'shortcuts-network' do Network - diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml new file mode 100644 index 00000000000..08e2fc48be7 --- /dev/null +++ b/app/views/projects/_find_file_link.html.haml @@ -0,0 +1,3 @@ += link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do + = icon('search') + %span Find File diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml new file mode 100644 index 00000000000..2930209fb56 --- /dev/null +++ b/app/views/projects/find_file/show.html.haml @@ -0,0 +1,27 @@ +- page_title "Find File", @ref +- header_title project_title(@project, "Files", project_files_path(@project)) + +.file-finder-holder.tree-holder.clearfix + .gray-content-block.top-block + .tree-ref-holder + = render 'shared/ref_switcher', destination: 'find_file', path: @path + %ul.breadcrumb.repo-breadcrumb + %li + = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do + = @project.path + %li.file-finder + %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path'} + + %div.tree-content-holder + .table-holder + %table.table.files-slider{class: "table_#{@hex_path} tree-table table-striped" } + %tbody + = spinner nil, true + +:coffeescript + projectFindFile = new ProjectFindFile($(".file-finder-holder"), { + url: "#{escape_javascript(namespace_project_files_path(@project.namespace, @project, @ref, @options.merge(format: :json)))}" + treeUrl: "#{escape_javascript(namespace_project_tree_path(@project.namespace, @project, @ref))}" + blobUrlTemplate: "#{escape_javascript(namespace_project_blob_path(@project.namespace, @project, @id || @commit.id))}" + }) + new ShortcutsFindFile(projectFindFile) diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index ec14bd7f65a..c57570afa09 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -3,12 +3,12 @@ = content_for :meta_tags do - if current_user = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits") - = render 'projects/last_push' -- if can? current_user, :download_code, @project - .tree-download-holder - = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'btn-group pull-right hidden-xs hidden-sm', split_button: true +.pull-right + = render 'projects/find_file_link' + - if can? current_user, :download_code, @project + = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped', split_button: true #tree-holder.tree-holder.clearfix .gray-content-block.top-block diff --git a/config/routes.rb b/config/routes.rb index 3e7d9f78710..5b69d06eb76 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -440,6 +440,24 @@ Rails.application.routes.draw do ) end + scope do + get( + '/find_file/*id', + to: 'find_file#show', + constraints: { id: /.+/, format: /html/ }, + as: :find_file + ) + end + + scope do + get( + '/files/*id', + to: 'find_file#list', + constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ }, + as: :files + ) + end + scope do post( '/create_dir/*id', diff --git a/doc/workflow/shortcuts.png b/doc/workflow/shortcuts.png index 68756ed1f98..e5914aa8e67 100644 Binary files a/doc/workflow/shortcuts.png and b/doc/workflow/shortcuts.png differ diff --git a/features/project/find_file.feature b/features/project/find_file.feature new file mode 100644 index 00000000000..ae8fa245923 --- /dev/null +++ b/features/project/find_file.feature @@ -0,0 +1,42 @@ +@dashboard +Feature: Project Find File + Background: + Given I sign in as a user + And I own a project + And I visit my project's files page + + @javascript + Scenario: Navigate to find file by shortcut + Given I press "t" + Then I should see "find file" page + + Scenario: Navigate to find file + Given I click Find File button + Then I should see "find file" page + + @javascript + Scenario: I search file + Given I visit project find file page + And I fill in file find with "change" + Then I should not see ".gitignore" in files + And I should not see ".gitmodules" in files + And I should see "CHANGELOG" in files + And I should not see "VERSION" in files + + @javascript + Scenario: I search file that not exist + Given I visit project find file page + And I fill in file find with "asdfghjklqwertyuizxcvbnm" + Then I should not see ".gitignore" in files + And I should not see ".gitmodules" in files + And I should not see "CHANGELOG" in files + And I should not see "VERSION" in files + + @javascript + Scenario: I search file that partially matches + Given I visit project find file page + And I fill in file find with "git" + Then I should see ".gitignore" in files + And I should see ".gitmodules" in files + And I should not see "CHANGELOG" in files + And I should not see "VERSION" in files diff --git a/features/steps/project/project_find_file.rb b/features/steps/project/project_find_file.rb new file mode 100644 index 00000000000..8c1d09d6cc6 --- /dev/null +++ b/features/steps/project/project_find_file.rb @@ -0,0 +1,73 @@ +class Spinach::Features::ProjectFindFile < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + include SharedProjectTab + + step 'I press "t"' do + find('body').native.send_key('t') + end + + step 'I click Find File button' do + click_link 'Find File' + end + + step 'I should see "find file" page' do + ensure_active_main_tab('Files') + expect(page).to have_selector('.file-finder-holder', count: 1) + end + + step 'I fill in Find by path with "git"' do + ensure_active_main_tab('Files') + expect(page).to have_selector('.file-finder-holder', count: 1) + end + + step 'I fill in file find with "git"' do + find_file "git" + end + + step 'I fill in file find with "change"' do + find_file "change" + end + + step 'I fill in file find with "asdfghjklqwertyuizxcvbnm"' do + find_file "asdfghjklqwertyuizxcvbnm" + end + + step 'I should see "VERSION" in files' do + expect(page).to have_content("VERSION") + end + + step 'I should not see "VERSION" in files' do + expect(page).not_to have_content("VERSION") + end + + step 'I should see "CHANGELOG" in files' do + expect(page).to have_content("CHANGELOG") + end + + step 'I should not see "CHANGELOG" in files' do + expect(page).not_to have_content("CHANGELOG") + end + + step 'I should see ".gitmodules" in files' do + expect(page).to have_content(".gitmodules") + end + + step 'I should not see ".gitmodules" in files' do + expect(page).not_to have_content(".gitmodules") + end + + step 'I should see ".gitignore" in files' do + expect(page).to have_content(".gitignore") + end + + step 'I should not see ".gitignore" in files' do + expect(page).not_to have_content(".gitignore") + end + + + def find_file(text) + fill_in 'file_find', with: text + end +end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index b33bd332655..4264c9c6f1a 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -259,6 +259,10 @@ module SharedPaths visit namespace_project_deploy_keys_path(@project.namespace, @project) end + step 'I visit project find file page' do + visit namespace_project_find_file_path(@project.namespace, @project, root_ref) + end + # ---------------------------------------- # "Shop" Project # ---------------------------------------- diff --git a/spec/controllers/projects/find_file_controller_spec.rb b/spec/controllers/projects/find_file_controller_spec.rb new file mode 100644 index 00000000000..038dfeb8466 --- /dev/null +++ b/spec/controllers/projects/find_file_controller_spec.rb @@ -0,0 +1,66 @@ +require 'spec_helper' + +describe Projects::FindFileController do + let(:project) { create(:project) } + let(:user) { create(:user) } + + before do + sign_in(user) + + project.team << [user, :master] + controller.instance_variable_set(:@project, project) + end + + describe "GET #show" do + # Make sure any errors accessing the tree in our views bubble up to this spec + render_views + + before do + get(:show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: id) + end + + context "valid branch" do + let(:id) { 'master' } + it { is_expected.to respond_with(:success) } + end + + context "invalid branch" do + let(:id) { 'invalid-branch' } + it { is_expected.to respond_with(:not_found) } + end + end + + describe "GET #list" do + def go(format: 'json') + get :list, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: id, + format: format + end + + context "valid branch" do + let(:id) { 'master' } + it 'returns an array of file path list' do + go + + json = JSON.parse(response.body) + is_expected.to respond_with(:success) + expect(json).not_to eq(nil) + expect(json.length).to be >= 0 + end + end + + context "invalid branch" do + let(:id) { 'invalid-branch' } + + it 'responds with status 404' do + go + is_expected.to respond_with(:not_found) + end + end + end +end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 82f62a8709c..2a70c190337 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -434,6 +434,18 @@ describe Projects::TreeController, 'routing' do end end +# project_find_file GET /:namespace_id/:project_id/find_file/*id(.:format) projects/find_file#show {:id=>/.+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?/html/} +# project_files GET /:namespace_id/:project_id/files/*id(.:format) projects/find_file#list {:id=>/(?:[^.]|\.(?!json$))+/, :namespace_id=>/[a-zA-Z.0-9_\-]+/, :project_id=>/[a-zA-Z.0-9_\-]+(?/json/} +describe Projects::FindFileController, 'routing' do + it 'to #show' do + expect(get('/gitlab/gitlabhq/find_file/master')).to route_to('projects/find_file#show', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master') + end + + it 'to #list' do + expect(get('/gitlab/gitlabhq/files/master.json')).to route_to('projects/find_file#list', namespace_id: 'gitlab', project_id: 'gitlabhq', id: 'master', format: 'json') + end +end + describe Projects::BlobController, 'routing' do it 'to #edit' do expect(get('/gitlab/gitlabhq/edit/master/app/models/project.rb')).to( diff --git a/vendor/assets/javascripts/fuzzaldrin-plus.min.js b/vendor/assets/javascripts/fuzzaldrin-plus.min.js new file mode 100644 index 00000000000..3f25c2d8373 --- /dev/null +++ b/vendor/assets/javascripts/fuzzaldrin-plus.min.js @@ -0,0 +1 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0?maxInners:candidates.length;bAllowErrors=!!allowErrors;bKey=key!=null;prepQuery=scorer.prepQuery(query);if(!legacy){for(i=0,len=candidates.length;i0){scoredCandidates.push({candidate:candidate,score:score});if(!--spotLeft){break}}}}else{queryHasSlashes=prepQuery.depth>0;coreQuery=prepQuery.core;for(j=0,len1=candidates.length;j0){scoredCandidates.push({candidate:candidate,score:score})}}}scoredCandidates.sort(sortCandidates);candidates=scoredCandidates.map(pluckCandidates);if(maxResults!=null){candidates=candidates.slice(0,maxResults)}return candidates}}).call(this)},{"./legacy":4,"./scorer":6,"path":7}],2:[function(require,module,exports){(function(){var PathSeparator,filter,legacy_scorer,matcher,prepQueryCache,scorer;scorer=require('./scorer');legacy_scorer=require('./legacy');filter=require('./filter');matcher=require('./matcher');PathSeparator=require('path').sep;prepQueryCache=null;module.exports={filter:function(candidates,query,options){if(!((query!=null?query.length:void 0)&&(candidates!=null?candidates.length:void 0))){return[]}return filter(candidates,query,options)},prepQuery:function(query){return scorer.prepQuery(query)},score:function(string,query,prepQuery,arg){var allowErrors,coreQuery,legacy,queryHasSlashes,ref,score;ref=arg!=null?arg:{},allowErrors=ref.allowErrors,legacy=ref.legacy;if(!((string!=null?string.length:void 0)&&(query!=null?query.length:void 0))){return 0}if(prepQuery==null){prepQuery=prepQueryCache&&prepQueryCache.query===query?prepQueryCache:(prepQueryCache=scorer.prepQuery(query))}if(!legacy){score=scorer.score(string,query,prepQuery,!!allowErrors)}else{queryHasSlashes=prepQuery.depth>0;coreQuery=prepQuery.core;score=legacy_scorer.score(string,coreQuery,queryHasSlashes);if(!queryHasSlashes){score=legacy_scorer.basenameScore(string,coreQuery,score)}}return score},match:function(string,query,prepQuery,arg){var allowErrors,baseMatches,i,matches,query_lw,ref,results,string_lw;allowErrors=(arg!=null?arg:{}).allowErrors;if(!string){return[]}if(!query){return[]}if(string===query){return(function(){results=[];for(var i=0,ref=string.length;0<=ref?iref;0<=ref?i++:i--){results.push(i)}return results}).apply(this)}if(prepQuery==null){prepQuery=prepQueryCache&&prepQueryCache.query===query?prepQueryCache:(prepQueryCache=scorer.prepQuery(query))}if(!(allowErrors||scorer.isMatch(string,prepQuery.core_lw,prepQuery.core_up))){return[]}string_lw=string.toLowerCase();query_lw=prepQuery.query_lw;matches=matcher.match(string,string_lw,prepQuery);if(matches.length===0){return matches}if(string.indexOf(PathSeparator)>-1){baseMatches=matcher.basenameMatch(string,string_lw,prepQuery);matches=matcher.mergeMatches(matches,baseMatches)}return matches}}}).call(this)},{"./filter":1,"./legacy":4,"./matcher":5,"./scorer":6,"path":7}],3:[function(require,module,exports){fuzzaldrinPlus=require('./fuzzaldrin')},{"./fuzzaldrin":2}],4:[function(require,module,exports){(function(){var PathSeparator,queryIsLastPathSegment;PathSeparator=require('path').sep;exports.basenameScore=function(string,query,score){var base,depth,index,lastCharacter,segmentCount,slashCount;index=string.length-1;while(string[index]===PathSeparator){index--}slashCount=0;lastCharacter=index;base=null;while(index>=0){if(string[index]===PathSeparator){slashCount++;if(base==null){base=string.substring(index+1,lastCharacter+1)}}else if(index===0){if(lastCharacterref;stringOffset<=ref?i++:i--){results.push(i)}return results}).apply(this)}queryLength=query.length;stringLength=string.length;indexInQuery=0;indexInString=0;matches=[];while(indexInQuery0){basePos=subject.lastIndexOf(PathSeparator,basePos-1);if(basePos===-1){return[]}}basePos++;end++;return exports.match(subject.slice(basePos,end),subject_lw.slice(basePos,end),prepQuery,basePos)};exports.mergeMatches=function(a,b){var ai,bj,i,j,m,n,out;m=a.length;n=b.length;if(n===0){return a.slice()}if(m===0){return b.slice()}i=-1;j=0;bj=b[j];out=[];while(++i0?csc_diag:scorer.scoreConsecutives(subject,subject_lw,query,query_lw,i,j,start);align=score_diag+scorer.scoreCharacter(i,j,start,acro_score,csc_score)}score_up=score_row[j];csc_diag=csc_row[j];if(score>score_up){move=LEFT}else{score=score_up;move=UP}if(align>score){score=align;move=DIAGONAL}else{csc_score=0}score_row[j]=score;csc_row[j]=csc_score;trace[++pos]=score>0?move:STOP}}i=m-1;j=n-1;pos=i*n+j;backtrack=true;matches=[];while(backtrack&&i>=0&&j>=0){switch(trace[pos]){case UP:i--;pos-=n;break;case LEFT:j--;pos--;break;case DIAGONAL:matches.push(i+offset);j--;i--;pos-=n+1;break;default:backtrack=false}}matches.reverse();return matches}}).call(this)},{"./scorer":6,"path":7}],6:[function(require,module,exports){(function(){var AcronymResult,PathSeparator,Query,basenameScore,coreChars,countDir,doScore,emptyAcronymResult,file_coeff,isMatch,isSeparator,isWordEnd,isWordStart,miss_coeff,opt_char_re,pos_bonus,scoreAcronyms,scoreCharacter,scoreConsecutives,scoreExact,scoreExactMatch,scorePattern,scorePosition,scoreSize,tau_depth,tau_size,truncatedUpperCase,wm;PathSeparator=require('path').sep;wm=150;pos_bonus=20;tau_depth=13;tau_size=85;file_coeff=1.2;miss_coeff=0.75;opt_char_re=/[ _\-:\/\\]/g;exports.coreChars=coreChars=function(query){return query.replace(opt_char_re,'')};exports.score=function(string,query,prepQuery,allowErrors){var score,string_lw;if(prepQuery==null){prepQuery=new Query(query)}if(allowErrors==null){allowErrors=false}if(!(allowErrors||isMatch(string,prepQuery.core_lw,prepQuery.core_up))){return 0}string_lw=string.toLowerCase();score=doScore(string,string_lw,prepQuery);return Math.ceil(basenameScore(string,string_lw,prepQuery,score))};Query=(function(){function Query(query){if(!(query!=null?query.length:void 0)){return null}this.query=query;this.query_lw=query.toLowerCase();this.core=coreChars(query);this.core_lw=this.core.toLowerCase();this.core_up=truncatedUpperCase(this.core);this.depth=countDir(query,query.length)}return Query})();exports.prepQuery=function(query){return new Query(query)};exports.isMatch=isMatch=function(subject,query_lw,query_up){var i,j,m,n,qj_lw,qj_up,si;m=subject.length;n=query_lw.length;if(!m||!n||n>m){return false}i=-1;j=-1;while(++j-1){return scoreExactMatch(subject,subject_lw,query,query_lw,pos,n,m)}score_row=new Array(n);csc_row=new Array(n);sz=scoreSize(n,m);miss_budget=Math.ceil(miss_coeff*n)+5;miss_left=miss_budget;j=-1;while(++j-1){i--}mm=subject_lw.lastIndexOf(query_lw[n-1],m);if(mm>i){m=mm+1}while(++iscore){score=score_up}csc_score=0;if(query_lw[j]===si_lw){start=isWordStart(i,subject,subject_lw);csc_score=csc_diag>0?csc_diag:scoreConsecutives(subject,subject_lw,query,query_lw,i,j,start);align=score_diag+scoreCharacter(i,j,start,acro_score,csc_score);if(align>score){score=align;miss_left=miss_budget}else{if(record_miss&&--miss_left<=0){return score_row[n-1]*sz}record_miss=false}}score_diag=score_up;csc_diag=csc_row[j];csc_row[j]=csc_score;score_row[j]=score}}return score*sz};exports.isWordStart=isWordStart=function(pos,subject,subject_lw){var curr_s,prev_s;if(pos===0){return true}curr_s=subject[pos];prev_s=subject[pos-1];return isSeparator(curr_s)||isSeparator(prev_s)||(curr_s!==subject_lw[pos]&&prev_s===subject_lw[pos-1])};exports.isWordEnd=isWordEnd=function(pos,subject,subject_lw,len){var curr_s,next_s;if(pos===len-1){return true}curr_s=subject[pos];next_s=subject[pos+1];return isSeparator(curr_s)||isSeparator(next_s)||(curr_s===subject_lw[pos]&&next_s!==subject_lw[pos+1])};isSeparator=function(c){return c===' '||c==='.'||c==='-'||c==='_'||c==='/'||c==='\\'};scorePosition=function(pos){var sc;if(poscsc_score?acro_score:csc_score)+10)}return posBonus+wm*csc_score};exports.scoreConsecutives=scoreConsecutives=function(subject,subject_lw,query,query_lw,i,j,start){var k,m,mi,n,nj,sameCase,startPos,sz;m=subject.length;n=query.length;mi=m-i;nj=n-j;k=mi-1){start=isWordStart(pos2,subject,subject_lw);if(start){pos=pos2}}}i=-1;sameCase=0;while(++i1&&n>1)){return emptyAcronymResult}count=0;pos=0;sameCase=0;i=-1;j=-1;while(++j0){basePos=subject.lastIndexOf(PathSeparator,basePos-1);if(basePos===-1){return fullPathScore}}basePos++;end++;basePathScore=doScore(subject.slice(basePos,end),subject_lw.slice(basePos,end),prepQuery);alpha=0.5*tau_depth/(tau_depth+countDir(subject,end+1));return alpha*basePathScore+(1-alpha)*fullPathScore*scoreSize(0,file_coeff*(end-basePos))};exports.countDir=countDir=function(path,end){var count,i;if(end<1){return 0}count=0;i=-1;while(++i=0;i--){var last=parts[i];if(last==='.'){parts.splice(i,1)}else if(last==='..'){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up--;up){parts.unshift('..')}}return parts}var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;var splitPath=function(filename){return splitPathRe.exec(filename).slice(1)};exports.resolve=function(){var resolvedPath='',resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=(i>=0)?arguments[i]:process.cwd();if(typeof path!=='string'){throw new TypeError('Arguments to path.resolve must be strings');}else if(!path){continue}resolvedPath=path+'/'+resolvedPath;resolvedAbsolute=path.charAt(0)==='/'}resolvedPath=normalizeArray(filter(resolvedPath.split('/'),function(p){return!!p}),!resolvedAbsolute).join('/');return((resolvedAbsolute?'/':'')+resolvedPath)||'.'};exports.normalize=function(path){var isAbsolute=exports.isAbsolute(path),trailingSlash=substr(path,-1)==='/';path=normalizeArray(filter(path.split('/'),function(p){return!!p}),!isAbsolute).join('/');if(!path&&!isAbsolute){path='.'}if(path&&trailingSlash){path+='/'}return(isAbsolute?'/':'')+path};exports.isAbsolute=function(path){return path.charAt(0)==='/'};exports.join=function(){var paths=Array.prototype.slice.call(arguments,0);return exports.normalize(filter(paths,function(p,index){if(typeof p!=='string'){throw new TypeError('Arguments to path.join must be strings');}return p}).join('/'))};exports.relative=function(from,to){from=exports.resolve(from).substr(1);to=exports.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=='')break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split('/'));var toParts=trim(to.split('/'));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i1){for(var i=1;i Date: Thu, 7 Jan 2016 13:04:09 +0100 Subject: Credit koreamic for creating Find File --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 22fb91baaf0..32308becba0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ v 8.4.0 (unreleased) - Update version check images to use SVG - Validate README format before displaying - Enable Microsoft Azure OAuth2 support (Janis Meybohm) + - Add file finder feature in tree view (koreamic) v 8.3.3 (unreleased) - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running @@ -106,7 +107,6 @@ v 8.3.0 - Fix online editor should not remove newlines at the end of the file - Expose Git's version in the admin area - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) - - Add file finder feature in tree view v 8.2.3 - Fix application settings cache not expiring after changes (Stan Hu) -- cgit v1.2.1 From 7b10cb6f0f5758c17dd950587982ff400d7aa971 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 13:05:00 +0100 Subject: Store request methods/URIs as values Since filtering by these values is very rare (they're mostly just displayed as-is) we don't need to waste any index space by saving them as tags. By storing them as values we also greatly reduce the number of series in InfluxDB. --- lib/gitlab/metrics/rack_middleware.rb | 4 ++-- lib/gitlab/metrics/transaction.rb | 6 +++++- spec/lib/gitlab/metrics/rack_middleware_spec.rb | 6 +++--- spec/lib/gitlab/metrics/transaction_spec.rb | 11 +++++++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb index 5c0587c4c51..e7a2f26d48b 100644 --- a/lib/gitlab/metrics/rack_middleware.rb +++ b/lib/gitlab/metrics/rack_middleware.rb @@ -32,8 +32,8 @@ module Gitlab def transaction_from_env(env) trans = Transaction.new - trans.add_tag(:request_method, env['REQUEST_METHOD']) - trans.add_tag(:request_uri, env['REQUEST_URI']) + trans.set(:request_uri, env['REQUEST_URI']) + trans.set(:request_method, env['REQUEST_METHOD']) trans end diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 306656d30fe..73131cc6ef2 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -4,7 +4,7 @@ module Gitlab class Transaction THREAD_KEY = :_gitlab_metrics_transaction - attr_reader :tags + attr_reader :tags, :values def self.current Thread.current[THREAD_KEY] @@ -46,6 +46,10 @@ module Gitlab @values[name] += value end + def set(name, value) + @values[name] = value + end + def add_tag(key, value) @tags[key] = value end diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb index a143fe4cfcd..4e6dfc73df2 100644 --- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb +++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb @@ -40,9 +40,9 @@ describe Gitlab::Metrics::RackMiddleware do expect(transaction).to be_an_instance_of(Gitlab::Metrics::Transaction) end - it 'tags the transaction with the request method and URI' do - expect(transaction.tags[:request_method]).to eq('GET') - expect(transaction.tags[:request_uri]).to eq('/foo') + it 'stores the request method and URI in the transaction as values' do + expect(transaction.values[:request_method]).to eq('GET') + expect(transaction.values[:request_uri]).to eq('/foo') end end diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 0c98b8f0127..3a27f897735 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -50,6 +50,17 @@ describe Gitlab::Metrics::Transaction do end end + describe '#set' do + it 'sets a value' do + transaction.set(:number, 10) + + expect(transaction).to receive(:add_metric). + with('transactions', { duration: 0.0, number: 10 }, {}) + + transaction.track_self + end + end + describe '#add_tag' do it 'adds a tag' do transaction.add_tag(:foo, 'bar') -- cgit v1.2.1 From 1886d727f738895bb552151d59d4024f405522e2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 13:37:14 +0100 Subject: Add API project upload endpoint --- app/assets/javascripts/dropzone_input.js.coffee | 14 ++----------- app/services/projects/download_service.rb | 7 ++++++- app/services/projects/upload_service.rb | 7 ++++++- doc/api/projects.md | 28 +++++++++++++++++++++++++ lib/api/projects.rb | 12 ++++++++++- lib/gitlab/email/receiver.rb | 7 ++----- lib/gitlab/fogbugz_import/importer.rb | 4 +--- spec/requests/api/projects_spec.rb | 14 +++++++++++++ 8 files changed, 70 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee index 30a35a04339..c714c0fa939 100644 --- a/app/assets/javascripts/dropzone_input.js.coffee +++ b/app/assets/javascripts/dropzone_input.js.coffee @@ -66,7 +66,7 @@ class @DropzoneInput success: (header, response) -> child = $(dropzone[0]).children("textarea") - $(child).val $(child).val() + formatLink(response.link) + "\n" + $(child).val $(child).val() + response.link.markdown + "\n" return error: (temp, errorMessage) -> @@ -99,11 +99,6 @@ class @DropzoneInput child = $(dropzone[0]).children("textarea") - formatLink = (link) -> - text = "[#{link.alt}](#{link.url})" - text = "!#{text}" if link.is_image - text - handlePaste = (event) -> pasteEvent = event.originalEvent if pasteEvent.clipboardData and pasteEvent.clipboardData.items @@ -162,7 +157,7 @@ class @DropzoneInput closeAlertMessage() success: (e, textStatus, response) -> - insertToTextArea(filename, formatLink(response.responseJSON.link)) + insertToTextArea(filename, response.responseJSON.link.markdown) error: (response) -> showError(response.responseJSON.message) @@ -202,8 +197,3 @@ class @DropzoneInput e.preventDefault() $(@).closest('.gfm-form').find('.div-dropzone').click() return - - formatLink: (link) -> - text = "[#{link.alt}](#{link.url})" - text = "!#{text}" if link.is_image - text diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb index 99f22293d0d..b846a59ed94 100644 --- a/app/services/projects/download_service.rb +++ b/app/services/projects/download_service.rb @@ -18,10 +18,15 @@ module Projects filename = uploader.image? ? uploader.file.basename : uploader.file.filename + escaped_filename = filename.gsub("]", "\\]") + markdown = "[#{escaped_filename}](#{uploader.secure_url})" + markdown.prepend("!") if uploader.image? + { 'alt' => filename, 'url' => uploader.secure_url, - 'is_image' => uploader.image? + 'is_image' => uploader.image?, + 'markdown' => markdown } end diff --git a/app/services/projects/upload_service.rb b/app/services/projects/upload_service.rb index 279550d6f4a..36ccf1cda12 100644 --- a/app/services/projects/upload_service.rb +++ b/app/services/projects/upload_service.rb @@ -12,10 +12,15 @@ module Projects filename = uploader.image? ? uploader.file.basename : uploader.file.filename + escaped_filename = filename.gsub("]", "\\]") + markdown = "[#{escaped_filename}](#{uploader.secure_url})" + markdown.prepend("!") if uploader.image? + { alt: filename, url: uploader.secure_url, - is_image: uploader.image? + is_image: uploader.image?, + markdown: markdown } end diff --git a/doc/api/projects.md b/doc/api/projects.md index 0ca81ffd49e..37d74216c1b 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -482,6 +482,34 @@ Parameters: - `id` (required) - The ID of a project +## Uploads + +### Upload a file + +Uploads a file to the specified project to be used in an issue or merge request description, or a comment. + +``` +POST /projects/:id/uploads +``` + +Parameters: + +- `id` (required) - The ID of the project +- `file` (required) - The file to be uploaded + +```json +{ + "alt": "dk", + "url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png", + "is_image": true, + "markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)" +} +``` + +**Note**: The returned `url` is relative to the project path. +In Markdown contexts, the link is automatically expanded when the format in `markdown` is used. + + ## Team members ### List project team members diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 0781236cf6d..8b1390e3289 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -269,7 +269,7 @@ module API # Remove a forked_from relationship # # Parameters: - # id: (required) - The ID of the project being marked as a fork + # id: (required) - The ID of the project being marked as a fork # Example Request: # DELETE /projects/:id/fork delete ":id/fork" do @@ -278,6 +278,16 @@ module API user_project.forked_project_link.destroy end end + + # Upload a file + # + # Parameters: + # id: (required) - The ID of the project + # file: (required) - The file to be uploaded + post ":id/uploads" do + ::Projects::UploadService.new(user_project, params[:file]).execute + end + # search for projects current_user has access to # # Parameters: diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb index 2b252b32887..2ca21af5bc8 100644 --- a/lib/gitlab/email/receiver.rb +++ b/lib/gitlab/email/receiver.rb @@ -74,7 +74,7 @@ module Gitlab def sent_notification return nil unless reply_key - + SentNotification.for(reply_key) end @@ -82,10 +82,7 @@ module Gitlab attachments = Email::AttachmentUploader.new(message).execute(sent_notification.project) attachments.each do |link| - text = "[#{link[:alt]}](#{link[:url]})" - text.prepend("!") if link[:is_image] - - reply << "\n\n#{text}" + reply << "\n\n#{link[:markdown]}" end reply diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index 403ebeec474..d5f755f90e5 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -232,9 +232,7 @@ module Gitlab return nil if res.nil? - text = "[#{res['alt']}](#{res['url']})" - text = "!#{text}" if res['is_image'] - text + text = res['markdown'] end def build_attachment_url(rel_url) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ab2530859ea..6f4c336b66c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -353,6 +353,20 @@ describe API::API, api: true do end end + describe "POST /projects/:id/uploads" do + before { project } + + it "uploads the file and returns its info" do + post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") + + expect(response.status).to be(201) + expect(json_response['alt']).to eq("dk") + expect(json_response['url']).to start_with("/uploads/") + expect(json_response['url']).to end_with("/dk.png") + expect(json_response['is_image']).to eq(true) + end + end + describe 'GET /projects/:id' do before { project } before { project_member } -- cgit v1.2.1 From 706d7eb0b7a1d54604a11e1202e44069c6acccee Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 13:37:59 +0100 Subject: Satisfy Rubocp --- lib/gitlab/fogbugz_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb index d5f755f90e5..0e6bee732f1 100644 --- a/lib/gitlab/fogbugz_import/importer.rb +++ b/lib/gitlab/fogbugz_import/importer.rb @@ -232,7 +232,7 @@ module Gitlab return nil if res.nil? - text = res['markdown'] + res['markdown'] end def build_attachment_url(rel_url) -- cgit v1.2.1 From b60c146267dfa8dc1c170426e1817c6b2a168d1a Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jan 2016 13:49:38 +0100 Subject: Change :variable_id to :key as resource ID in API --- app/models/ci/variable.rb | 6 +++++- lib/api/variables.rb | 41 ++++++++++++++---------------------- spec/requests/api/variables_spec.rb | 42 +++++++++++++------------------------ 3 files changed, 36 insertions(+), 53 deletions(-) diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 0e2712086ca..1d9ee91a31c 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -18,8 +18,12 @@ module Ci belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id - validates_presence_of :key validates_uniqueness_of :key, scope: :gl_project_id + validates :key, + presence: true, + length: { within: 0..255 }, + format: { with: /\A[a-zA-Z0-9_]+\z/, + message: "can contain only letters, digits and '_'." } attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base end diff --git a/lib/api/variables.rb b/lib/api/variables.rb index b8bbcb6ce3b..cc038e5731d 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -22,19 +22,12 @@ module API # # Parameters: # id (required) - The ID of a project - # variable_id (required) - The ID OR `key` of variable to show; if variable_id contains only digits it's treated - # as ID other ways it's treated as `key` + # key (required) - The `key` of variable # Example Request: - # GET /projects/:id/variables/:variable_id - get ':id/variables/:variable_id' do - variable_id = params[:variable_id] - variables = user_project.variables - variables = - if variable_id.match(/^\d+$/) - variables.where(id: variable_id.to_i) - else - variables.where(key: variable_id) - end + # GET /projects/:id/variables/:key + get ':id/variables/:key' do + key = params[:key] + variables = user_project.variables.where(key: key) return not_found!('Variable') if variables.empty? @@ -45,8 +38,8 @@ module API # # Parameters: # id (required) - The ID of a project - # key (required) - The key of variable being created - # value (required) - The value of variable being created + # key (required) - The key of variable + # value (required) - The value of variable # Example Request: # POST /projects/:id/variables post ':id/variables' do @@ -63,17 +56,15 @@ module API # # Parameters: # id (required) - The ID of a project - # variable_id (required) - The ID of a variable - # key (optional) - new value for `key` field of variable - # value (optional) - new value for `value` field of variable + # key (optional) - The `key` of variable + # value (optional) - New value for `value` field of variable # Example Request: - # PUT /projects/:id/variables/:variable_id - put ':id/variables/:variable_id' do - variable = user_project.variables.where(id: params[:variable_id].to_i).first + # PUT /projects/:id/variables/:key + put ':id/variables/:key' do + variable = user_project.variables.where(key: params[:key]).first return not_found!('Variable') unless variable - variable.key = params[:key] variable.value = params[:value] variable.save! @@ -84,11 +75,11 @@ module API # # Parameters: # id (required) - The ID of a project - # variable_id (required) - The ID of a variable + # key (required) - The ID of a variable # Exanoke Reqyest: - # DELETE /projects/:id/variables/:variable_id - delete ':id/variables/:variable_id' do - variable = user_project.variables.where(id: params[:variable_id].to_i).first + # DELETE /projects/:id/variables/:key + delete ':id/variables/:key' do + variable = user_project.variables.where(key: params[:key]).first return not_found!('Variable') unless variable diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb index bf0dd77473a..214d7d5a0cc 100644 --- a/spec/requests/api/variables_spec.rb +++ b/spec/requests/api/variables_spec.rb @@ -37,26 +37,17 @@ describe API::API, api: true do end end - describe 'GET /projects/:id/variables/:variable_id' do + describe 'GET /projects/:id/variables/:key' do context 'authorized user with proper permissions' do - it 'should return project variable details when ID is used as :variable_id' do - get api("/projects/#{project.id}/variables/#{variable.id}", user) - - expect(response.status).to eq(200) - expect(json_response['key']).to eq(variable.key) - expect(json_response['value']).to eq(variable.value) - end - - it 'should return project variable details when `key` is used as :variable_id' do + it 'should return project variable details' do get api("/projects/#{project.id}/variables/#{variable.key}", user) expect(response.status).to eq(200) - expect(json_response['id']).to eq(variable.id) expect(json_response['value']).to eq(variable.value) end it 'should responde with 404 Not Found if requesting non-existing variable' do - get api("/projects/#{project.id}/variables/9999", user) + get api("/projects/#{project.id}/variables/non_existing_variable", user) expect(response.status).to eq(404) end @@ -64,7 +55,7 @@ describe API::API, api: true do context 'authorized user with invalid permissions' do it 'should not return project variable details' do - get api("/projects/#{project.id}/variables/#{variable.id}", user2) + get api("/projects/#{project.id}/variables/#{variable.key}", user2) expect(response.status).to eq(403) end @@ -72,7 +63,7 @@ describe API::API, api: true do context 'unauthorized user' do it 'should not return project variable details' do - get api("/projects/#{project.id}/variables/#{variable.id}") + get api("/projects/#{project.id}/variables/#{variable.key}") expect(response.status).to eq(401) end @@ -117,26 +108,23 @@ describe API::API, api: true do end end - describe 'PUT /projects/:id/variables/:variable_id' do + describe 'PUT /projects/:id/variables/:key' do context 'authorized user with proper permissions' do it 'should update variable data' do initial_variable = project.variables.first - key_before = initial_variable.key value_before = initial_variable.value - put api("/projects/#{project.id}/variables/#{variable.id}", user), key: 'TEST_VARIABLE_1_UP', value: 'VALUE_1_UP' + put api("/projects/#{project.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP' updated_variable = project.variables.first expect(response.status).to eq(200) - expect(key_before).to eq(variable.key) expect(value_before).to eq(variable.value) - expect(updated_variable.key).to eq('TEST_VARIABLE_1_UP') expect(updated_variable.value).to eq('VALUE_1_UP') end it 'should responde with 404 Not Found if requesting non-existing variable' do - put api("/projects/#{project.id}/variables/9999", user) + put api("/projects/#{project.id}/variables/non_existing_variable", user) expect(response.status).to eq(404) end @@ -144,7 +132,7 @@ describe API::API, api: true do context 'authorized user with invalid permissions' do it 'should not update variable' do - put api("/projects/#{project.id}/variables/#{variable.id}", user2) + put api("/projects/#{project.id}/variables/#{variable.key}", user2) expect(response.status).to eq(403) end @@ -152,24 +140,24 @@ describe API::API, api: true do context 'unauthorized user' do it 'should not update variable' do - put api("/projects/#{project.id}/variables/#{variable.id}") + put api("/projects/#{project.id}/variables/#{variable.key}") expect(response.status).to eq(401) end end end - describe 'DELETE /projects/:id/variables/:variable_id' do + describe 'DELETE /projects/:id/variables/:key' do context 'authorized user with proper permissions' do it 'should delete variable' do expect do - delete api("/projects/#{project.id}/variables/#{variable.id}", user) + delete api("/projects/#{project.id}/variables/#{variable.key}", user) end.to change{project.variables.count}.by(-1) expect(response.status).to eq(200) end it 'should responde with 404 Not Found if requesting non-existing variable' do - delete api("/projects/#{project.id}/variables/9999", user) + delete api("/projects/#{project.id}/variables/non_existing_variable", user) expect(response.status).to eq(404) end @@ -177,7 +165,7 @@ describe API::API, api: true do context 'authorized user with invalid permissions' do it 'should not delete variable' do - delete api("/projects/#{project.id}/variables/#{variable.id}", user2) + delete api("/projects/#{project.id}/variables/#{variable.key}", user2) expect(response.status).to eq(403) end @@ -185,7 +173,7 @@ describe API::API, api: true do context 'unauthorized user' do it 'should not delete variable' do - delete api("/projects/#{project.id}/variables/#{variable.id}") + delete api("/projects/#{project.id}/variables/#{variable.key}") expect(response.status).to eq(401) end -- cgit v1.2.1 From 892d5dbb87614156bb9ad7ce8aa817fdb6b9c79b Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 14:10:23 +0100 Subject: Update Gemfile.lock --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..3c7cb6cf439 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) -- cgit v1.2.1 From b60445906849e84ff52ac6a5d7d501bb5a21eb60 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jan 2016 14:10:49 +0100 Subject: Update ./doc/api --- doc/api/README.md | 1 + doc/api/variables.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/entities.rb | 2 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 doc/api/variables.md diff --git a/doc/api/README.md b/doc/api/README.md index 25a31b235cc..198b4ddfee1 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -23,6 +23,7 @@ - [Namespaces](namespaces.md) - [Settings](settings.md) - [Keys](keys.md) +- [Variables](variables.md) ## Clients diff --git a/doc/api/variables.md b/doc/api/variables.md new file mode 100644 index 00000000000..cdc9ba42254 --- /dev/null +++ b/doc/api/variables.md @@ -0,0 +1,106 @@ +# Variables + +## Variables keys + +All variable keys must contains only letters, digits and '\_'. They must also be no longer than 255 characters. + +## List project variables + +Get list of variables of a project. + +``` +GET /projects/:id/variables +``` + +Parameters: + +- `id` (required) - The ID of a project + +```json +[ + { + "key": "TEST_VARIABLE_1", + "value": "TEST_1" + }, + { + "key": "TEST_VARIABLE_2", + "value": "TEST_2" + } +] +``` + +## Show variable details + +Get details of specifica variable of a project. + +``` +GET /projects/:id/variables/:key +``` + +Parameters: + +- `id` (required) - The ID of a project +- `key` (required) - The `key` of variable + +```json +{ + "key": "TEST_VARIABLE_1", + "value": "TEST_1" +} +``` + +## Create variable + +Create new variable in project. + +``` +POST /projects/:id/variables +``` + +Parameters: + +- `id` (required) - The ID of a project +- `key` (required) - The `key` for variable +- `value` (required) - The `value` for variable + +```json +{ + "key": "NEW_VARIABLE", + "value": "new value" +} +``` + +## Update variable + +Update variable. + +``` +PUT /projects/:id/variables/:key +``` + +Parameters: + +- `id` (required) - The ID of a project +- `key` (required) - The `key` for variable +- `value` (required) - The `value` for variable + +```json +{ + "key": "NEW_VARIABLE", + "value": "updated value" +} +``` + +## Remove variable + +Remove variable. + +``` +DELETE /projects/:id/variables/:key +``` + +Parameters: + +- `id` (required) - The ID of a project +- `key` (required) - The `key` for variable + diff --git a/lib/api/entities.rb b/lib/api/entities.rb index f71d072f269..db3164d9d9c 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -367,7 +367,7 @@ module API end class Variable < Grape::Entity - expose :id, :key, :value + expose :key, :value end end end -- cgit v1.2.1 From 7a240397afc56ab366b6c3504761fbf531b78ec1 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 17:27:47 +0100 Subject: Added an index on milestones.title Certain pages (e.g. the group wide issues page) filter miletones by their title. Without an index this will result in a sequence scan on a large dataset increasing the total loading time of a page. --- db/migrate/20160106162223_add_index_milestones_title.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20160106162223_add_index_milestones_title.rb diff --git a/db/migrate/20160106162223_add_index_milestones_title.rb b/db/migrate/20160106162223_add_index_milestones_title.rb new file mode 100644 index 00000000000..767885e2aac --- /dev/null +++ b/db/migrate/20160106162223_add_index_milestones_title.rb @@ -0,0 +1,5 @@ +class AddIndexMilestonesTitle < ActiveRecord::Migration + def change + add_index :milestones, :title + end +end -- cgit v1.2.1 From 9dacc3bc568c6c8cfc4a1bc1af23eb96f9eae9b0 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 17:29:44 +0100 Subject: Sort by ID when sorting using "Recently created" Sorting by "id" has the same effect as sorting by created_at while performing far better and without the need of an extra index (in case one wanted to speed up sorting by "created_at"). Sorting by "Recently updated" still uses the physical "updated_at" column as there's no way to use the "id" column for this instead. --- app/controllers/application_controller.rb | 2 +- app/helpers/sorting_helper.rb | 4 ++-- app/models/concerns/sortable.rb | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d9a37a4d45f..81cb1367e2c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -286,7 +286,7 @@ class ApplicationController < ActionController::Base end def set_filters_params - params[:sort] ||= 'created_desc' + params[:sort] ||= 'id_desc' params[:scope] = 'all' if params[:scope].blank? params[:state] = 'opened' if params[:state].blank? diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index bb12d43f397..9a7c26d1ccf 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -63,11 +63,11 @@ module SortingHelper end def sort_value_oldest_created - 'created_asc' + 'id_asc' end def sort_value_recently_created - 'created_desc' + 'id_desc' end def sort_value_milestone_soon diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index 7391a77383c..8b47b9e0abd 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -11,6 +11,7 @@ module Sortable default_scope { order_id_desc } scope :order_id_desc, -> { reorder(id: :desc) } + scope :order_id_asc, -> { reorder(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) } @@ -28,6 +29,8 @@ module Sortable when 'updated_desc' then order_updated_desc when 'created_asc' then order_created_asc when 'created_desc' then order_created_desc + when 'id_desc' then order_id_desc + when 'id_asc' then order_id_asc else all end -- cgit v1.2.1 From 0d0049c0584be2d358048c3cc545406b64bf3826 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 17:32:25 +0100 Subject: Don't pluck IDs when getting issues/MRs per group This replaces plucking of IDs with a sub-query, saving the overhead of loading the data in Ruby and then mapping the rows to an Array of IDs. This also scales much better when dealing with a large amount of IDs that would be involved. --- app/models/issue.rb | 2 +- app/models/merge_request.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 80ecd15077f..21cc2105161 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -33,7 +33,7 @@ class Issue < ActiveRecord::Base belongs_to :project validates :project, presence: true - scope :of_group, ->(group) { where(project_id: group.project_ids) } + scope :of_group, ->(group) { where(project_id: group.projects.select(:id)) } scope :cared, ->(user) { where(assignee_id: user) } scope :open_for, ->(user) { opened.assigned_to(user) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 30d0c2b5961..3c41bebc807 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -131,7 +131,7 @@ class MergeRequest < ActiveRecord::Base validate :validate_branches validate :validate_fork - scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) } + scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id)) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } -- cgit v1.2.1 From fc443ea7bcdd7255745cd1811c8bc95546ae3b12 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Wed, 6 Jan 2016 17:33:43 +0100 Subject: Drop projects order in IssuableFinder When grabbing the projects to filter issues by we don't care about the order they're returned in. By removing the ORDER BY the resulting query can be quite a bit faster. --- app/finders/issuable_finder.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 3d5e8b6fbe7..4d56b48e3f8 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -79,9 +79,9 @@ class IssuableFinder if project? @projects = project elsif current_user && params[:authorized_only].presence && !current_user_related? - @projects = current_user.authorized_projects + @projects = current_user.authorized_projects.reorder(nil) else - @projects = ProjectsFinder.new.execute(current_user) + @projects = ProjectsFinder.new.execute(current_user).reorder(nil) end end -- cgit v1.2.1 From 5dc708e11c912acebf739851df4e9e2e11188d02 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 11:19:10 +0100 Subject: Updated Gemfile.lock due to Bundler re-ordering Bundler keeps re-ordering this particular Gem every time something is executed using "bundle exec". --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..3c7cb6cf439 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) -- cgit v1.2.1 From 9b0c360bd5f8dd0538ecb5ffcb39da9a618b0231 Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 12:38:21 +0100 Subject: Fixed issue sorting specs for ID changes These specs assumed data was still sorted by timestamp, instead of by ID. --- spec/features/issues_spec.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index a2fb3e4c75d..e844e681ebf 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -127,15 +127,15 @@ describe 'Issues', feature: true do it 'sorts by newest' do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_recently_created) - expect(first_issue).to include('foo') - expect(last_issue).to include('baz') + expect(first_issue).to include('baz') + expect(last_issue).to include('foo') end it 'sorts by oldest' do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_oldest_created) - expect(first_issue).to include('baz') - expect(last_issue).to include('foo') + expect(first_issue).to include('foo') + expect(last_issue).to include('baz') end it 'sorts by most recently updated' do @@ -190,8 +190,8 @@ describe 'Issues', feature: true do sort: sort_value_oldest_created, assignee_id: user2.id) - expect(first_issue).to include('bar') - expect(last_issue).to include('foo') + expect(first_issue).to include('foo') + expect(last_issue).to include('bar') expect(page).not_to have_content 'baz' end end -- cgit v1.2.1 From 9a250ad6d85678d7f9197a55c69cb724c81008e0 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 6 Jan 2016 11:27:23 +0100 Subject: Filter commits by search parameter Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/commits_controller.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 04a88990bf4..66e27524a23 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -8,10 +8,16 @@ class Projects::CommitsController < Projects::ApplicationController before_action :authorize_download_code! def show - @repo = @project.repository @limit, @offset = (params[:limit] || 40).to_i, (params[:offset] || 0).to_i + search = params[:search] + + @commits = + if search.present? + @repository.find_commits_by_message(search).compact + else + @repository.commits(@ref, @path, @limit, @offset) + end - @commits = @repo.commits(@ref, @path, @limit, @offset) @note_counts = project.notes.where(commit_id: @commits.map(&:id)). group(:commit_id).count -- cgit v1.2.1 From 5e93f912c3d77e8e22ff563d1701e89b36ddf733 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 6 Jan 2016 11:40:03 +0100 Subject: Add search field to commits page Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/pages/commits.scss | 10 +++------- app/views/projects/commits/show.html.haml | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 879bd287470..800df95cff3 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -28,10 +28,6 @@ } } -.commits-feed-holder { - float: right; -} - li.commit { list-style: none; @@ -126,14 +122,14 @@ li.commit { .divergence-graph { padding: 12px 12px 0 0; float: right; - + .graph-side { position: relative; width: 80px; height: 22px; padding: 5px 0 13px; float: left; - + .bar { position: absolute; height: 4px; @@ -149,7 +145,7 @@ li.commit { left: 0; border-radius: 0 3px 3px 0; } - + .count { padding-top: 6px; padding-bottom: 0px; diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 2dd99cc8215..0d652a832f5 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -10,15 +10,21 @@ .tree-ref-holder = render 'shared/ref_switcher', destination: 'commits' - .commits-feed-holder.hidden-xs.hidden-sm + .pull-right.hidden-xs.hidden-sm - if create_mr_button?(@repository.root_ref, @ref) - = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do - = icon('plus') - Create Merge Request + .pull-left.prepend-left-10 + = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do + = icon('plus') + Create Merge Request + + .pull-left.prepend-left-10 + = form_tag(namespace_project_commits_path(@project.namespace, @project, @ref), method: :get, class: 'pull-left commits-search-form') do + = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', class: 'form-control search-text-input', spellcheck: false } - if current_user && current_user.private_token - = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'prepend-left-10 btn' do - = icon("rss") + .pull-left.prepend-left-10 + = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do + = icon("rss") %ul.breadcrumb.repo-breadcrumb -- cgit v1.2.1 From d1a40e06cc34a83f196345635e5b5ed16685ab62 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 6 Jan 2016 12:58:15 +0100 Subject: Add ajax filtering for commits list Also handle commits list with Pager class to prevent code duplication Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/commits.js.coffee | 67 +++++++++++++------------------ app/views/projects/commits/show.html.haml | 10 ++--- 2 files changed, 31 insertions(+), 46 deletions(-) diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index c183e78e513..2d33e0f6ebe 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -1,15 +1,5 @@ class @CommitsList - @data = - ref: null - limit: 0 - offset: 0 - @disable = false - - @showProgress: -> - $('.loading').show() - - @hideProgress: -> - $('.loading').hide() + @timer = null @init: (ref, limit) -> $("body").on "click", ".day-commits-table li.commit", (event) -> @@ -18,38 +8,35 @@ class @CommitsList e.stopPropagation() return false - @data.ref = ref - @data.limit = limit - @data.offset = limit + Pager.init limit, true + + @content = $("#commits-list") + @searchField = $("#commits-search") + @initSearch() - this.initLoadMore() - this.showProgress() + @initSearch: -> + @timer = null + @searchField.keyup => + clearTimeout(@timer) + @timer = setTimeout(@filterResults, 500) + + @filterResults: => + form = $(".commits-search-form") + search = @searchField.val() + commitsUrl = form.attr("action") + '?' + form.serialize() + @setOpacitiy("0.5") - @getOld: -> - this.showProgress() $.ajax type: "GET" - url: location.href - data: @data - complete: this.hideProgress - success: (data) -> - CommitsList.append(data.count, data.html) + url: form.attr("action") + data: form.serialize() + complete: => + @setOpacitiy("1.0") + success: (data) => + @content.html(data.html) + # Change url so if user reload a page - search results are saved + history.replaceState {page: commitsUrl}, document.title, commitsUrl dataType: "json" - @append: (count, html) -> - $("#commits-list").append(html) - if count > 0 - @data.offset += count - else - @disable = true - - @initLoadMore: -> - $(document).unbind('scroll') - $(document).endlessScroll - bottomPixels: 400 - fireDelay: 1000 - fireOnce: true - ceaseFire: => - @disable - callback: => - this.getOld() + @setOpacitiy: (opacity) -> + @content.css("opacity", opacity) diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 0d652a832f5..5e59afcd783 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -19,7 +19,7 @@ .pull-left.prepend-left-10 = form_tag(namespace_project_commits_path(@project.namespace, @project, @ref), method: :get, class: 'pull-left commits-search-form') do - = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', class: 'form-control search-text-input', spellcheck: false } + = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false } - if current_user && current_user.private_token .pull-left.prepend-left-10 @@ -31,11 +31,9 @@ = commits_breadcrumbs %div{id: dom_id(@project)} - #commits-list= render "commits", project: @project + #commits-list.content_list= render "commits", project: @project .clear = spinner -- if @commits.count == @limit - :javascript - CommitsList.init("#{@ref}", #{@limit}); - +:javascript + CommitsList.init("#{@ref}", #{@limit}); -- cgit v1.2.1 From 0456855911448bca5996428283e0c3e9b2d27c3b Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 6 Jan 2016 17:12:25 +0100 Subject: Add CHANGELOG item and tests Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + features/project/commits/commits.feature | 5 +++++ features/steps/project/commits/commits.rb | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 94a776a35eb..879d057fff0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -23,6 +23,7 @@ v 8.4.0 (unreleased) - Validate README format before displaying - Enable Microsoft Azure OAuth2 support (Janis Meybohm) - Add file finder feature in tree view (koreamic) + - Ajax filter by message for commits page v 8.3.3 (unreleased) - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature index 5bb2d0e976b..01c10721312 100644 --- a/features/project/commits/commits.feature +++ b/features/project/commits/commits.feature @@ -55,3 +55,8 @@ Feature: Project Commits Scenario: I browse a commit with an image Given I visit a commit with an image that changed Then The diff links to both the previous and current image + + @javascript + Scenario: I filter commits by message + When I search "submodules" commits + Then I should see only "submodules" commits diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index a3141fe3be1..daf6cdaaac8 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -124,4 +124,13 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps expect(page).to have_content "build: pending" expect(page).to have_content "1 build" end + + step 'I search "submodules" commits' do + fill_in 'commits-search', with: 'submodules' + end + + step 'I should see only "submodules" commits' do + expect(page).to have_content "More submodules" + expect(page).not_to have_content "Change some files" + end end -- cgit v1.2.1 From b2b4e9a7eb925bec2a09e44940cab31199f1de5c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jan 2016 12:38:35 +0100 Subject: Prevent loading first 20 commits twice Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/commits.js.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index 2d33e0f6ebe..bddcf83d4dd 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -8,7 +8,7 @@ class @CommitsList e.stopPropagation() return false - Pager.init limit, true + Pager.init limit, false @content = $("#commits-list") @searchField = $("#commits-search") @@ -24,14 +24,14 @@ class @CommitsList form = $(".commits-search-form") search = @searchField.val() commitsUrl = form.attr("action") + '?' + form.serialize() - @setOpacitiy("0.5") + @setOpacitiy(0.5) $.ajax type: "GET" url: form.attr("action") data: form.serialize() complete: => - @setOpacitiy("1.0") + @setOpacitiy(1.0) success: (data) => @content.html(data.html) # Change url so if user reload a page - search results are saved -- cgit v1.2.1 From 4443a5f3c76015f7bf083248b6910d01839cfc88 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jan 2016 13:00:47 +0100 Subject: Add support for ref and path to commits filtering Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/commits_controller.rb | 2 +- app/models/repository.rb | 21 ++++++++++++--------- app/views/projects/commits/show.html.haml | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 66e27524a23..bf5b54c8cb7 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -13,7 +13,7 @@ class Projects::CommitsController < Projects::ApplicationController @commits = if search.present? - @repository.find_commits_by_message(search).compact + @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact else @repository.commits(@ref, @path, @limit, @offset) end diff --git a/app/models/repository.rb b/app/models/repository.rb index 9deb08d93b8..d9ff71c01ed 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -92,9 +92,12 @@ class Repository commits end - def find_commits_by_message(query) + def find_commits_by_message(query, ref = nil, path = nil, limit = 1000, offset = 0) + ref ||= root_ref + # Limited to 1000 commits for now, could be parameterized? - args = %W(#{Gitlab.config.git.bin_path} log --pretty=%H --max-count 1000 --grep=#{query}) + args = %W(#{Gitlab.config.git.bin_path} log #{ref} --pretty=%H --skip #{offset} --max-count #{limit} --grep=#{query}) + args = args.concat(%W(-- #{path})) if path.present? git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp) commits = git_log_results.map { |c| commit(c) } @@ -175,7 +178,7 @@ class Repository def size cache.fetch(:size) { raw_repository.size } end - + def diverging_commit_counts(branch) root_ref_hash = raw_repository.rev_parse_target(root_ref).oid cache.fetch(:"diverging_commit_counts_#{branch.name}") do @@ -183,7 +186,7 @@ class Repository # than SHA-1 hashes number_commits_behind = commits_between(branch.target, root_ref_hash).size number_commits_ahead = commits_between(root_ref_hash, branch.target).size - + { behind: number_commits_behind, ahead: number_commits_ahead } end end @@ -192,7 +195,7 @@ class Repository %i(size branch_names tag_names commit_count readme version contribution_guide changelog license) end - + def branch_cache_keys branches.map do |branch| :"diverging_commit_counts_#{branch.name}" @@ -205,7 +208,7 @@ class Repository send(key) end end - + branches.each do |branch| unless cache.exist?(:"diverging_commit_counts_#{branch.name}") send(:diverging_commit_counts, branch) @@ -227,10 +230,10 @@ class Repository cache_keys.each do |key| cache.expire(key) end - + expire_branch_cache end - + def expire_branch_cache branches.each do |branch| cache.expire(:"diverging_commit_counts_#{branch.name}") @@ -242,7 +245,7 @@ class Repository cache.expire(key) send(key) end - + branches.each do |branch| cache.expire(:"diverging_commit_counts_#{branch.name}") diverging_commit_counts(branch) diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 5e59afcd783..8f6625fef8a 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -18,7 +18,7 @@ Create Merge Request .pull-left.prepend-left-10 - = form_tag(namespace_project_commits_path(@project.namespace, @project, @ref), method: :get, class: 'pull-left commits-search-form') do + = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false } - if current_user && current_user.private_token -- cgit v1.2.1 From 44dc9aa69e19d90564fdb279f9e8d51b5caf2cae Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jan 2016 13:04:25 +0100 Subject: Fix typo in js method and some repeating css Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/commits.js.coffee | 6 +++--- app/assets/stylesheets/framework/blocks.scss | 9 +++++++++ app/views/projects/commits/show.html.haml | 8 ++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index bddcf83d4dd..c337038e446 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -24,19 +24,19 @@ class @CommitsList form = $(".commits-search-form") search = @searchField.val() commitsUrl = form.attr("action") + '?' + form.serialize() - @setOpacitiy(0.5) + @setOpacity(0.5) $.ajax type: "GET" url: form.attr("action") data: form.serialize() complete: => - @setOpacitiy(1.0) + @setOpacity(1.0) success: (data) => @content.html(data.html) # Change url so if user reload a page - search results are saved history.replaceState {page: commitsUrl}, document.title, commitsUrl dataType: "json" - @setOpacitiy: (opacity) -> + @setOpacity: (opacity) -> @content.css("opacity", opacity) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 206d39cc9b3..fa0e70847f3 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -72,6 +72,15 @@ > p:last-child { margin-bottom: 0; } + + .block-controls { + float: right; + + .control { + float: left; + margin-left: 10px; + } + } } .cover-block { diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml index 8f6625fef8a..034057da42e 100644 --- a/app/views/projects/commits/show.html.haml +++ b/app/views/projects/commits/show.html.haml @@ -10,19 +10,19 @@ .tree-ref-holder = render 'shared/ref_switcher', destination: 'commits' - .pull-right.hidden-xs.hidden-sm + .block-controls.hidden-xs.hidden-sm - if create_mr_button?(@repository.root_ref, @ref) - .pull-left.prepend-left-10 + .control = link_to create_mr_path(@repository.root_ref, @ref), class: 'btn btn-success' do = icon('plus') Create Merge Request - .pull-left.prepend-left-10 + .control = form_tag(namespace_project_commits_path(@project.namespace, @project, @id), method: :get, class: 'pull-left commits-search-form') do = search_field_tag :search, params[:search], { placeholder: 'Filter by commit message', id: 'commits-search', class: 'form-control search-text-input', spellcheck: false } - if current_user && current_user.private_token - .pull-left.prepend-left-10 + .control = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Commits Feed", class: 'btn' do = icon("rss") -- cgit v1.2.1 From c7fc84f5162f58d1f643beaf88196a3333c75980 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 7 Jan 2016 15:15:21 +0100 Subject: Use fadeTo instead of css Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/commits.js.coffee | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/commits.js.coffee b/app/assets/javascripts/commits.js.coffee index c337038e446..ffd3627b1b0 100644 --- a/app/assets/javascripts/commits.js.coffee +++ b/app/assets/javascripts/commits.js.coffee @@ -24,19 +24,16 @@ class @CommitsList form = $(".commits-search-form") search = @searchField.val() commitsUrl = form.attr("action") + '?' + form.serialize() - @setOpacity(0.5) + @content.fadeTo('fast', 0.5) $.ajax type: "GET" url: form.attr("action") data: form.serialize() complete: => - @setOpacity(1.0) + @content.fadeTo('fast', 1.0) success: (data) => @content.html(data.html) # Change url so if user reload a page - search results are saved history.replaceState {page: commitsUrl}, document.title, commitsUrl dataType: "json" - - @setOpacity: (opacity) -> - @content.css("opacity", opacity) -- cgit v1.2.1 From 8386edafd13c8cca1c6ed45abbbc554351300e9d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 7 Jan 2016 06:28:24 -0800 Subject: Accept 2xx status codes for successful Web hook triggers Closes https://github.com/gitlabhq/gitlabhq/issues/9956 --- CHANGELOG | 1 + app/models/hooks/web_hook.rb | 2 +- spec/models/hooks/web_hook_spec.rb | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 94a776a35eb..a849d5efbf0 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.4.0 (unreleased) + - Accept 2xx status codes for successful Web hook triggers (Stan Hu) - Fix missing date of month in network graph when commits span a month (Stan Hu) - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu) - Don't notify users twice if they are both project watchers and subscribers (Stan Hu) diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 40eb0e20b4b..b12a45e922a 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -60,7 +60,7 @@ class WebHook < ActiveRecord::Base basic_auth: auth) end - [response.code == 200, ActionView::Base.full_sanitizer.sanitize(response.to_s)] + [(response.code >= 200 && response.code < 300), ActionView::Base.full_sanitizer.sanitize(response.to_s)] rescue SocketError, OpenSSL::SSL::SSLError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e logger.error("WebHook Error => #{e}") [false, e.to_s] diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 2d90b0793cc..7070aa4ac62 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -77,5 +77,17 @@ describe ProjectHook, models: true do expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) end + + it "handles 200 status code" do + WebMock.stub_request(:post, @project_hook.url).to_return(status: 200, body: "Success") + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + end + + it "handles 2xx status codes" do + WebMock.stub_request(:post, @project_hook.url).to_return(status: 201, body: "Success") + + expect(@project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success']) + end end end -- cgit v1.2.1 From 19a0db30ba14cb07a8f542abec56bd9cb2fa417f Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Thu, 7 Jan 2016 15:34:37 +0100 Subject: Removed ORDER BY in "of_group" scopes These scopes don't care about the order. Removing the explicit "ORDER BY" can speed up the queries by a little bit. --- app/models/issue.rb | 4 +++- app/models/merge_request.rb | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/models/issue.rb b/app/models/issue.rb index 21cc2105161..f52e47f3e62 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -33,7 +33,9 @@ class Issue < ActiveRecord::Base belongs_to :project validates :project, presence: true - scope :of_group, ->(group) { where(project_id: group.projects.select(:id)) } + scope :of_group, + ->(group) { where(project_id: group.projects.select(:id).reorder(nil)) } + scope :cared, ->(user) { where(assignee_id: user) } scope :open_for, ->(user) { opened.assigned_to(user) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 3c41bebc807..4e685ad3b7c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -131,7 +131,7 @@ class MergeRequest < ActiveRecord::Base validate :validate_branches validate :validate_fork - scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id)) } + scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.projects.select(:id).reorder(nil)) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } scope :by_milestone, ->(milestone) { where(milestone_id: milestone) } -- cgit v1.2.1 From 1e927d39b4bf6d1177dee0dd4a6c60bf270db3f2 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 15:51:12 +0100 Subject: Update spec --- Gemfile.lock | 10 +++++----- spec/lib/gitlab/email/receiver_spec.rb | 11 +++++++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..3c7cb6cf439 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) diff --git a/spec/lib/gitlab/email/receiver_spec.rb b/spec/lib/gitlab/email/receiver_spec.rb index b535413bbd4..abe179cd4af 100644 --- a/spec/lib/gitlab/email/receiver_spec.rb +++ b/spec/lib/gitlab/email/receiver_spec.rb @@ -42,7 +42,7 @@ describe Gitlab::Email::Receiver, lib: true do context "when the email was auto generated" do let!(:reply_key) { '636ca428858779856c226bb145ef4fad' } let!(:email_raw) { fixture_file("emails/auto_reply.eml") } - + it "raises an AutoGeneratedEmailError" do expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::AutoGeneratedEmailError) end @@ -90,7 +90,7 @@ describe Gitlab::Email::Receiver, lib: true do context "when the reply is blank" do let!(:email_raw) { fixture_file("emails/no_content_reply.eml") } - + it "raises an EmptyEmailError" do expect { receiver.execute }.to raise_error(Gitlab::Email::Receiver::EmptyEmailError) end @@ -107,13 +107,16 @@ describe Gitlab::Email::Receiver, lib: true do end context "when everything is fine" do + let(:markdown) { "![image](uploads/image.png)" } + before do allow_any_instance_of(Gitlab::Email::AttachmentUploader).to receive(:execute).and_return( [ { url: "uploads/image.png", is_image: true, - alt: "image" + alt: "image", + markdown: markdown } ] ) @@ -132,7 +135,7 @@ describe Gitlab::Email::Receiver, lib: true do note = noteable.notes.last - expect(note.note).to include("![image](uploads/image.png)") + expect(note.note).to include(markdown) end end end -- cgit v1.2.1 From c3865bda0257506e3e9ba8352913eb288f0e3e34 Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Thu, 7 Jan 2016 14:04:01 +0100 Subject: Properly set task-list class on single item task lists --- CHANGELOG | 1 + lib/banzai/filter/task_list_filter.rb | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 22fb91baaf0..c034344e7f9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ v 8.4.0 (unreleased) - Update version check images to use SVG - Validate README format before displaying - Enable Microsoft Azure OAuth2 support (Janis Meybohm) + - Properly set task-list class on single item task lists v 8.3.3 (unreleased) - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb index bdf7c2ebdfc..d0ce13003a5 100644 --- a/lib/banzai/filter/task_list_filter.rb +++ b/lib/banzai/filter/task_list_filter.rb @@ -12,13 +12,18 @@ module Banzai # # See https://github.com/github/task_list/pull/60 class TaskListFilter < TaskList::Filter - def add_css_class(node, *new_class_names) + def add_css_class_with_fix(node, *new_class_names) if new_class_names.include?('task-list') - super if node.children.any? { |c| c['class'] == 'task-list-item' } - else - super + # Don't add class to all lists + return + elsif new_class_names.include?('task-list-item') + add_css_class_without_fix(node.parent, 'task-list') end + + add_css_class_without_fix(node, *new_class_names) end + + alias_method_chain :add_css_class, :fix end end end -- cgit v1.2.1 From ab9612df8dd97d83a94a8290f1530760e5cc7d2e Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 11:47:39 -0500 Subject: initial json requests instead of HTML --- app/assets/javascripts/merge_request.js.coffee | 6 ++++ .../javascripts/merge_request_widget.js.coffee | 5 +-- .../projects/merge_requests_controller.rb | 24 ++++++++++--- app/helpers/merge_requests_helper.rb | 9 +++++ .../merge_requests/widget/_closed.html.haml | 2 +- .../merge_requests/widget/_locked.html.haml | 2 +- .../merge_requests/widget/_merged.html.haml | 2 +- .../projects/merge_requests/widget/_open.html.haml | 39 +++++++++++----------- .../projects/merge_requests/widget/_show.html.haml | 14 +++----- .../merge_requests/widget/open/_archived.html.haml | 2 +- 10 files changed, 66 insertions(+), 39 deletions(-) diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 8c321319b30..c6c7f37707f 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -63,12 +63,18 @@ class @MergeRequest $('a.btn-reopen').removeClass('hidden') $('div.status-box-closed').removeClass('hidden') $('div.status-box-open').addClass('hidden') + + $('div.mr-state-widget-closed').removeClass('hidden') + $('div.mr-state-widget-opened').addClass('hidden') else $('a.btn-reopen').addClass('hidden') $('a.issuable-edit').removeClass('hidden') $('a.btn-close').removeClass('hidden') $('div.status-box-closed').addClass('hidden') $('div.status-box-open').removeClass('hidden') + + $('div.mr-state-widget-closed').addClass('hidden') + $('div.mr-state-widget-opened').removeClass('hidden') else new Flash(mergeRequestFailMessage, 'alert') $this.prop('disabled', false) diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 738ffc8343b..f0a687f79b1 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -28,8 +28,9 @@ class @MergeRequestWidget getMergeStatus: -> $.get @opts.url_to_automerge_check, (data) -> - $('.mr-state-widget').replaceWith(data) - + console.log("data",data); + # $('div.mr-state-widget.mr-state-widget-opened').replaceWith(data) + getCiStatus: -> if @opts.ci_enable $.get @opts.url_to_ci_check, (data) => diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index ab5c953189c..e3c4aa4873a 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -48,9 +48,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController @note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)). group(:commit_id).count + json_merge_request = @merge_requests.as_json + respond_to do |format| format.html - format.json { render json: @merge_request } + format.json do + render json: { + hi: "yes" + } + end format.diff { render text: @merge_request.to_diff(current_user) } format.patch { render text: @merge_request.to_patch(current_user) } end @@ -143,7 +149,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController format.json do render json: { saved: @merge_request.valid?, - assignee_avatar_url: @merge_request.assignee.try(:avatar_url) + assignee_avatar_url: @merge_request.assignee.try(:avatar_url), + closed_event: @merge_request.closed_event } end end @@ -154,8 +161,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController def merge_check @merge_request.check_if_can_be_merged if @merge_request.unchecked? - - render partial: "projects/merge_requests/widget/show.html.haml", layout: false + puts @merge_request.merge_status + respond_to do |format| + format.json do + render json: { + can_be_merged: @merge_request.merge_status == :can_be_merged + } + end + format.html do + render partial: "projects/merge_requests/widget/show.html.haml", layout: false + end + end end def cancel_merge_when_build_succeeds diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index fafe2acd538..6306450ca26 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -23,6 +23,15 @@ module MergeRequestsHelper return 'hidden' if mr.closed? == closed end + def merge_request_widget_visibility(mr, *states) + states.each do |state| + if mr.state == state + return + end + end + return 'hidden' + end + def mr_css_classes(mr) classes = "merge-request" classes << " closed" if mr.closed? diff --git a/app/views/projects/merge_requests/widget/_closed.html.haml b/app/views/projects/merge_requests/widget/_closed.html.haml index f3cc0e7e8a1..46ee22ec873 100644 --- a/app/views/projects/merge_requests/widget/_closed.html.haml +++ b/app/views/projects/merge_requests/widget/_closed.html.haml @@ -1,4 +1,4 @@ -.mr-state-widget +.mr-state-widget.mr-state-widget-closed{class: merge_request_widget_visibility(@merge_request, 'closed')} = render 'projects/merge_requests/widget/heading' .mr-widget-body %h4 diff --git a/app/views/projects/merge_requests/widget/_locked.html.haml b/app/views/projects/merge_requests/widget/_locked.html.haml index 78d0783cba0..55ecd69a6ce 100644 --- a/app/views/projects/merge_requests/widget/_locked.html.haml +++ b/app/views/projects/merge_requests/widget/_locked.html.haml @@ -1,4 +1,4 @@ -.mr-state-widget +.mr-state-widget.mr-state-widget-locked{class: merge_request_widget_visibility(@merge_request, 'locked')} = render 'projects/merge_requests/widget/heading' .mr-widget-body %h4 diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml index d1d602eecdc..2bb50967023 100644 --- a/app/views/projects/merge_requests/widget/_merged.html.haml +++ b/app/views/projects/merge_requests/widget/_merged.html.haml @@ -1,4 +1,4 @@ -.mr-state-widget +.mr-state-widget.mr-state-widget-merged{class: merge_request_widget_visibility(@merge_request, 'merged')} = render 'projects/merge_requests/widget/heading' .mr-widget-body %h4 diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml index 55dbae598d3..2257a166460 100644 --- a/app/views/projects/merge_requests/widget/_open.html.haml +++ b/app/views/projects/merge_requests/widget/_open.html.haml @@ -1,24 +1,23 @@ -.mr-state-widget +.mr-state-widget.mr-state-widget-opened{class: merge_request_widget_visibility(@merge_request, "opened","reopened")} = render 'projects/merge_requests/widget/heading' - .mr-widget-body - - if @project.archived? - = render 'projects/merge_requests/widget/open/archived' - - elsif @merge_request.commits.blank? - = render 'projects/merge_requests/widget/open/nothing' - - elsif @merge_request.branch_missing? - = render 'projects/merge_requests/widget/open/missing_branch' - - elsif @merge_request.unchecked? - = render 'projects/merge_requests/widget/open/check' - - elsif @merge_request.cannot_be_merged? - = render 'projects/merge_requests/widget/open/conflicts' - - elsif @merge_request.work_in_progress? - = render 'projects/merge_requests/widget/open/wip' - - elsif @merge_request.merge_when_build_succeeds? - = render 'projects/merge_requests/widget/open/merge_when_build_succeeds' - - elsif !@merge_request.can_be_merged_by?(current_user) - = render 'projects/merge_requests/widget/open/not_allowed' - - elsif @merge_request.can_be_merged? - = render 'projects/merge_requests/widget/open/accept' + .mr-widget-body.merge-request-archived{class: ("hidden" unless @project.archived?)} + = render 'projects/merge_requests/widget/open/archived' + .mr-widget-body.merge-request-blank{class: ("hidden" unless @merge_request.commits.blank?)} + = render 'projects/merge_requests/widget/open/nothing' + .mr-widget-body.merge-request-branch-missing{class: ("hidden" unless @merge_request.branch_missing?)} + = render 'projects/merge_requests/widget/open/missing_branch' + .mr-widget-body.merge-request-unchecked{class: ("hidden" unless @merge_request.unchecked?)} + = render 'projects/merge_requests/widget/open/check' + .mr-widget-body.merge-request-cannot-be-merged{class: ("hidden" unless @merge_request.cannot_be_merged?)} + = render 'projects/merge_requests/widget/open/conflicts' + .mr-widget-body.merge-request-work-in-progress{class: ("hidden" unless @merge_request.work_in_progress?)} + = render 'projects/merge_requests/widget/open/wip' + .mr-widget-body.merge-request-merge-when-build-succeeds{class: ("hidden" unless @merge_request.merge_when_build_succeeds?)} + = render 'projects/merge_requests/widget/open/merge_when_build_succeeds' + .mr-widget-body.not-allowed{class: ("hidden" if @merge_request.can_be_merged_by?(current_user))} + = render 'projects/merge_requests/widget/open/not_allowed' + .mr-widget-body.merge-request-archived.can-be-merged{class: ("hidden" unless @merge_request.can_be_merged?)} + = render 'projects/merge_requests/widget/open/accept' - if @closes_issues.present? .mr-widget-footer diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml index a489d4f9b24..d8f81dab067 100644 --- a/app/views/projects/merge_requests/widget/_show.html.haml +++ b/app/views/projects/merge_requests/widget/_show.html.haml @@ -1,17 +1,13 @@ -- if @merge_request.open? - = render 'projects/merge_requests/widget/open' -- elsif @merge_request.merged? - = render 'projects/merge_requests/widget/merged' -- elsif @merge_request.closed? - = render 'projects/merge_requests/widget/closed' -- elsif @merge_request.locked? - = render 'projects/merge_requests/widget/locked' += render 'projects/merge_requests/widget/open' += render 'projects/merge_requests/widget/merged' += render 'projects/merge_requests/widget/closed' += render 'projects/merge_requests/widget/locked' :javascript var merge_request_widget; merge_request_widget = new MergeRequestWidget({ - url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", + url_to_automerge_check: "#{merge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request, format: :json)}", check_enable: #{@merge_request.unchecked? ? "true" : "false"}, url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}", ci_enable: #{@project.ci_service ? "true" : "false"}, diff --git a/app/views/projects/merge_requests/widget/open/_archived.html.haml b/app/views/projects/merge_requests/widget/open/_archived.html.haml index ab30fa6b243..0d61e56d8fb 100644 --- a/app/views/projects/merge_requests/widget/open/_archived.html.haml +++ b/app/views/projects/merge_requests/widget/open/_archived.html.haml @@ -1,4 +1,4 @@ -%h4 +%h4 Project is archived %p This merge request cannot be merged because archived projects cannot be written to. -- cgit v1.2.1 From 8f6ca700553bdb88fa924dba21f5cb00a3c515f1 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 12:27:01 -0500 Subject: fixes ajax issue with issue spec --- Gemfile.lock | 12 ++++++------ spec/javascripts/issue_spec.js.coffee | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..f5391ef9e48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) @@ -999,4 +999,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.10.6 + 1.11.2 diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index 7e67c778861..b85fadcbe82 100644 --- a/spec/javascripts/issue_spec.js.coffee +++ b/spec/javascripts/issue_spec.js.coffee @@ -26,10 +26,10 @@ describe 'reopen/close issue', -> fixture.load('issues_show.html') @issue = new Issue() it 'closes an issue', -> - $.ajax = (obj) -> - expect(obj.type).toBe('PUT') - expect(obj.url).toBe('http://gitlab.com/issues/6/close') - obj.success saved: true + spyOn(jQuery, 'ajax').and.callFake (req) -> + expect(req.type).toBe('PUT') + expect(req.url).toBe('http://gitlab.com/issues/6/close') + req.success saved: true $btnClose = $('a.btn-close') $btnReopen = $('a.btn-reopen') @@ -46,10 +46,10 @@ describe 'reopen/close issue', -> it 'fails to closes an issue with success:false', -> - $.ajax = (obj) -> - expect(obj.type).toBe('PUT') - expect(obj.url).toBe('http://goesnowhere.nothing/whereami') - obj.success saved: false + spyOn(jQuery, 'ajax').and.callFake (req) -> + expect(req.type).toBe('PUT') + expect(req.url).toBe('http://goesnowhere.nothing/whereami') + req.success saved: false $btnClose = $('a.btn-close') $btnReopen = $('a.btn-reopen') @@ -69,10 +69,10 @@ describe 'reopen/close issue', -> it 'fails to closes an issue with HTTP error', -> - $.ajax = (obj) -> - expect(obj.type).toBe('PUT') - expect(obj.url).toBe('http://goesnowhere.nothing/whereami') - obj.error() + spyOn(jQuery, 'ajax').and.callFake (req) -> + expect(req.type).toBe('PUT') + expect(req.url).toBe('http://goesnowhere.nothing/whereami') + req.error() $btnClose = $('a.btn-close') $btnReopen = $('a.btn-reopen') @@ -91,10 +91,10 @@ describe 'reopen/close issue', -> expect($('div.flash-alert').text()).toBe('Unable to update this issue at this time.') it 'reopens an issue', -> - $.ajax = (obj) -> - expect(obj.type).toBe('PUT') - expect(obj.url).toBe('http://gitlab.com/issues/6/reopen') - obj.success saved: true + spyOn(jQuery, 'ajax').and.callFake (req) -> + expect(req.type).toBe('PUT') + expect(req.url).toBe('http://gitlab.com/issues/6/reopen') + req.success saved: true $btnClose = $('a.btn-close') $btnReopen = $('a.btn-reopen') -- cgit v1.2.1 From e0ec69d919cb44194e76034f2324ec0d4f5f1df6 Mon Sep 17 00:00:00 2001 From: Tomasz Maczukin Date: Thu, 7 Jan 2016 18:48:33 +0100 Subject: Change 'trigger_id' to 'token' as resource ID in triggers API --- doc/api/triggers.md | 12 ++++------- lib/api/entities.rb | 2 +- lib/api/triggers.rb | 25 ++++++++--------------- spec/requests/api/triggers_spec.rb | 42 +++++++++++++------------------------- 4 files changed, 27 insertions(+), 54 deletions(-) diff --git a/doc/api/triggers.md b/doc/api/triggers.md index 6adcc8fe3b8..4150d7b4c10 100644 --- a/doc/api/triggers.md +++ b/doc/api/triggers.md @@ -17,7 +17,6 @@ Parameters: { "created_at": "2015-12-23T16:24:34.716Z", "deleted_at": null, - "id": 1, "last_used": "2016-01-04T15:41:21.986Z", "token": "fbdb730c2fbdb095a0862dbd8ab88b", "updated_at": "2015-12-23T16:24:34.716Z" @@ -25,7 +24,6 @@ Parameters: { "created_at": "2015-12-23T16:25:56.760Z", "deleted_at": null, - "id": 2, "last_used": null, "token": "7b9148c158980bbd9bcea92c17522d", "updated_at": "2015-12-23T16:25:56.760Z" @@ -38,19 +36,18 @@ Parameters: Get details of trigger of a project ``` -GET /projects/:id/triggers/:trigger_id +GET /projects/:id/triggers/:token ``` Parameters: - `id` (required) - The ID of a project -- `trigger_id` (required) - The ID of a trigger +- `token` (required) - The `token` of a trigger ```json { "created_at": "2015-12-23T16:25:56.760Z", "deleted_at": null, - "id": 2, "last_used": null, "token": "7b9148c158980bbd9bcea92c17522d", "updated_at": "2015-12-23T16:25:56.760Z" @@ -73,7 +70,6 @@ Parameters: { "created_at": "2016-01-07T09:53:58.235Z", "deleted_at": null, - "id": 5, "last_used": null, "token": "6d056f63e50fe6f8c5f8f4aa10edb7", "updated_at": "2016-01-07T09:53:58.235Z" @@ -85,10 +81,10 @@ Parameters: Remove a trigger of a project ``` -DELETE /projects/:id/triggers/:trigger_id +DELETE /projects/:id/triggers/:token ``` Parameters: - `id` (required) - The ID of a project -- `trigger_id` (required) - The ID of a trigger +- `token` (required) - The `token` of a trigger diff --git a/lib/api/entities.rb b/lib/api/entities.rb index bc0cd76a2b8..37c483b45ec 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -367,7 +367,7 @@ module API end class Trigger < Grape::Entity - expose :id, :token, :created_at, :updated_at, :deleted_at + expose :token, :created_at, :updated_at, :deleted_at expose :last_used do |repo_obj, _options| if repo_obj.respond_to?(:last_trigger_request) request = repo_obj.last_trigger_request diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index 0e548b936cd..25bb8aef20b 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -66,23 +66,14 @@ module API # # Parameters: # id (required) - The ID of a project - # trigger_id (required) - The ID or `token` of a trigger to show; if trigger_id contains only digits it's - # treated as ID other ways it's reated as `key` + # token (required) - The `token` of a trigger # Example Request: - # GET /projects/:id/triggers/:trigger_id - get ':id/triggers/:trigger_id' do + # GET /projects/:id/triggers/:token + get ':id/triggers/:token' do authenticate! authorize_admin_project - trigger_id = params[:trigger_id] - triggers = user_project.triggers - triggers = - if trigger_id.match(/^\d+$/) - triggers.where(id: trigger_id.to_i) - else - triggers.where(token: trigger_id) - end - + triggers = user_project.triggers.where(token: params[:token]) return not_found!('Trigger') if triggers.empty? present triggers.first, with: Entities::Trigger @@ -108,14 +99,14 @@ module API # # Parameters: # id (required) - The ID of a project - # trigger_id - The ID of trigger to delete + # token (required) - The `token` of a trigger # Example Request: - # DELETE /projects/:id/triggers/:trigger_id - delete ':id/triggers/:trigger_id' do + # DELETE /projects/:id/triggers/:token + delete ':id/triggers/:token' do authenticate! authorize_admin_project - trigger = user_project.triggers.where(id: params[:trigger_id].to_i).first + trigger = user_project.triggers.where(token: params[:token]).first return not_found!('Trigger') unless trigger trigger.destroy diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb index c1c2bb04b29..e8d89426ec0 100644 --- a/spec/requests/api/triggers_spec.rb +++ b/spec/requests/api/triggers_spec.rb @@ -93,8 +93,7 @@ describe API::API do expect(response.status).to eq(200) expect(json_response).to be_a(Array) - expect(json_response[0]['token']).to eq(trigger_token) - expect(json_response[1]['token']).to eq(trigger_token_2) + expect(json_response[0]).to have_key('token') end end @@ -115,30 +114,17 @@ describe API::API do end end - describe 'GET /projects/:id/triggers/:triggers_id' do + describe 'GET /projects/:id/triggers/:token' do context 'authenticated user with valid permissions' do - context 'ID is used as :trigger_id' do - it 'should return trigger details' do - get api("/projects/#{project.id}/triggers/#{trigger.id}", user) + it 'should return trigger details' do + get api("/projects/#{project.id}/triggers/#{trigger.token}", user) - expect(response.status).to eq(200) - expect(json_response).to be_a(Hash) - expect(json_response['token']).to eq(trigger_token) - end - end - - context '`token` is used as :trigger_id' do - it 'should return trigger details' do - get api("/projects/#{project.id}/triggers/#{trigger.token}", user) - - expect(response.status).to eq(200) - expect(json_response).to be_a(Hash) - expect(json_response['id']).to eq(trigger.id) - end + expect(response.status).to eq(200) + expect(json_response).to be_a(Hash) end it 'should responde with 404 Not Found if requesting non-existing trigger' do - get api("/projects/#{project.id}/triggers/9999", user) + get api("/projects/#{project.id}/triggers/abcdef012345", user) expect(response.status).to eq(404) end @@ -146,7 +132,7 @@ describe API::API do context 'authenticated user with invalid permissions' do it 'should not return triggers list' do - get api("/projects/#{project.id}/triggers/#{trigger.id}", user2) + get api("/projects/#{project.id}/triggers/#{trigger.token}", user2) expect(response.status).to eq(403) end @@ -154,7 +140,7 @@ describe API::API do context 'unauthentikated user' do it 'should not return triggers list' do - get api("/projects/#{project.id}/triggers/#{trigger.id}") + get api("/projects/#{project.id}/triggers/#{trigger.token}") expect(response.status).to eq(401) end @@ -190,17 +176,17 @@ describe API::API do end end - describe 'DELETE /projects/:id/triggers/:trigger_id' do + describe 'DELETE /projects/:id/triggers/:token' do context 'authenticated user with valid permissions' do it 'should delete trigger' do expect do - delete api("/projects/#{project.id}/triggers/#{trigger.id}", user) + delete api("/projects/#{project.id}/triggers/#{trigger.token}", user) end.to change{project.triggers.count}.by(-1) expect(response.status).to eq(200) end it 'should responde with 404 Not Found if requesting non-existing trigger' do - delete api("/projects/#{project.id}/triggers/9999", user) + delete api("/projects/#{project.id}/triggers/abcdef012345", user) expect(response.status).to eq(404) end @@ -208,7 +194,7 @@ describe API::API do context 'authenticated user with invalid permissions' do it 'should not delete trigger' do - delete api("/projects/#{project.id}/triggers/#{trigger.id}", user2) + delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2) expect(response.status).to eq(403) end @@ -216,7 +202,7 @@ describe API::API do context 'unauthentikated user' do it 'should not delete trigger' do - delete api("/projects/#{project.id}/triggers/#{trigger.id}") + delete api("/projects/#{project.id}/triggers/#{trigger.token}") expect(response.status).to eq(401) end -- cgit v1.2.1 From dada25d4472ec9ad601447fdd12da2301ac9ee79 Mon Sep 17 00:00:00 2001 From: Tommy Beadle Date: Thu, 7 Jan 2016 12:54:35 -0500 Subject: Include the username in user_create/destroy system hooks. --- app/services/system_hooks_service.rb | 3 ++- doc/system_hooks/system_hooks.md | 2 ++ spec/services/system_hooks_service_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 6dc854ec33d..2bd8223fbd8 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -47,7 +47,8 @@ class SystemHooksService data.merge!({ name: model.name, email: model.email, - user_id: model.id + user_id: model.id, + username: model.username }) when ProjectMember data.merge!(project_member_data(model)) diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 49f98ded046..0539f30e802 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -129,6 +129,7 @@ X-Gitlab-Event: System Hook "email": "js@gitlabhq.com", "event_name": "user_create", "name": "John Smith", + "username": "js", "user_id": 41 } ``` @@ -142,6 +143,7 @@ X-Gitlab-Event: System Hook "email": "js@gitlabhq.com", "event_name": "user_destroy", "name": "John Smith", + "username": "js", "user_id": 41 } ``` diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 4455ae7b321..1824e51ebfe 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -9,8 +9,8 @@ describe SystemHooksService, services: true do let(:group_member) { create(:group_member) } context 'event data' do - it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) } - it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) } + it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } + it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } -- cgit v1.2.1 From 1f64332e11949a5954b1e4ac7c6667b03ea70a0b Mon Sep 17 00:00:00 2001 From: Tommy Beadle Date: Thu, 7 Jan 2016 12:54:54 -0500 Subject: Include user_username in user_(add_to/remove_from)_(project/group) system hooks. --- app/services/system_hooks_service.rb | 2 ++ doc/system_hooks/system_hooks.md | 4 ++++ spec/services/system_hooks_service_spec.rb | 8 ++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 2bd8223fbd8..bc39ae9f2cb 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -100,6 +100,7 @@ class SystemHooksService project_path: model.project.path, project_path_with_namespace: model.project.path_with_namespace, project_id: model.project.id, + user_username: model.user.username, user_name: model.user.name, user_email: model.user.email, access_level: model.human_access, @@ -112,6 +113,7 @@ class SystemHooksService group_name: model.group.name, group_path: model.group.path, group_id: model.group.id, + user_username: model.user.username, user_name: model.user.name, user_email: model.user.email, user_id: model.user.id, diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 0539f30e802..612376e3a49 100644 --- a/doc/system_hooks/system_hooks.md +++ b/doc/system_hooks/system_hooks.md @@ -96,6 +96,7 @@ X-Gitlab-Event: System Hook "project_path_with_namespace": "jsmith/storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", + "user_username": "johnsmith", "user_id": 41, "project_visibility": "private", } @@ -115,6 +116,7 @@ X-Gitlab-Event: System Hook "project_path_with_namespace": "jsmith/storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", + "user_username": "johnsmith", "user_id": 41, "project_visibility": "private", } @@ -217,6 +219,7 @@ X-Gitlab-Event: System Hook "group_path": "storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", + "user_username": "johnsmith", "user_id": 41 } ``` @@ -233,6 +236,7 @@ X-Gitlab-Event: System Hook "group_path": "storecloud", "user_email": "johnsmith@gmail.com", "user_name": "John Smith", + "user_username": "johnsmith", "user_id": 41 } ``` diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 1824e51ebfe..eb066fe97f3 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -13,8 +13,8 @@ describe SystemHooksService, services: true do it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) } it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) } - it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } - it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :access_level, :project_visibility) } + it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :access_level, :project_visibility) } it { expect(event_data(key, :create)).to include(:username, :key, :id) } it { expect(event_data(key, :destroy)).to include(:username, :key, :id) } @@ -50,13 +50,13 @@ describe SystemHooksService, services: true do it do expect(event_data(group_member, :create)).to include( :event_name, :created_at, :updated_at, :group_name, :group_path, - :group_id, :user_id, :user_name, :user_email, :group_access + :group_id, :user_id, :user_username, :user_name, :user_email, :group_access ) end it do expect(event_data(group_member, :destroy)).to include( :event_name, :created_at, :updated_at, :group_name, :group_path, - :group_id, :user_id, :user_name, :user_email, :group_access + :group_id, :user_id, :user_username, :user_name, :user_email, :group_access ) end end -- cgit v1.2.1 From df18441e09e99d79d228c58d63bb9247c516163b Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 12:59:45 -0500 Subject: changes titles --- Gemfile.lock | 12 ++++++------ app/helpers/sorting_helper.rb | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..f5391ef9e48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) @@ -999,4 +999,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.10.6 + 1.11.2 diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb index bb12d43f397..99d7df64a83 100644 --- a/app/helpers/sorting_helper.rb +++ b/app/helpers/sorting_helper.rb @@ -19,7 +19,7 @@ module SortingHelper end def sort_title_recently_updated - 'Recently updated' + 'Last updated' end def sort_title_oldest_created @@ -27,7 +27,7 @@ module SortingHelper end def sort_title_recently_created - 'Recently created' + 'Last created' end def sort_title_milestone_soon -- cgit v1.2.1 From ad8366ed84b9c32c24c0ffe1c0a071f726cb119f Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 13:23:17 -0500 Subject: css change to height to make gray container fit --- Gemfile.lock | 12 ++++++------ app/assets/stylesheets/pages/events.scss | 1 + app/assets/stylesheets/pages/projects.scss | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a1168ed3b7a..f5391ef9e48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -443,6 +443,10 @@ GEM omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-azure-oauth2 (0.0.6) + jwt (~> 1.0) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) omniauth-bitbucket (0.0.2) multi_json (~> 1.7) omniauth (~> 1.1) @@ -488,10 +492,6 @@ GEM activesupport nokogiri (>= 1.4.4) omniauth (~> 1.0) - omniauth-azure-oauth2 (0.0.6) - jwt (~> 1.0) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) opennebula (4.14.2) json nokogiri @@ -920,6 +920,7 @@ DEPENDENCIES oauth2 (~> 1.0.0) octokit (~> 3.7.0) omniauth (~> 1.2.2) + omniauth-azure-oauth2 omniauth-bitbucket (~> 0.0.2) omniauth-cas3 (~> 1.1.2) omniauth-facebook (~> 3.0.0) @@ -931,7 +932,6 @@ DEPENDENCIES omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) omniauth_crowd - omniauth-azure-oauth2 org-ruby (~> 0.9.12) paranoia (~> 2.0) pg (~> 0.18.2) @@ -999,4 +999,4 @@ DEPENDENCIES wikicloth (= 0.8.1) BUNDLED WITH - 1.10.6 + 1.11.2 diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 282aaf2219b..984b4b91216 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -138,6 +138,7 @@ */ .event-last-push { overflow: auto; + width: 100%; .event-last-push-text { @include str-truncated(100%); padding: 5px 0; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index be6ef43e49c..0133a0d6822 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -415,6 +415,7 @@ ul.nav.nav-projects-tabs { border-bottom: 1px solid #EEE; margin: 0 -16px; padding: 0 $gl-padding; + height: 57px; ul.left-top-menu { display: inline-block; -- cgit v1.2.1 From 69209612e1793fcebcdb784074056d7a02b0f6f7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 22 Dec 2015 12:15:06 -0800 Subject: Suppress e-mails on failed builds if allow_failure is set Every time I push to GitLab, I get > 2 emails saying a spec failed when I don't care about benchmarks and other specs that have `allow_failure` set to `true`. --- CHANGELOG | 1 + .../project_services/builds_email_service.rb | 6 +++++- lib/gitlab/build_data_builder.rb | 1 + spec/lib/gitlab/build_data_builder_spec.rb | 1 + .../project_services/builds_email_service_spec.rb | 23 ++++++++++++++++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 spec/models/project_services/builds_email_service_spec.rb diff --git a/CHANGELOG b/CHANGELOG index 879d057fff0..a6726eb1ec2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.4.0 (unreleased) v 8.3.3 (unreleased) - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running + - Suppress e-mails on failed builds if allow_failure is set (Stan Hu) - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu) - Enable "Add key" button when user fills in a proper key (Stan Hu) diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb index 92c9b13c9b9..f6313255cbb 100644 --- a/app/models/project_services/builds_email_service.rb +++ b/app/models/project_services/builds_email_service.rb @@ -73,12 +73,16 @@ class BuildsEmailService < Service when 'success' !notify_only_broken_builds? when 'failed' - true + !allow_failure?(data) else false end end + def allow_failure?(data) + data[:build_allow_failure] == true + end + def all_recipients(data) all_recipients = recipients.split(',') diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb index 86bfa0a4378..34e949130da 100644 --- a/lib/gitlab/build_data_builder.rb +++ b/lib/gitlab/build_data_builder.rb @@ -23,6 +23,7 @@ module Gitlab build_started_at: build.started_at, build_finished_at: build.finished_at, build_duration: build.duration, + build_allow_failure: build.allow_failure, # TODO: do we still need it? project_id: project.id, diff --git a/spec/lib/gitlab/build_data_builder_spec.rb b/spec/lib/gitlab/build_data_builder_spec.rb index 839b30f1ff4..38be9448794 100644 --- a/spec/lib/gitlab/build_data_builder_spec.rb +++ b/spec/lib/gitlab/build_data_builder_spec.rb @@ -14,6 +14,7 @@ describe 'Gitlab::BuildDataBuilder' do it { expect(data[:tag]).to eq(build.tag) } it { expect(data[:build_id]).to eq(build.id) } it { expect(data[:build_status]).to eq(build.status) } + it { expect(data[:build_allow_failure]).to eq(false) } it { expect(data[:project_id]).to eq(build.project.id) } it { expect(data[:project_name]).to eq(build.project.name_with_namespace) } end diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb new file mode 100644 index 00000000000..905379a64e3 --- /dev/null +++ b/spec/models/project_services/builds_email_service_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe BuildsEmailService do + let(:build) { create(:ci_build) } + let(:data) { Gitlab::BuildDataBuilder.build(build) } + let(:service) { BuildsEmailService.new } + + describe :execute do + it "sends email" do + service.recipients = 'test@gitlab.com' + data[:build_status] = 'failed' + expect(BuildEmailWorker).to receive(:perform_async) + service.execute(data) + end + + it "does not sends email with failed build and allowed_failure on" do + data[:build_status] = 'failed' + data[:build_allow_failure] = true + expect(BuildEmailWorker).not_to receive(:perform_async) + service.execute(data) + end + end +end -- cgit v1.2.1 From 60aedb46d9e6d6c084ddfdc62805b7b1031fbea5 Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 15:18:07 -0500 Subject: adds reference to the bottom of sidebar --- app/assets/stylesheets/pages/issuable.scss | 10 +++++++++- app/views/shared/issuable/_sidebar.html.haml | 19 ++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 9da273a0b6b..81925283f11 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -94,8 +94,16 @@ } .cross-project-reference { - font-weight: bold; color: $gl-link-color; + + span { + white-space: nowrap; + width: 80%; + overflow: hidden; + position: relative; + display: inline-block; + text-overflow: ellipsis; + } button { float: right; diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 79c5cc7f40a..78c52938bd9 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -54,14 +54,6 @@ = f.collection_select :label_ids, issuable.project.labels.all, :id, :name, { selected: issuable.label_ids }, multiple: true, class: 'select2 js-select2', data: { placeholder: "Select labels" } - .block - .title - Cross-project reference - .cross-project-reference - %span#cross-project-reference - = cross_project_reference(@project, issuable) - = clipboard_button(clipboard_target: 'span#cross-project-reference') - = render "shared/issuable/participants", participants: issuable.participants(current_user) - if current_user @@ -77,7 +69,16 @@ You're not receiving notifications from this thread. .subscribed{class: ( 'hidden' unless subscribed )} You're receiving notifications because you're subscribed to this thread. + - project_ref = cross_project_reference(@project, issuable) + .block + .title + .cross-project-reference + %span#cross-project-reference + References: + %a{href: '#', title:project_ref} + = project_ref + = clipboard_button(clipboard_target: 'span#cross-project-reference') :javascript new Subscription("#{toggle_subscription_path(issuable)}"); - new IssuableContext(); + new IssuableContext(); \ No newline at end of file -- cgit v1.2.1 From a652c563663a0f5ed101fd5f9bcb14dfe111a9eb Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Thu, 7 Jan 2016 15:25:38 -0500 Subject: adds 85% width for text --- app/assets/stylesheets/pages/issuable.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 81925283f11..d4b44004f4f 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -98,7 +98,7 @@ span { white-space: nowrap; - width: 80%; + width: 85%; overflow: hidden; position: relative; display: inline-block; -- cgit v1.2.1 From 65308a9c15cd371986999d38eb6f359d8f601687 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 7 Jan 2016 15:23:29 -0500 Subject: Add spec for single-item task lists --- spec/lib/banzai/filter/task_list_filter_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/lib/banzai/filter/task_list_filter_spec.rb b/spec/lib/banzai/filter/task_list_filter_spec.rb index f2e3a44478d..569cbc885c7 100644 --- a/spec/lib/banzai/filter/task_list_filter_spec.rb +++ b/spec/lib/banzai/filter/task_list_filter_spec.rb @@ -7,4 +7,10 @@ describe Banzai::Filter::TaskListFilter, lib: true do exp = act = %(
  • Item
) expect(filter(act).to_html).to eq exp end + + it 'applies `task-list` to single-item task lists' do + act = filter('
  • [ ] Task 1
') + + expect(act.to_html).to start_with '