diff --git a/CHANGELOG b/CHANGELOG index f765899b42d08e00e9775a36bb9d77e0226faa0d..de161b3bc49195a31d391dd8081dd57092e16888 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,11 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.4.0 (unreleased) + - Autocomplete data is now always loaded, instead of when focusing a comment text area (Yorick Peterse) + - Improved performance of finding issues for an entire group (Yorick Peterse) + - Added custom application performance measuring system powered by InfluxDB (Yorick Peterse) - Bump fog to 1.36.0 (Stan Hu) + - Add user's last used IP addresses to admin page (Stan Hu) - Add housekeeping function to project settings page - The default GitLab logo now acts as a loading indicator - Fix caching issue where build status was not updating in project dashboard (Stan Hu) @@ -39,6 +43,10 @@ v 8.4.0 (unreleased) - API: Add support for deleting a tag via the API (Robert Schilling) - Allow subsequent validations in CI Linter - Show referenced MRs & Issues only when the current viewer can access them + - Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee) + +v 8.3.4 + - Use gitlab-workhorse 0.5.4 (fixes API routing bug) v 8.3.3 - Preserve CE behavior with JIRA integration by only calling API if URL is set @@ -53,6 +61,7 @@ v 8.3.3 - Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu) - Use WOFF versions of SourceSansPro fonts - Fix regression when builds were not generated for tags created through web/api interface + - Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells) v 8.3.2 - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b9c2b3d2f8e8242009998736c80c1303a90a2c9d..1eabbdc5cad0e0fbcc6eec6af135a4f3b2fec7a0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -334,9 +334,9 @@ merge request: 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript) 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security -1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Database Migrations](doc/development/migration_style_guide.md) -1. [Documentation styleguide](doc_styleguide.md) +1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) +1. [Documentation styleguide](doc/development/doc_styleguide.md) 1. Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index be14282b7fffb9ba95d51c6546ed9816dc8f3ff8..7d8568351b4f8d3809763af69f84d5499ef881d0 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.5.3 +0.5.4 diff --git a/Gemfile b/Gemfile index 9a5253924ea4587030a14344c6976d309f06abe2..a9a8bed106404b5f1fe634d086a27c6b1889471d 100644 --- a/Gemfile +++ b/Gemfile @@ -247,7 +247,7 @@ group :development, :test do gem 'byebug', platform: :mri gem 'pry-rails' - gem 'awesome_print', '~> 1.2.0' + gem 'awesome_print', '~> 1.2.0', require: false gem 'fuubar', '~> 2.0.0' gem 'database_cleaner', '~> 1.4.0' diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index c256ec8f41bb178189c5e39ce1a589d312a05dd8..0d26c58a81d606c45846bd8bd228609820325ddc 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/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 9047587db81af96a3aeca6a6451b64851a3cd4ba..ed0bf2b3f487f5008ac3ecab3afd3fd566b986d9 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -19,6 +19,7 @@ class @MergeRequest # Prevent duplicate event bindings @disableTaskList() + @initMRBtnListeners() if $("a.btn-close").length @initTaskList() @@ -43,6 +44,27 @@ class @MergeRequest $('.detail-page-description .js-task-list-container').taskList('enable') $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList + initMRBtnListeners: -> + _this = @ + $('a.btn-close, a.btn-reopen').on 'click', (e) -> + $this = $(this) + if $this.data('submitted') + return + e.preventDefault() + e.stopImmediatePropagation() + shouldSubmit = $this.hasClass('btn-comment') + console.log("shouldSubmit") + if shouldSubmit + _this.submitNoteForm($this.closest('form'),$this) + + submitNoteForm: (form, $button) => + noteText = form.find("textarea.js-note-text").val() + if noteText.trim().length > 0 + form.submit() + $button.data('submitted',true) + $button.trigger('click') + + 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 9e5204bfeebb7b474ad935e64a3eb59e46d2daa2..8ba00ecbbab8051122cc37d41aca2e90eaf537f8 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -33,8 +33,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 +510,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 +518,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/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 1c74e525a608348f04250da3177c9fb01ce15838..bbdb1c038c5e408b3714870d62cc12dd16a1813f 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -74,7 +74,7 @@ /** light list with border-bottom between li **/ -ul.bordered-list { +ul.bordered-list, ul.unstyled-list { @include basic-list; &.top-list { @@ -88,6 +88,10 @@ ul.bordered-list { } } +ul.unstyled-list > li { + border-bottom: none; +} + ul.task-list { li.task-list-item { list-style-type: none; diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index af75123b0af367508648ead871ee196e231dc049..d0ff3248ce14ea953a77977cf00fed9839168ac2 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -24,6 +24,7 @@ $gl-gray: #5a5a5a; $gl-padding: 16px; $gl-padding-top:10px; $gl-avatar-size: 46px; +$secondary-text: #7f8fa4; /* * Color schema diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index a02a3a72e79fc54bd8f1fa865e79c65823f29064..1e1af6628507a0ef673472600ac486f6c9d79ef6 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -144,3 +144,8 @@ form.edit-issue { .issue-form .select2-container { width: 250px !important; } + +.issue-closed-by-widget { + color: $secondary-text; + margin-left: 52px; +} \ No newline at end of file diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index 38a5a9fca08e36f30c77ee9c9b9cfd858297a250..2463cfa87be6dcff55e93085ae8d4eee28c82b4c 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -6,11 +6,9 @@ class Admin::AbuseReportsController < Admin::ApplicationController def destroy abuse_report = AbuseReport.find(params[:id]) - if params[:remove_user] - abuse_report.user.destroy - end - + abuse_report.remove_user if params[:remove_user] abuse_report.destroy + render nothing: true end end diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 44d06b6a64787b488a3df6dac30c9446f3ea6d6d..91f7d78bd73184e0d84dd3a96388b21ed4e62329 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -73,6 +73,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :metrics_pool_size, :metrics_timeout, :metrics_method_call_threshold, + :metrics_sample_interval, :recaptcha_enabled, :recaptcha_site_key, :recaptcha_private_key, diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2b9bad9c9ea1d04061faaeff1a1055c66192280e..436fbcd41386f4b88f0173c603cab838777348a4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -205,7 +205,7 @@ module ApplicationHelper def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false) element = content_tag :time, time.to_s, class: "#{html_class} js-timeago js-timeago-pending", - datetime: time.getutc.iso8601, + datetime: time.to_time.getutc.iso8601, title: time.in_time_zone.to_s(:medium), data: { toggle: 'tooltip', placement: placement, container: 'body' } @@ -266,7 +266,7 @@ module ApplicationHelper state: params[:state], scope: params[:scope], label_name: params[:label_name], - milestone_id: params[:milestone_id], + milestone_title: params[:milestone_title], assignee_id: params[:assignee_id], author_id: params[:author_id], sort: params[:sort], diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 5f0c921413a5b8e81ab6c479cbd15ebb2388994f..53c543c28c5d20e9352fb2f105d06f9d606f13dc 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -67,7 +67,7 @@ module NotesHelper line_type: line_type } - button_tag class: 'btn reply-btn js-discussion-reply-button', + button_tag class: 'btn btn-nr reply-btn js-discussion-reply-button', data: data, title: 'Add a reply' do link_text = icon('comment') link_text << ' Reply' diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index 55864236b2fd81d162703688f462333db8ebe8e1..2bc15c60d5784af9b02745091cd1268e03aca914 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -19,6 +19,11 @@ class AbuseReport < ActiveRecord::Base validates :message, presence: true validates :user_id, uniqueness: true + def remove_user + user.block + user.destroy + end + def notify return unless self.persisted? diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index d0aadfc330a1bedcb787cc730dbc2190256faae5..3bb50c63cacbf4f0280baabd3da846aefe585f54 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -48,8 +48,8 @@ class WebHook < ActiveRecord::Base else post_url = url.gsub("#{parsed_url.userinfo}@", "") auth = { - username: URI.decode(parsed_url.user), - password: URI.decode(parsed_url.password), + username: CGI.unescape(parsed_url.user), + password: CGI.unescape(parsed_url.password), } response = WebHook.post(post_url, body: data.to_json, diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 32a81808930312c721733915374298fb94164bc1..0e3fa4a40fe5d0a967b00df49f0c76439c445f18 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -120,13 +120,13 @@ class HipchatService < Service message << "#{push[:user_name]} " if Gitlab::Git.blank_ref?(before) message << "pushed new #{ref_type} <a href=\""\ - "#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\ + "#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\ " to #{project_link}\n" elsif Gitlab::Git.blank_ref?(after) message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n" else message << "pushed to #{ref_type} <a href=\""\ - "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> " + "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> " message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" @@ -255,8 +255,8 @@ class HipchatService < Service status = data[:commit][:status] duration = data[:commit][:duration] - branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>" - commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>" + branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>" + commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>" "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)" end diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index bd9b580038fbe5bd7ac1b772119c9f3ae7bd63ee..04c714bfaad4fddee9d29a4fd0ecb4591a26e0af 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -73,9 +73,10 @@ class IrkerService < Service 'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\ 'you want the channel to be a nickname instead, append ",isnick" to ' \ 'the channel name; if the channel is protected by a secret password, ' \ - ' append "?key=secretpassword" to the URI. Note that if you specify a ' \ - ' default IRC URI to prepend before each recipient, you can just give ' \ - ' a channel name.' }, + ' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \ + ' want to use a password, you have to omit the "#" on the channel). If you ' \ + ' specify a default IRC URI to prepend before each recipient, you can just ' \ + ' give a channel name.' }, { type: 'checkbox', name: 'colorize_messages' }, ] end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 6dc854ec33d884c76223be6824d0f7e7703aecd1..ea2b26ccb525604f49492fb8977ebec2dab20ac3 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)) @@ -99,8 +100,10 @@ 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, + user_id: model.user.id, access_level: model.human_access, project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase } @@ -111,6 +114,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/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 81337432ab786ccbf6b1911c2425b0829bae091d..83f6814d822e23ebbe3eb0c65c561c79aa624fba 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -202,6 +202,13 @@ .help-block A method call is only tracked when it takes longer to complete than the given amount of milliseconds. + .form-group + = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :metrics_sample_interval, class: 'form-control' + .help-block + The sampling interval in seconds. Sampled data includes memory usage, + retained Ruby objects, file descriptors and so on. %fieldset %legend Spam and Anti-bot Protection diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml index d67454c03e797b2c5f9c9a8e37863456de92e4d1..3c57e3dc174271b1259a54d3f30ecd6361b11f5f 100644 --- a/app/views/admin/labels/index.html.haml +++ b/app/views/admin/labels/index.html.haml @@ -1,5 +1,5 @@ - page_title "Labels" -= link_to new_admin_label_path, class: "pull-right btn btn-new" do += link_to new_admin_label_path, class: "pull-right btn btn-nr btn-new" do New label %h3.page-title Labels diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 0c986af4a958b26529dd44f1c677e5581bcd7d3b..d734e60682a31fa1a1d88c559ce1bdd4d6dfa424 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -1,7 +1,7 @@ - page_title @project.name_with_namespace, "Projects" %h3.page-title Project: #{@project.name_with_namespace} - = link_to edit_project_path(@project), class: "btn pull-right" do + = link_to edit_project_path(@project), class: "btn btn-nr pull-right" do %i.fa.fa-pencil-square-o Edit %hr diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml index 5e17b018163d05f47b86f658b3aa50b0aa7993e2..bc44b1b1d8e2e724c4b724bbd2a756c2512e094f 100644 --- a/app/views/admin/users/_head.html.haml +++ b/app/views/admin/users/_head.html.haml @@ -7,8 +7,8 @@ .pull-right - unless @user == current_user || @user.blocked? - = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info" - = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do + = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-nr btn-grouped btn-info" + = link_to edit_admin_user_path(@user), class: "btn btn-nr btn-grouped" do %i.fa.fa-pencil-square-o Edit %hr diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 2c2450d4117ae3cc76a677dc72c16634296fb999..2bdbae195880edfd7a83c81d57268d24fcd6403a 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -70,6 +70,14 @@ %strong.cred No + %li + %span.light Current sign-in IP: + %strong + - if @user.current_sign_in_ip + = @user.current_sign_in_ip + - else + never + %li %span.light Current sign-in at: %strong @@ -78,6 +86,14 @@ - else never + %li + %span.light Last sign-in IP: + %strong + - if @user.last_sign_in_ip + = @user.last_sign_in_ip + - else + never + %li %span.light Last sign-in at: %strong diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml index 4316c358dcb81c2aa0f87e490b95499d529b8156..49a558e8ac9a9f469a1f343cec8705b8bf71fdbc 100644 --- a/app/views/dashboard/milestones/show.html.haml +++ b/app/views/dashboard/milestones/show.html.haml @@ -16,7 +16,7 @@ - if @milestone.complete? && @milestone.active? .alert.alert-success.prepend-top-default - %span All issues for this milestone are closed. You may close the milestone now. + %span All issues for this milestone are closed. Navigate to the project to close the milestone. .table-holder %table.table diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml index 3361d7e2a8d279fb6ffaed64de52c00615f74c2d..e7ab4f2409bb941ffb9bd020891200fb7496e53f 100644 --- a/app/views/groups/group_members/_new_group_member.html.haml +++ b/app/views/groups/group_members/_new_group_member.html.haml @@ -4,7 +4,7 @@ .col-sm-10 = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true) .help-block - Search for existing users or invite new ones using their email address. + Search for users by name, username, or email, or invite new ones using their email address. .form-group = f.label :access_level, "Group Access", class: 'control-label' diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml index de415ae51a41d1979f408fc8102716dfd2c98f06..38469ed4774c950c0cb4e88c18c377ef806bb6e4 100644 --- a/app/views/projects/issues/_closed_by_box.html.haml +++ b/app/views/projects/issues/_closed_by_box.html.haml @@ -1,2 +1,4 @@ -.issue-closed-by-widget.gray-content-block.second-block.white - This issue will be closed automatically when merge request #{markdown(merge_requests_sentence(@closed_by_merge_requests), pipeline: :gfm)} is accepted. +.issue-closed-by-widget.second-block + - pluralized_mr_this = merge_request_count > 1 ? "these" : "this" + - pluralized_mr_is = merge_request_count > 1 ? "are" : "is" + When #{pluralized_mr_this} merge #{"request".pluralize(merge_request_count)} #{pluralized_mr_is} accepted, this issue will be closed automatically. diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml index dc434cf38c4ab006432903456cfd5dace77a4865..673020a4e3010cddf63cdb0d3ec415baf8e2180a 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' diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml index 254968e4f678f7850a25b3c74d33a77dd8b90531..640a1962ffc6e941d0d91fe46242a4a7b646ae74 100644 --- a/app/views/projects/issues/_merge_requests.html.haml +++ b/app/views/projects/issues/_merge_requests.html.haml @@ -1,7 +1,7 @@ -if @merge_requests.any? %h2.merge-requests-title = pluralize(@merge_requests.count, 'Related Merge Request') - %ul.bordered-list + %ul.unstyled-list - has_any_ci = @merge_requests.any?(&:ci_commit) - @merge_requests.each do |merge_request| %li @@ -11,7 +11,7 @@ - elsif has_any_ci = icon('blank fw') %span.merge-request-id - \##{merge_request.iid} + \!#{merge_request.iid} %span.merge-request-info %strong = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title" @@ -24,3 +24,5 @@ MERGED - elsif merge_request.closed? CLOSED + - if @closed_by_merge_requests.present? + = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count} diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index f931a0d3b92bcdfc6f2bfb8830f6f23ebfe43ada..7ed898ce72fbc6f1997baa09b4a91a70a8de0225 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -53,9 +53,6 @@ .gray-content-block.second-block.oneline-block = render 'votes/votes_block', votable: @issue - - if @closed_by_merge_requests.present? - = render 'projects/issues/closed_by_box' - .row %section.col-md-9 .issuable-discussion diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml index bff3c3b283ddf33a3fee9104980120eb62136021..1c7de94acfdf426840494390098c1a5c12de96c6 100644 --- a/app/views/projects/merge_requests/_discussion.html.haml +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -1,8 +1,8 @@ - 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" + = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment 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 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" #notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index 3ccda1b381caf9a27210399683ece84899fe49d8..5d78652befa6b1a5bf84cad2509a3c132beb900a 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -6,5 +6,5 @@ = render 'projects/notes/hints' .note-form-actions - = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button' - = link_to 'Cancel', '#', class: 'btn btn-cancel note-edit-cancel' + = f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button' + = link_to 'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel' diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml index acb6dc52a8e204f7fecfc7d2f70eeabe844041dc..f10a4145d623b2e5d5b2e8d6cba0d3605a2258ba 100644 --- a/app/views/projects/notes/_form.html.haml +++ b/app/views/projects/notes/_form.html.haml @@ -15,4 +15,4 @@ .note-form-actions.clearfix = f.submit 'Add Comment', class: "btn btn-nr btn-create comment-btn btn-grouped js-comment-button" = yield(:note_actions) - %a.btn.btn-cancel.js-close-discussion-note-form Cancel + %a.btn.btn-nr.btn-cancel.js-close-discussion-note-form Cancel diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index d708b01a114873fda0195d43cc4cf09d29a45018..f0f3bb3c177eab2d04f5b1f01ef0135f4b6c4fb2 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -4,7 +4,7 @@ .col-sm-10 = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true) .help-block - Search for existing users or invite new ones using their email address. + Search for users by name, username, or email, or invite new ones using their email address. .form-group = f.label :access_level, "Project Access", class: 'control-label' diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index a9c5b2caf0a3f97d426c1553ed80c52488dd4bb3..d625a909bf1df174c3a1186739513a4c9b0f4b49 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -11,7 +11,7 @@ class Settings < Settingslogic # get host without www, thanks to http://stackoverflow.com/a/6674363/1233435 def get_host_without_www(url) - url = URI.encode(url) + url = CGI.escape(url) uri = URI.parse(url) uri = URI.parse("http://#{url}") if uri.scheme.nil? host = uri.host.downcase diff --git a/config/initializers/haml.rb b/config/initializers/haml.rb index 1516476815a56e96907396b699a59de3b2f931c4..7e8ddb3716bad55ceaa5fef99c9f2334c8afa07e 100644 --- a/config/initializers/haml.rb +++ b/config/initializers/haml.rb @@ -1,7 +1 @@ Haml::Template.options[:ugly] = true - -# Remove the `:coffee` and `:coffeescript` filters -# -# See https://git.io/vztMu and http://stackoverflow.com/a/17571242/223897 -Haml::Filters.remove_filter('coffee') -Haml::Filters.remove_filter('coffeescript') diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb new file mode 100644 index 0000000000000000000000000000000000000000..b741f5d2c758db6ff093b3d55e995458f8d50fbb --- /dev/null +++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb @@ -0,0 +1,6 @@ +class AddMetricsSampleInterval < ActiveRecord::Migration + def change + add_column :application_settings, :metrics_sample_interval, :integer, + default: 15 + end +end diff --git a/db/schema.rb b/db/schema.rb index 2ded8a45e188f523220a5d2eb34f520a61afbfd2..ecbe575bf83e1619b631bf509afb625a5871fba4 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: 20160106164438) do +ActiveRecord::Schema.define(version: 20160113111034) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -32,23 +32,23 @@ ActiveRecord::Schema.define(version: 20160106164438) do t.text "sign_in_text" t.datetime "created_at" t.datetime "updated_at" - t.string "home_page_url", limit: 255 - t.integer "default_branch_protection", default: 2 - t.boolean "twitter_sharing_enabled", default: true + t.string "home_page_url" + 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", limit: 255 - t.integer "session_expire_delay", default: 10080, null: false + t.boolean "user_oauth_applications", default: true + t.string "after_sign_out_path" + t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" t.text "help_page_text" - 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 "admin_notification_email" + t.boolean "shared_runners_enabled", default: true, null: false + t.integer "max_artifacts_size", default: 100, null: false t.string "runners_registration_token" t.boolean "require_two_factor_authentication", default: false t.integer "two_factor_grace_period", default: 48 @@ -60,14 +60,15 @@ ActiveRecord::Schema.define(version: 20160106164438) do 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 + t.integer "metrics_sample_interval", default: 15 end create_table "audit_events", force: :cascade do |t| - 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.integer "author_id", null: false + t.string "type", null: false + t.integer "entity_id", null: false + t.string "entity_type", null: false t.text "details" t.datetime "created_at" t.datetime "updated_at" @@ -78,14 +79,14 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.string "font", limit: 255 + t.string "color" + t.string "font" end create_table "ci_application_settings", force: :cascade do |t| @@ -97,7 +98,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "ci_builds", force: :cascade do |t| t.integer "project_id" - t.string "status", limit: 255 + t.string "status" t.datetime "finished_at" t.text "trace" t.datetime "created_at" @@ -108,19 +109,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do t.integer "commit_id" t.text "commands" t.integer "job_id" - t.string "name", limit: 255 - t.boolean "deploy", default: false + t.string "name" + t.boolean "deploy", default: false t.text "options" - t.boolean "allow_failure", default: false, null: false - t.string "stage", limit: 255 + t.boolean "allow_failure", default: false, null: false + t.string "stage" t.integer "trigger_request_id" t.integer "stage_idx" t.boolean "tag" - t.string "ref", limit: 255 + t.string "ref" t.integer "user_id" - t.string "type", limit: 255 - t.string "target_url", limit: 255 - t.string "description", limit: 255 + t.string "type" + t.string "target_url" + t.string "description" t.text "artifacts_file" t.integer "gl_project_id" end @@ -139,13 +140,13 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "ci_commits", force: :cascade do |t| t.integer "project_id" - t.string "ref", limit: 255 - t.string "sha", limit: 255 - t.string "before_sha", limit: 255 + t.string "ref" + t.string "sha" + t.string "before_sha" 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" @@ -172,16 +173,16 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", 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.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.datetime "deleted_at" end @@ -189,25 +190,25 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.integer "timeout", default: 3600, null: false + t.string "name" + t.integer "timeout", default: 3600, null: false t.datetime "created_at" t.datetime "updated_at" - 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.string "token" + t.string "default_ref" + t.string "path" + 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", limit: 255 + t.boolean "public", default: false, null: false + t.string "ssh_url_to_repo" t.integer "gitlab_id" - 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.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.text "generated_yaml_config" end @@ -226,34 +227,34 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 + t.string "token" t.datetime "created_at" t.datetime "updated_at" - t.string "description", limit: 255 + t.string "description" t.datetime "contacted_at" - 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 + 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" end create_table "ci_services", force: :cascade do |t| - t.string "type", limit: 255 - t.string "title", limit: 255 - t.integer "project_id", null: false + t.string "type" + t.string "title" + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false + t.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", limit: 255, null: false + t.string "session_id", null: false t.text "data" t.datetime "created_at" t.datetime "updated_at" @@ -265,9 +266,9 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "ci_taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type", limit: 255 + t.string "taggable_type" t.integer "tagger_id" - t.string "tagger_type", limit: 255 + t.string "tagger_type" t.string "context", limit: 128 t.datetime "created_at" end @@ -276,8 +277,8 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.integer "taggings_count", default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree @@ -291,7 +292,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do end create_table "ci_triggers", force: :cascade do |t| - t.string "token", limit: 255 + t.string "token" t.integer "project_id" t.datetime "deleted_at" t.datetime "created_at" @@ -304,19 +305,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "ci_variables", force: :cascade do |t| t.integer "project_id" - t.string "key", limit: 255 + t.string "key" t.text "value" t.text "encrypted_value" - t.string "encrypted_value_salt", limit: 255 - t.string "encrypted_value_iv", limit: 255 + t.string "encrypted_value_salt" + t.string "encrypted_value_iv" 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", limit: 255, null: false - t.integer "project_id", null: false + t.string "url", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -331,8 +332,8 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255, null: false + t.integer "user_id", null: false + t.string "email", null: false t.datetime "created_at" t.datetime "updated_at" end @@ -341,9 +342,9 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 + t.string "target_type" t.integer "target_id" - t.string "title", limit: 255 + t.string "title" t.text "data" t.integer "project_id" t.datetime "created_at" @@ -369,8 +370,8 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.string "provider", limit: 255 + t.string "extern_uid" + t.string "provider" t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -380,17 +381,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree create_table "issues", force: :cascade do |t| - t.string "title", limit: 255 + t.string "title" 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", limit: 255 + t.integer "position", default: 0 + t.string "branch_name" t.text "description" t.integer "milestone_id" - t.string "state", limit: 255 + t.string "state" t.integer "iid" t.integer "updated_by_id" end @@ -410,10 +411,10 @@ ActiveRecord::Schema.define(version: 20160106164438) do t.datetime "created_at" t.datetime "updated_at" t.text "key" - t.string "title", limit: 255 - t.string "type", limit: 255 - t.string "fingerprint", limit: 255 - t.boolean "public", default: false, null: false + t.string "title" + t.string "type" + t.string "fingerprint" + t.boolean "public", default: false, null: false end add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree @@ -422,7 +423,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "label_links", force: :cascade do |t| t.integer "label_id" t.integer "target_id" - t.string "target_type", limit: 255 + t.string "target_type" t.datetime "created_at" t.datetime "updated_at" end @@ -431,22 +432,22 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.string "color", limit: 255 + t.string "title" + t.string "color" 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", limit: 255, null: false - t.integer "size", null: false + t.string "oid", null: false + t.integer "size", null: false t.datetime "created_at" t.datetime "updated_at" - t.string "file", limit: 255 + t.string "file" end add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree @@ -461,17 +462,17 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255, null: false + t.integer "access_level", null: false + t.integer "source_id", null: false + t.string "source_type", null: false t.integer "user_id" - t.integer "notification_level", null: false - t.string "type", limit: 255 + t.integer "notification_level", null: false + t.string "type" t.datetime "created_at" t.datetime "updated_at" t.integer "created_by_id" - t.string "invite_email", limit: 255 - t.string "invite_token", limit: 255 + t.string "invite_email" + t.string "invite_token" t.datetime "invite_accepted_at" end @@ -483,10 +484,10 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 + t.string "state" 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 @@ -494,26 +495,26 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255, null: false - t.string "source_branch", limit: 255, null: false - t.integer "source_project_id", null: false + t.string "target_branch", null: false + t.string "source_branch", null: false + t.integer "source_project_id", null: false t.integer "author_id" t.integer "assignee_id" - t.string "title", limit: 255 + t.string "title" t.datetime "created_at" t.datetime "updated_at" t.integer "milestone_id" - t.string "state", limit: 255 - t.string "merge_status", limit: 255 - t.integer "target_project_id", null: false + t.string "state" + t.string "merge_status" + 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", limit: 255 + t.string "merge_error" 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 @@ -529,13 +530,13 @@ ActiveRecord::Schema.define(version: 20160106164438) do add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree create_table "milestones", force: :cascade do |t| - t.string "title", limit: 255, null: false - t.integer "project_id", null: false + t.string "title", 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", limit: 255 + t.string "state" t.integer "iid" end @@ -543,16 +544,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree + add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree create_table "namespaces", force: :cascade do |t| - t.string "name", limit: 255, null: false - t.string "path", limit: 255, 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", limit: 255 - t.string "description", limit: 255, default: "", null: false - t.string "avatar", limit: 255 + t.string "type" + t.string "description", default: "", null: false + t.string "avatar" end add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree @@ -563,19 +565,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "notes", force: :cascade do |t| t.text "note" - t.string "noteable_type", limit: 255 + t.string "noteable_type" t.integer "author_id" t.datetime "created_at" t.datetime "updated_at" t.integer "project_id" - t.string "attachment", limit: 255 - t.string "line_code", limit: 255 - t.string "commit_id", limit: 255 + t.string "attachment" + t.string "line_code" + t.string "commit_id" 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 @@ -591,14 +593,14 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255, 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", 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", limit: 255 + t.string "scopes" end add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree @@ -606,12 +608,12 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "oauth_access_tokens", force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" - t.string "token", limit: 255, null: false - t.string "refresh_token", limit: 255 + t.string "token", null: false + t.string "refresh_token" t.integer "expires_in" t.datetime "revoked_at" - t.datetime "created_at", null: false - t.string "scopes", limit: 255 + t.datetime "created_at", null: false + t.string "scopes" end add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree @@ -619,15 +621,15 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", 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.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.datetime "created_at" t.datetime "updated_at" t.integer "owner_id" - t.string "owner_type", limit: 255 + t.string "owner_type" end add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree @@ -639,39 +641,39 @@ ActiveRecord::Schema.define(version: 20160106164438) do end create_table "projects", force: :cascade do |t| - t.string "name", limit: 255 - t.string "path", limit: 255 + t.string "name" + t.string "path" 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", limit: 255, default: "gitlab", null: false - t.string "issues_tracker_id", limit: 255 - t.boolean "snippets_enabled", default: true, null: false + t.string "issues_tracker", default: "gitlab", null: false + t.string "issues_tracker_id" + t.boolean "snippets_enabled", default: true, null: false t.datetime "last_activity_at" - 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.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.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 @@ -687,17 +689,17 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255, null: false + t.integer "project_id", null: false + t.string "name", 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", limit: 255 + t.string "tag" t.text "description" t.integer "project_id" t.datetime "created_at" @@ -710,30 +712,30 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "sent_notifications", force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" - t.string "noteable_type", limit: 255 + t.string "noteable_type" t.integer "recipient_id" - t.string "commit_id", limit: 255 - t.string "reply_key", limit: 255, null: false - t.string "line_code", limit: 255 + t.string "commit_id" + t.string "reply_key", null: false + t.string "line_code" 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", limit: 255 - t.string "title", limit: 255 + t.string "type" + t.string "title" 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 @@ -741,16 +743,16 @@ ActiveRecord::Schema.define(version: 20160106164438) do add_index "services", ["template"], name: "index_services_on_template", using: :btree create_table "snippets", force: :cascade do |t| - t.string "title", limit: 255 + t.string "title" 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", limit: 255 + t.string "file_name" t.datetime "expires_at" - t.string "type", limit: 255 - t.integer "visibility_level", default: 0, null: false + t.string "type" + t.integer "visibility_level", default: 0, null: false end add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree @@ -763,7 +765,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "subscriptions", force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" - t.string "subscribable_type", limit: 255 + t.string "subscribable_type" t.boolean "subscribed" t.datetime "created_at" t.datetime "updated_at" @@ -774,10 +776,10 @@ ActiveRecord::Schema.define(version: 20160106164438) do create_table "taggings", force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" - t.string "taggable_type", limit: 255 + t.string "taggable_type" t.integer "tagger_id" - t.string "tagger_type", limit: 255 - t.string "context", limit: 255 + t.string "tagger_type" + t.string "context" t.datetime "created_at" end @@ -785,67 +787,67 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 - t.integer "taggings_count", default: 0 + t.string "name" + t.integer "taggings_count", default: 0 end add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree create_table "users", force: :cascade do |t| - 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.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", 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.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.datetime "locked_at" - 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.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.datetime "password_expires_at" t.integer "created_by_id" t.datetime "last_credential_check_at" - t.string "avatar", limit: 255 - t.string "confirmation_token", limit: 255 + t.string "avatar" + t.string "confirmation_token" t.datetime "confirmed_at" t.datetime "confirmation_sent_at" - 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.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.text "otp_backup_codes" - t.string "public_email", limit: 255, 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 @@ -872,19 +874,19 @@ ActiveRecord::Schema.define(version: 20160106164438) 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", limit: 255 + t.string "url" t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" - t.string "type", limit: 255, default: "ProjectHook" + t.string "type", 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 diff --git a/doc/README.md b/doc/README.md index 25fe3abcb9a6243c4488589931fa39f19ddcabfd..7d4f84857e001211ad6cc3ec4b17a5b2af1398bc 100644 --- a/doc/README.md +++ b/doc/README.md @@ -70,6 +70,8 @@ ## Contributor documentation +- [Documentation styleguide](development/doc_styleguide.md) Use this styleguide if you are + contributing to documentation. - [Development](development/README.md) Explains the architecture and the guidelines for shell commands. - [Legal](legal/README.md) Contributor license agreements. - [Release](release/README.md) How to make the monthly and security releases. diff --git a/doc/api/projects.md b/doc/api/projects.md index 37d74216c1b5a7850fd1ae7d05017e4fb2ab2d23..241229221db2d8de459a0af4739eb8ef8fb92c15 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -76,7 +76,10 @@ 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 }, { "id": 6, @@ -129,7 +132,11 @@ Parameters: } }, "archived": false, - "avatar_url": null + "avatar_url": null, + "shared_runners_enabled": true, + "forks_count": 0, + "star_count": 0, + "runners_token": "b8547b1dc37721d05889db52fa2f02" } ] ``` @@ -244,7 +251,11 @@ 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, + "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b" } ``` diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md new file mode 100644 index 0000000000000000000000000000000000000000..0bd32b7820169e87fe7dd632a63f7958f5fcb8ac --- /dev/null +++ b/doc/development/doc_styleguide.md @@ -0,0 +1,231 @@ +# Documentation styleguide + +This styleguide recommends best practices to improve documentation and to keep +it organized and easy to find. + +## Naming + +- When creating a new document and it has more than one word in its name, + make sure to use underscores instead of spaces or dashes (`-`). For example, + a proper naming would be `import_projects_from_github.md`. The same rule + applies to images. + +## Text + +- Split up long lines, this makes it much easier to review and edit. Only + double line breaks are shown as a full line break in [GitLab markdown][gfm]. + 80-100 characters is a good line length +- Make sure that the documentation is added in the correct directory and that + there's a link to it somewhere useful +- Do not duplicate information +- Be brief and clear +- Unless there's a logical reason not to, add documents in alphabetical order +- Write in US English +- Use [single spaces][] instead of double spaces + +## Formatting + +- Use dashes (`-`) for unordered lists instead of asterisks (`*`) +- Use the number one (`1`) for ordered lists +- Use underscores (`_`) to mark a word or text in italics +- Use double asterisks (`**`) to mark a word or text in bold +- When using lists, prefer not to end each item with a period. You can use + them if there are multiple sentences, just keep the last sentence without + a period + +## Headings + +- Add only one H1 title in each document, by adding `#` at the beginning of + it (when using markdown). For subheadings, use `##`, `###` and so on +- Avoid putting numbers in headings. Numbers shift, hence documentation anchor + links shift too, which eventually leads to dead links. If you think it is + compelling to add numbers in headings, make sure to at least discuss it with + someone in the Merge Request +- When introducing a new document, be careful for the headings to be + grammatically and syntactically correct. It is advised to mention one or all + of the following GitLab members for a review: `@axil`, `@rspeicher`, + `@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document + with wrong heading is going live without an audit, thus preventing dead links + and redirection issues when corrected +- Leave exactly one newline after a heading + +## Links + +- If a link makes the paragraph to span across multiple lines, do not use + the regular Markdown approach: `[Text](https://example.com)`. Instead use + `[Text][identifier]` and at the very bottom of the document add: + `[identifier]: https://example.com`. This is another way to create Markdown + links which keeps the document clear and concise. Bonus points if you also + add an alternative text: `[identifier]: https://example.com "Alternative text"` + that appears when hovering your mouse on a link + +## Images + +- Place images in a separate directory named `img/` in the same directory where + the `.md` document that you're working on is located. Always prepend their + names with the name of the document that they will be included in. For + example, if there is a document called `twitter.md`, then a valid image name + could be `twitter_login_screen.png`. +- Images should have a specific, non-generic name that will differentiate them. +- Keep all file names in lower case. +- Consider using PNG images instead of JPEG. + +Inside the document: + +- The Markdown way of using an image inside a document is: + `` +- Always use a proper description for what the image is about. That way, when a + browser fails to show the image, this text will be used as an alternative + description +- If there are consecutive images with little text between them, always add + three dashes (`---`) between the image and the text to create a horizontal + line for better clarity +- If a heading is placed right after an image, always add three dashes (`---`) + between the image and the heading + +## Notes + +- Notes should be in italics with the word `Note:` being bold. Use this form: + `_**Note:** This is something to note._`. If the note spans across multiple + lines it's OK to split the line. + +## New features + +- Every piece of documentation that comes with a new feature should declare the + GitLab version that feature got introduced. Right below the heading add a + note: `_**Note:** This feature was introduced in GitLab 8.3_` +- If possible every feature should have a link to the MR that introduced it. + The above note would be then transformed to: + `_**Note:** This feature was [introduced][ce-1242] in GitLab 8.3_`, where + the [link identifier](#links) is named after the repository (CE) and the MR + number +- If the feature is only in GitLab EE, don't forget to mention it, like: + `_**Note:** This feature was introduced in GitLab EE 8.3_`. Otherwise, leave + this mention out + +## API + +Here is a list of must-have items. Use them in the exact order that appears +on this document. Further explanation is given below. + +- Every method must have the REST API request. For example: + + ``` + GET /projects/:id/repository/branches + ``` + +- Every method must have a detailed + [description of the parameters](#method-description). +- Every method must have a cURL example. +- Every method must have a response body (in JSON format). + +### Method description + +Use the following table headers to describe the methods. Attributes should +always be in code blocks using backticks (`). + +``` +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +``` + +Rendered example: + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `user` | string | yes | The GitLab username | + +### cURL commands + +- Use `https://gitlab.example.com/api/v3/` as an endpoint. +- Wherever needed use this private token: `9koXpg98eAheJpvBs5tK`. +- Always put the request first. `GET` is the default so you don't have to + include it. +- Use double quotes to the URL when it includes additional parameters. +- Prefer to use examples using the private token and don't pass data of + username and password. + +| Methods | Description | +| ------- | ----------- | +| `-H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK"` | Use this method as is, whenever authentication needed | +| `-X POST` | Use this method when creating new objects | +| `-X PUT` | Use this method when updating existing objects | +| `-X DELETE` | Use this method when removing existing objects | + +### cURL Examples + +Below is a set of [cURL][] examples that you can use in the API documentation. + +#### Simple cURL command + +Get the details of a group: + +```bash +curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/gitlab-org +``` + +#### cURL example with parameters passed in the URL + +Create a new project under the authenticated user's namespace: + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects?name=foo" +``` + +#### Post data using cURL's --data + +Instead of using `-X POST` and appending the parameters to the URI, you can use +cURL's `--data` option. The example below will create a new project `foo` under +the authenticated user's namespace. + +```bash +curl --data "name=foo" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects" +``` + +#### Post data using JSON content + +_**Note:** In this example we create a new group. Watch carefully the single +and double quotes._ + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v3/groups +``` + +#### Post data using form-data + +Instead of using JSON or urlencode you can use multipart/form-data which +properly handles data encoding: + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -F "title=ssh-key" -F "key=ssh-rsa AAAAB3NzaC1yc2EA..." https://gitlab.example.com/api/v3/users/25/keys +``` + +The above example is run by and administrator and will add an SSH public key +titled ssh-key to user's account which has an id of 25. + +#### Escape special characters + +Spaces or slashes (`/`) may sometimes result to errors, thus it is recommended +to escape them when possible. In the example below we create a new issue which +contains spaces in its title. Observe how spaces are escaped using the `%20` +ASCII code. + +```bash +curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/42/issues?title=Hello%20Dude" +``` + +Use `%2F` for slashes (`/`). + +#### Pass arrays to API calls + +The GitLab API sometimes accepts arrays of strings or integers. For example, to +restrict the sign-up e-mail domains of a GitLab instance to `*.example.com` and +`example.net`, you would do something like this: + +```bash +curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -d "restricted_signup_domains[]=*.example.com" -d "restricted_signup_domains[]=example.net" https://gitlab.example.com/api/v3/application/settings +``` + +[cURL]: http://curl.haxx.se/ "cURL website" +[single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html +[gfm]: http://doc.gitlab.com/ce/markdown/markdown.html#newlines "GitLab flavored markdown documentation" diff --git a/doc/install/installation.md b/doc/install/installation.md index 8c4e092c636b48efb0f6e70d00c476aefa486b9d..e645445df2ac5aca458c3cc201d1ed6a724e1b99 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -231,9 +231,9 @@ sudo usermod -aG redis git ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-3-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-4-stable gitlab -**Note:** You can change `8-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `8-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It @@ -348,7 +348,7 @@ GitLab Shell is an SSH access and repository management software developed speci cd /home/git sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git cd gitlab-workhorse - sudo -u git -H git checkout 0.5.3 + sudo -u git -H git checkout 0.5.4 sudo -u git -H make ### Initialize Database and Activate Advanced Features diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md index 6258e5f1030980980b9188cebb2d2c2031fc554a..a0be3dd4e5cc3f3ee63cf84e1dd966aa639fbfbd 100644 --- a/doc/integration/shibboleth.md +++ b/doc/integration/shibboleth.md @@ -1,8 +1,8 @@ # Shibboleth OmniAuth Provider -This documentation is for enabling shibboleth with gitlab-omnibus package. +This documentation is for enabling shibboleth with omnibus-gitlab package. -In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. +In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in omnibus-gitlab package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. To enable the Shibboleth OmniAuth provider you must: diff --git a/doc/release/patch.md b/doc/release/patch.md index 3022e375acac13590d1a959043fd2dafd1a2705a..1c921439156aa118ec7895cfa179ba99db104b8d 100644 --- a/doc/release/patch.md +++ b/doc/release/patch.md @@ -24,7 +24,7 @@ Use the following template: - Picked into respective `stable` branches: - [ ] Merge `x-y-stable` into `x-y-stable-ee` - [ ] release-tools: `x.y.z` -- gitlab-omnibus +- omnibus-gitlab - [ ] `x.y.z+ee.0` - [ ] `x.y.z+ce.0` - [ ] Deploy diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md index 49f98ded0464ee94b11848c899b9131d535f3690..612376e3a49a5515967f810bf38ab5e5e8acaaf6 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", } @@ -129,6 +131,7 @@ X-Gitlab-Event: System Hook "email": "js@gitlabhq.com", "event_name": "user_create", "name": "John Smith", + "username": "js", "user_id": 41 } ``` @@ -142,6 +145,7 @@ X-Gitlab-Event: System Hook "email": "js@gitlabhq.com", "event_name": "user_destroy", "name": "John Smith", + "username": "js", "user_id": 41 } ``` @@ -215,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 } ``` @@ -231,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/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md index a3950df5fef96fd0c1888012e0dd8651133ef520..2ca4e1f37702308a506594766e9c70221605be6e 100644 --- a/doc/update/8.2-to-8.3.md +++ b/doc/update/8.2-to-8.3.md @@ -78,7 +78,7 @@ which should already be on your system from GitLab 8.1. ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch --all -sudo -u git -H git checkout 0.5.3 +sudo -u git -H git checkout 0.5.4 sudo -u git -H make ``` diff --git a/doc/update/8.3-to-8.4.md b/doc/update/8.3-to-8.4.md new file mode 100644 index 0000000000000000000000000000000000000000..1cbeab3eca36fecccbf0ee5b15f029f42e1ddaf1 --- /dev/null +++ b/doc/update/8.3-to-8.4.md @@ -0,0 +1,148 @@ +# From 8.3 to 8.4 + +### 1. Stop server + + sudo service gitlab stop + +### 2. Backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production +``` + +### 3. Get latest code + +```bash +sudo -u git -H git fetch --all +sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically +``` + +For GitLab Community Edition: + +```bash +sudo -u git -H git checkout 8-4-stable +``` + +OR + +For GitLab Enterprise Edition: + +```bash +sudo -u git -H git checkout 8-4-stable-ee +``` + +### 4. Update gitlab-shell + +```bash +cd /home/git/gitlab-shell +sudo -u git -H git fetch --all +sudo -u git -H git checkout v2.6.9 +``` + +### 5. Update gitlab-workhorse + +Install and compile gitlab-workhorse. This requires [Go 1.5](https://golang.org/dl) +which should already be on your system from GitLab 8.1. + +```bash +cd /home/git/gitlab-workhorse +sudo -u git -H git fetch --all +sudo -u git -H git checkout 0.5.4 +sudo -u git -H make +``` + +### 6. Install libs, migrations, etc. + +```bash +cd /home/git/gitlab + +# MySQL installations (note: the line below states '--without postgres') +sudo -u git -H bundle install --without postgres development test --deployment + +# PostgreSQL installations (note: the line below states '--without mysql') +sudo -u git -H bundle install --without mysql development test --deployment + +# Run database migrations +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production + +# Clean up assets and cache +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production + +``` + +### 7. Update configuration files + +#### New configuration options for `gitlab.yml` + +There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: + +```sh +git diff origin/8-3-stable:config/gitlab.yml.example origin/8-4-stable:config/gitlab.yml.example +``` + +#### Nginx configuration + +GitLab 8.3 introduced major changes in the NGINX configuration. Ensure you're +still up-to-date with the latest changes: + +```sh +# For HTTPS configurations +git diff origin/8-3-stable:lib/support/nginx/gitlab-ssl origin/8-4-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/8-3-stable:lib/support/nginx/gitlab origin/8-4-stable:lib/support/nginx/gitlab +``` + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-workhorse listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/lib/support/init.d/gitlab.default.example#L34 + +#### Init script + +We updated the init script for GitLab in order to pass new +configuration options to gitlab-workhorse. We let gitlab-workhorse +connect to the Rails application via a Unix domain socket and we tell +it where the 'public' directory of GitLab is. + +``` +cd /home/git/gitlab +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab +``` + +### 8. Start application + + sudo service gitlab start + sudo service nginx restart + +### 9. Check application status + +Check if GitLab and its environment are configured correctly: + + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production + +To make sure you didn't miss anything run a more thorough check: + + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production + +If all items are green, then congratulations, the upgrade is complete! + +## Things went south? Revert to previous version (8.3) + +### 1. Revert the code to the previous version + +Follow the [upgrade guide from 8.2 to 8.3](8.2-to-8.3.md), except for the +database migration (the backup is already migrated to the previous version). + +### 2. Restore from the backup + +```bash +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production +``` + +If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index c19ee49f9e035bdcdfff57a3b913d3570c6b6d93..a10e62877ba89de5a1228006fff608162115d1d0 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. diff --git a/doc_styleguide.md b/doc_styleguide.md index cceb449a8543ac1155a61afaed5b0a0405645937..05ff46323ac721e46b085b9e65d76a8f9083fc27 100644 --- a/doc_styleguide.md +++ b/doc_styleguide.md @@ -1,26 +1,3 @@ # Documentation styleguide -This styleguide recommends best practices to improve documentation and to keep it organized and easy to find. - -## Text - -- Split up long lines, this makes it much easier to review and edit. Only -double line breaks are shown as a full line break in markdown. 80 characters -is a good line length. -- For subtitles, make sure to start with the largest and go down, meaning: -`#` for the title, `##` for subtitles and `###` for subtitles of the subtitles, etc. -- Make sure that the documentation is added in the correct directory and that there's a link to it somewhere useful. -- Add only one H1 or title in each document, by adding '#' at the begining of it (when using markdown). -For subtitles, use '##', '###' and so on. -- Do not duplicate information. -- Be brief and clear. -- Whenever it applies, add documents in alphabetical order. -- Write in US English -- Use [single spaces](http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html) instead of double spaces. - -## Images - -- Create a directory to store the images with the specific name of the document where the images belong. -It could be in the same directory where the .md document that you're working on is located. -- Images should have a specific, non-generic name that will differentiate them. -- Keep all file names in lower case. \ No newline at end of file +Moved to [development/doc_styleguide](doc/development/doc_styleguide.md). diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 26e7c956e8f79f2bc23ba4650c9e64693d1e75b0..e3bc3316ce56cdad022a513c246e532e446b8e5b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -71,6 +71,7 @@ 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 :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } end class ProjectMember < UserBasic diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 8b1390e3289ec8d8db1e55f72b4fd880089cf106..71bb342f8448cacd216acf55fb80a0a56265242b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -69,7 +69,8 @@ module API # Example Request: # GET /projects/:id get ":id" do - present user_project, with: Entities::ProjectWithAccess, user: current_user + present user_project, with: Entities::ProjectWithAccess, user: current_user, + user_can_admin_project: can?(current_user, :admin_project, user_project) end # Get events for a single project @@ -118,7 +119,8 @@ module API attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(current_user, attrs).execute if @project.saved? - present @project, with: Entities::Project + present @project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, @project) else if @project.errors[:limit_reached].present? error!(@project.errors[:limit_reached], 403) @@ -163,7 +165,8 @@ module API attrs = map_public_to_visibility_level(attrs) @project = ::Projects::CreateService.new(user, attrs).execute if @project.saved? - present @project, with: Entities::Project + present @project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, @project) else render_validation_error!(@project) end @@ -182,8 +185,9 @@ module API if @forked_project.errors.any? conflict!(@forked_project.errors.messages) else - present @forked_project, with: Entities::Project - end + present @forked_project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, @forked_project) + end end # Update an existing project @@ -229,7 +233,8 @@ module API if user_project.errors.any? render_validation_error!(user_project) else - present user_project, with: Entities::Project + present user_project, with: Entities::Project, + user_can_admin_project: can?(current_user, :admin_project, user_project) end end diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb index 7198a8b03e2bfbf5f73e85e31a8915b92fbf596a..5dd6d2fe3c7c26cf1b30e7633b5e0d6b122e4d48 100644 --- a/lib/banzai/filter/reference_filter.rb +++ b/lib/banzai/filter/reference_filter.rb @@ -133,7 +133,7 @@ module Banzai next unless link && text - link = URI.decode(link) + link = CGI.unescape(link) # Ignore ending punctionation like periods or commas next unless link == text && text =~ /\A#{pattern}/ @@ -170,7 +170,7 @@ module Banzai text = node.text next unless link && text - link = URI.decode(link) + link = CGI.unescape(link) next unless link && link =~ /\A#{pattern}\z/ html = yield link, text diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb index e24b94d615944df3eee683768bf20d111451cd54..59926084d075a78b70d0ca9c0a87397f142c4a97 100644 --- a/lib/gitlab/gitlab_import/importer.rb +++ b/lib/gitlab/gitlab_import/importer.rb @@ -12,7 +12,7 @@ module Gitlab end def execute - project_identifier = URI.encode(project.import_source, '/') + project_identifier = CGI.escape(project.import_source, '/') #Issues && Comments issues = client.issues(project_identifier) diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb index 44356a0e42cd11f65cb607211dc840e248f1adcf..88a265c6af286662598699ca1bbe565b5a61f3f6 100644 --- a/lib/gitlab/metrics.rb +++ b/lib/gitlab/metrics.rb @@ -13,7 +13,8 @@ module Gitlab timeout: current_application_settings[:metrics_timeout], method_call_threshold: current_application_settings[:metrics_method_call_threshold], host: current_application_settings[:metrics_host], - port: current_application_settings[:metrics_port] + port: current_application_settings[:metrics_port], + sample_interval: current_application_settings[:metrics_sample_interval] || 15 } end @@ -36,20 +37,6 @@ module Gitlab @pool end - # Returns a relative path and line number based on the last application call - # frame. - def self.last_relative_application_frame - frame = caller_locations.find do |l| - l.path.start_with?(RAILS_ROOT) && !l.path.start_with?(METRICS_ROOT) - end - - if frame - return frame.path.sub(PATH_REGEX, ''), frame.lineno - else - return nil, nil - end - end - def self.submit_metrics(metrics) prepared = prepare_metrics(metrics) diff --git a/lib/gitlab/metrics/sampler.rb b/lib/gitlab/metrics/sampler.rb index 1ea425bc904bbe4b1ad6dd43e28ef5aaf83f013c..fc709222a9b748b584b5865c86fda02fccf5dd70 100644 --- a/lib/gitlab/metrics/sampler.rb +++ b/lib/gitlab/metrics/sampler.rb @@ -7,9 +7,14 @@ module Gitlab # statistics, etc. class Sampler # interval - The sampling interval in seconds. - def initialize(interval = 15) - @interval = interval - @metrics = [] + def initialize(interval = Metrics.settings[:sample_interval]) + interval_half = interval.to_f / 2 + + @interval = interval + @interval_steps = (-interval_half..interval_half).step(0.1).to_a + @last_step = nil + + @metrics = [] @last_minor_gc = Delta.new(GC.stat[:minor_gc_count]) @last_major_gc = Delta.new(GC.stat[:major_gc_count]) @@ -26,7 +31,7 @@ module Gitlab Thread.current.abort_on_exception = true loop do - sleep(@interval) + sleep(sleep_interval) sample end @@ -102,6 +107,23 @@ module Gitlab def sidekiq? Sidekiq.server? end + + # Returns the sleep interval with a random adjustment. + # + # The random adjustment is put in place to ensure we: + # + # 1. Don't generate samples at the exact same interval every time (thus + # potentially missing anything that happens in between samples). + # 2. Don't sample data at the same interval two times in a row. + def sleep_interval + while step = @interval_steps.sample + if step != @last_step + @last_step = step + + return @interval + @last_step + end + end + end end end end diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb index 7c0105d543a5f8ff09a5bd82dbcc88012741365f..2e9dd4645e39d84719a7f4be0123d179168b635b 100644 --- a/lib/gitlab/metrics/subscribers/action_view.rb +++ b/lib/gitlab/metrics/subscribers/action_view.rb @@ -33,16 +33,8 @@ module Gitlab def tags_for(event) path = relative_path(event.payload[:identifier]) - tags = { view: path } - file, line = Metrics.last_relative_application_frame - - if file and line - tags[:file] = file - tags[:line] = line - end - - tags + { view: path } end def current_transaction diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb index 86606b1c6d68041f239db943910fd7ffb7ff5735..2578ddc49f46837e92aae2373c7a3f0a488e3cca 100644 --- a/lib/gitlab/metrics/transaction.rb +++ b/lib/gitlab/metrics/transaction.rb @@ -23,20 +23,29 @@ module Gitlab @values = Hash.new(0) @tags = {} @action = action + + @memory_before = 0 + @memory_after = 0 end def duration @finished_at ? (@finished_at - @started_at) * 1000.0 : 0.0 end + def allocated_memory + @memory_after - @memory_before + end + def run Thread.current[THREAD_KEY] = self - @started_at = Time.now + @memory_before = System.memory_usage + @started_at = Time.now yield ensure - @finished_at = Time.now + @memory_after = System.memory_usage + @finished_at = Time.now Thread.current[THREAD_KEY] = nil end @@ -65,7 +74,7 @@ module Gitlab end def track_self - values = { duration: duration } + values = { duration: duration, allocated_memory: allocated_memory } @values.each do |name, value| values[name] = value diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake index ebe516ec879c71e67dff706a173d110ec926c91b..8c63877e51c649b63975bd27d8f51dbfd4377390 100644 --- a/lib/tasks/gitlab/task_helpers.rake +++ b/lib/tasks/gitlab/task_helpers.rake @@ -2,6 +2,8 @@ module Gitlab class TaskAbortedByUserError < StandardError; end end +String.disable_colorization = true unless STDOUT.isatty + namespace :gitlab do # Ask if the user wants to continue diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index efc850eb7050fff526b39c31ce192eed878117b9..30e353148a8749e362cc1fa10935b641cebee0ca 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -285,6 +285,10 @@ describe ApplicationHelper do it 'allows the script tag to be excluded' do expect(element(skip_js: true)).not_to include 'script' end + + it 'converts to Time' do + expect { helper.time_ago_with_tooltip(Date.today) }.not_to raise_error + end end describe 'render_markup' do diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee index b85fadcbe82e0169eba50b89e53632c0d2d7740a..86ba9dd8e9665b9c5db7d0287648123b81a765dd 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', -> spyOn(jQuery, 'ajax').and.callFake (req) -> expect(req.type).toBe('PUT') diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb index 27211350fbee558dcdfa4ed92284aebf03364b9e..38da77adc9f17fe9db4dd60dabb53668b942b1ff 100644 --- a/spec/lib/gitlab/metrics/sampler_spec.rb +++ b/spec/lib/gitlab/metrics/sampler_spec.rb @@ -9,7 +9,7 @@ describe Gitlab::Metrics::Sampler do describe '#start' do it 'gathers a sample at a given interval' do - expect(sampler).to receive(:sleep).with(5) + expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)) expect(sampler).to receive(:sample) expect(sampler).to receive(:loop).and_yield @@ -116,4 +116,24 @@ describe Gitlab::Metrics::Sampler do sampler.add_metric('cats', value: 10) end end + + describe '#sleep_interval' do + it 'returns a Numeric' do + expect(sampler.sleep_interval).to be_a_kind_of(Numeric) + end + + # Testing random behaviour is very hard, so treat this test as a basic smoke + # test instead of a very accurate behaviour/unit test. + it 'does not return the same interval twice in a row' do + last = nil + + 100.times do + interval = sampler.sleep_interval + + expect(interval).to_not eq(last) + + last = interval + 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 05e4fbbeb51226b98167796288965ca1c671dd83..0695c5ce096fca07564bfe53d9397955797cdee6 100644 --- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb @@ -14,19 +14,12 @@ describe Gitlab::Metrics::Subscribers::ActionView do before do allow(subscriber).to receive(:current_transaction).and_return(transaction) - - allow(Gitlab::Metrics).to receive(:last_relative_application_frame). - and_return(['app/views/x.html.haml', 4]) end describe '#render_template' do it 'tracks rendering of a template' do values = { duration: 2.1 } - tags = { - view: 'app/views/x.html.haml', - file: 'app/views/x.html.haml', - line: 4 - } + tags = { view: 'app/views/x.html.haml' } expect(transaction).to receive(:increment). with(:view_duration, 2.1) diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb index 6bdeb71949153d52fe1ede5785cf6f09f7724d19..1d5a51a157ea91b84df9f99cb037ecb42915ec21 100644 --- a/spec/lib/gitlab/metrics/transaction_spec.rb +++ b/spec/lib/gitlab/metrics/transaction_spec.rb @@ -11,6 +11,14 @@ describe Gitlab::Metrics::Transaction do end end + describe '#allocated_memory' do + it 'returns the allocated memory in bytes' do + transaction.run { 'a' * 32 } + + expect(transaction.allocated_memory).to be_a_kind_of(Numeric) + end + end + describe '#run' do it 'yields the supplied block' do expect { |b| transaction.run(&b) }.to yield_control @@ -43,8 +51,10 @@ describe Gitlab::Metrics::Transaction do transaction.increment(:time, 1) transaction.increment(:time, 2) + values = { duration: 0.0, time: 3, allocated_memory: a_kind_of(Numeric) } + expect(transaction).to receive(:add_metric). - with('transactions', { duration: 0.0, time: 3 }, {}) + with('transactions', values, {}) transaction.track_self end @@ -54,8 +64,14 @@ describe Gitlab::Metrics::Transaction do it 'sets a value' do transaction.set(:number, 10) + values = { + duration: 0.0, + number: 10, + allocated_memory: a_kind_of(Numeric) + } + expect(transaction).to receive(:add_metric). - with('transactions', { duration: 0.0, number: 10 }, {}) + with('transactions', values, {}) transaction.track_self end @@ -80,8 +96,13 @@ describe Gitlab::Metrics::Transaction do describe '#track_self' do it 'adds a metric for the transaction itself' do + values = { + duration: transaction.duration, + allocated_memory: a_kind_of(Numeric) + } + expect(transaction).to receive(:add_metric). - with('transactions', { duration: transaction.duration }, {}) + with('transactions', values, {}) transaction.track_self end @@ -104,7 +125,7 @@ describe Gitlab::Metrics::Transaction do hash = { series: 'rails_transactions', tags: { action: 'Foo#bar' }, - values: { duration: 0.0 }, + values: { duration: 0.0, allocated_memory: a_kind_of(Numeric) }, timestamp: an_instance_of(Fixnum) } diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index c2782f95c8e782d8d4173952cc4c260b460041d5..0ec8a6dc5cb35f4cde6911fd94680d76238c831f 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -13,15 +13,6 @@ describe Gitlab::Metrics do 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 - - expect(line).to eq(__LINE__ - 2) - 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) diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index 46cab1644c728e1e93274d71a202ab04de37c965..f9be8fcbcfec4c83c40bf832f0b68d2501e8f078 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -29,6 +29,22 @@ RSpec.describe AbuseReport, type: :model do it { is_expected.to validate_uniqueness_of(:user_id) } end + describe '#remove_user' do + it 'blocks the user' do + report = build(:abuse_report) + + allow(report.user).to receive(:destroy) + + expect { report.remove_user }.to change { report.user.blocked? }.to(true) + end + + it 'removes the user' do + report = build(:abuse_report) + + expect { report.remove_user }.to change { User.count }.by(-1) + end + end + describe '#notify' do it 'delivers' do expect(AbuseReportMailer).to receive(:notify).with(subject.id). diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb index 4455ae7b321e38317027a1befa1b49850cf4c312..fef211ded50901b0cb266eec118b568e4b09d754 100644 --- a/spec/services/system_hooks_service_spec.rb +++ b/spec/services/system_hooks_service_spec.rb @@ -9,54 +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, :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) } - 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, :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_username, :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) } 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, + :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, + :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, :updated_at, :path, :group_id, + :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, :updated_at, :path, :group_id, + :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, :updated_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_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 + :event_name, :created_at, :updated_at, :group_name, :group_path, + :group_id, :user_id, :user_username, :user_name, :user_email, :group_access ) end end