diff options
82 files changed, 732 insertions, 398 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 084febe175e..adde3400107 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -538,6 +538,8 @@ lint:javascript:report: <<: *except-docs <<: *pull-cache stage: post-test + dependencies: + - setup-test-env before_script: [] script: - find app/ spec/ -name '*.js' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 21574090598..ca222b7cf39 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.22.0 +0.23.0 @@ -71,7 +71,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.4', require: false gem 'github-linguist', '~> 4.7.0', require: 'linguist' # API -gem 'grape', '~> 0.19.0' +gem 'grape', '~> 0.19.2' gem 'grape-entity', '~> 0.6.0' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' @@ -386,7 +386,7 @@ gem 'vmstat', '~> 2.3.0' gem 'sys-filesystem', '~> 1.1.6' # Gitaly GRPC client -gem 'gitaly', '~> 0.17.0' +gem 'gitaly', '~> 0.18.0' gem 'toml-rb', '~> 0.3.15', require: false diff --git a/Gemfile.lock b/Gemfile.lock index 0c4fc1fee0c..b0b437ae342 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -269,7 +269,7 @@ GEM po_to_json (>= 1.0.0) rails (>= 3.2.0) gherkin-ruby (0.3.2) - gitaly (0.17.0) + gitaly (0.18.0) google-protobuf (~> 3.1) grpc (~> 1.0) github-linguist (4.7.6) @@ -332,13 +332,13 @@ GEM multi_json (~> 1.11) os (~> 0.9) signet (~> 0.7) - grape (0.19.1) + grape (0.19.2) activesupport builder hashie (>= 2.1.0) multi_json (>= 1.3.2) multi_xml (>= 0.5.2) - mustermann-grape (~> 0.4.0) + mustermann-grape (~> 1.0.0) rack (>= 1.3.0) rack-accept virtus (>= 1.0.0) @@ -463,10 +463,9 @@ GEM multi_json (1.12.1) multi_xml (0.6.0) multipart-post (2.0.0) - mustermann (0.4.0) - tool (~> 0.2) - mustermann-grape (0.4.0) - mustermann (= 0.4.0) + mustermann (1.0.0) + mustermann-grape (1.0.0) + mustermann (~> 1.0.0) mysql2 (0.4.5) net-ldap (0.12.1) netrc (0.11.0) @@ -850,7 +849,6 @@ GEM timfel-krb5-auth (0.8.3) toml-rb (0.3.15) citrus (~> 3.0, > 3.0) - tool (0.2.3) truncato (0.7.8) htmlentities (~> 4.3.1) nokogiri (~> 1.6.1) @@ -972,7 +970,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.2.0) - gitaly (~> 0.17.0) + gitaly (~> 0.18.0) github-linguist (~> 4.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-markup (~> 1.5.1) @@ -981,7 +979,7 @@ DEPENDENCIES gollum-rugged_adapter (~> 0.4.4) gon (~> 6.1.0) google-api-client (~> 0.8.6) - grape (~> 0.19.0) + grape (~> 0.19.2) grape-entity (~> 0.6.0) haml_lint (~> 0.21.0) hamlit (~> 2.6.1) diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 9e90a36a364..ffe97c071ba 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -41,7 +41,6 @@ import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater'; import Landing from './landing'; import BlobForkSuggestion from './blob/blob_fork_suggestion'; import UserCallout from './user_callout'; -import { ProtectedTagCreate, ProtectedTagEditList } from './protected_tags'; import ShortcutsWiki from './shortcuts_wiki'; import Pipelines from './pipelines'; import BlobViewer from './blob/viewer/index'; @@ -396,12 +395,6 @@ import PerformanceBar from './performance_bar'; new Search(); break; case 'projects:settings:repository:show': - // Initialize Protected Branch Settings - new gl.ProtectedBranchCreate(); - new gl.ProtectedBranchEditList(); - // Initialize Protected Tag Settings - new ProtectedTagCreate(); - new ProtectedTagEditList(); // Initialize expandable settings panels initSettingsPanels(); break; diff --git a/app/assets/javascripts/protected_branches/index.js b/app/assets/javascripts/protected_branches/index.js new file mode 100644 index 00000000000..c9e7af127d2 --- /dev/null +++ b/app/assets/javascripts/protected_branches/index.js @@ -0,0 +1,9 @@ +/* eslint-disable no-unused-vars */ + +import ProtectedBranchCreate from './protected_branch_create'; +import ProtectedBranchEditList from './protected_branch_edit_list'; + +$(() => { + const protectedBranchCreate = new ProtectedBranchCreate(); + const protectedBranchEditList = new ProtectedBranchEditList(); +}); diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js index 42993a252c3..38b1406a99f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js @@ -1,31 +1,26 @@ -/* eslint-disable arrow-parens, no-param-reassign, object-shorthand, no-else-return, comma-dangle, max-len */ +export default class ProtectedBranchAccessDropdown { + constructor(options) { + this.options = options; + this.initDropdown(); + } -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchAccessDropdown = class { - constructor(options) { - const { $dropdown, data, onSelect } = options; - - $dropdown.glDropdown({ - data: data, - selectable: true, - inputId: $dropdown.data('input-id'), - fieldName: $dropdown.data('field-name'), - toggleLabel(item, el) { - if (el.is('.is-active')) { - return item.text; - } else { - return 'Select'; - } - }, - clicked(opts) { - const { e } = opts; - - e.preventDefault(); - onSelect(); + initDropdown() { + const { $dropdown, data, onSelect } = this.options; + $dropdown.glDropdown({ + data, + selectable: true, + inputId: $dropdown.data('input-id'), + fieldName: $dropdown.data('field-name'), + toggleLabel(item, $el) { + if ($el.is('.is-active')) { + return item.text; } - }); - } - }; -})(window); + return 'Select'; + }, + clicked(options) { + options.e.preventDefault(); + onSelect(); + }, + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js index 57ea2f52814..10da3783123 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_create.js +++ b/app/assets/javascripts/protected_branches/protected_branch_create.js @@ -1,55 +1,51 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ -/* global ProtectedBranchDropdown */ - -(global => { - global.gl = global.gl || {}; - - gl.ProtectedBranchCreate = class { - constructor() { - this.$wrap = this.$form = $('#new_protected_branch'); - this.buildDropdowns(); - } - - buildDropdowns() { - const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); - - // Cache callback - this.onSelectCallback = this.onSelect.bind(this); - - // Allowed to Merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelectCallback - }); - - // Allowed to Push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: $allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelectCallback - }); - - // Select default - $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); - $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); - - // Protected branch dropdown - new ProtectedBranchDropdown({ - $dropdown: this.$wrap.find('.js-protected-branch-select'), - onSelect: this.onSelectCallback - }); - } - - // This will run after clicked callback - onSelect() { - // Enable submit button - const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]'); - const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); - const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); - - this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); - } - }; -})(window); +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; +import ProtectedBranchDropdown from './protected_branch_dropdown'; + +export default class ProtectedBranchCreate { + constructor() { + this.$form = $('.js-new-protected-branch'); + this.buildDropdowns(); + } + + buildDropdowns() { + const $allowedToMergeDropdown = this.$form.find('.js-allowed-to-merge'); + const $allowedToPushDropdown = this.$form.find('.js-allowed-to-push'); + + // Cache callback + this.onSelectCallback = this.onSelect.bind(this); + + // Allowed to Merge dropdown + this.protectedBranchMergeAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); + + // Allowed to Push dropdown + this.protectedBranchPushAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: $allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + + // Select default + $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0); + $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0); + + // Protected branch dropdown + this.protectedBranchDropdown = new ProtectedBranchDropdown({ + $dropdown: this.$form.find('.js-protected-branch-select'), + onSelect: this.onSelectCallback, + }); + } + + // This will run after clicked callback + onSelect() { + // Enable submit button + const $branchInput = this.$form.find('input[name="protected_branch[name]"]'); + const $allowedToMergeInput = this.$form.find('input[name="protected_branch[merge_access_levels_attributes][0][access_level]"]'); + const $allowedToPushInput = this.$form.find('input[name="protected_branch[push_access_levels_attributes][0][access_level]"]'); + + this.$form.find('input[type="submit"]').attr('disabled', !($branchInput.val() && $allowedToMergeInput.length && $allowedToPushInput.length)); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js index bc6110fcd4e..cc0b2ebe071 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_dropdown.js +++ b/app/assets/javascripts/protected_branches/protected_branch_dropdown.js @@ -1,6 +1,10 @@ -/* eslint-disable comma-dangle, no-unused-vars */ - -class ProtectedBranchDropdown { +export default class ProtectedBranchDropdown { + /** + * @param {Object} options containing + * `$dropdown` target element + * `onSelect` event callback + * $dropdown must be an element created using `dropdown_branch()` rails helper + */ constructor(options) { this.onSelect = options.onSelect; this.$dropdown = options.$dropdown; @@ -12,7 +16,7 @@ class ProtectedBranchDropdown { this.bindEvents(); // Hide footer - this.$dropdownFooter.addClass('hidden'); + this.toggleFooter(true); } buildDropdown() { @@ -21,7 +25,7 @@ class ProtectedBranchDropdown { filterable: true, remote: false, search: { - fields: ['title'] + fields: ['title'], }, selectable: true, toggleLabel(selected) { @@ -36,10 +40,9 @@ class ProtectedBranchDropdown { }, onFilter: this.toggleCreateNewButton.bind(this), clicked: (options) => { - const { $el, e } = options; - e.preventDefault(); + options.e.preventDefault(); this.onSelect(); - } + }, }); } @@ -64,20 +67,22 @@ class ProtectedBranchDropdown { } toggleCreateNewButton(branchName) { - this.selectedBranch = { - title: branchName, - id: branchName, - text: branchName - }; - if (branchName) { + this.selectedBranch = { + title: branchName, + id: branchName, + text: branchName, + }; + this.$dropdownContainer .find('.js-create-new-protected-branch code') .text(branchName); } - this.$dropdownFooter.toggleClass('hidden', !branchName); + this.toggleFooter(!branchName); } -} -window.ProtectedBranchDropdown = ProtectedBranchDropdown; + toggleFooter(toggleState) { + this.$dropdownFooter.toggleClass('hidden', toggleState); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js index 6ef59e94384..3b920942a3f 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js @@ -1,69 +1,67 @@ -/* eslint-disable no-new, arrow-parens, no-param-reassign, comma-dangle, max-len */ +/* eslint-disable no-new */ /* global Flash */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchAccessDropdown from './protected_branch_access_dropdown'; - gl.ProtectedBranchEdit = class { - constructor(options) { - this.$wrap = options.$wrap; - this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); - this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); +export default class ProtectedBranchEdit { + constructor(options) { + this.$wrap = options.$wrap; + this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge'); + this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push'); + this.onSelectCallback = this.onSelect.bind(this); - this.buildDropdowns(); - } + this.buildDropdowns(); + } - buildDropdowns() { - // Allowed to merge dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToMergeDropdown, - data: gon.merge_access_levels, - onSelect: this.onSelect.bind(this) - }); + buildDropdowns() { + // Allowed to merge dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToMergeDropdown, + data: gon.merge_access_levels, + onSelect: this.onSelectCallback, + }); - // Allowed to push dropdown - new gl.ProtectedBranchAccessDropdown({ - $dropdown: this.$allowedToPushDropdown, - data: gon.push_access_levels, - onSelect: this.onSelect.bind(this) - }); - } + // Allowed to push dropdown + this.protectedBranchAccessDropdown = new ProtectedBranchAccessDropdown({ + $dropdown: this.$allowedToPushDropdown, + data: gon.push_access_levels, + onSelect: this.onSelectCallback, + }); + } - onSelect() { - const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); - const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); + onSelect() { + const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`); + const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`); - // Do not update if one dropdown has not selected any option - if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; + // Do not update if one dropdown has not selected any option + if (!($allowedToMergeInput.length && $allowedToPushInput.length)) return; - this.$allowedToMergeDropdown.disable(); - this.$allowedToPushDropdown.disable(); + this.$allowedToMergeDropdown.disable(); + this.$allowedToPushDropdown.disable(); - $.ajax({ - type: 'POST', - url: this.$wrap.data('url'), - dataType: 'json', - data: { - _method: 'PATCH', - protected_branch: { - merge_access_levels_attributes: [{ - id: this.$allowedToMergeDropdown.data('access-level-id'), - access_level: $allowedToMergeInput.val() - }], - push_access_levels_attributes: [{ - id: this.$allowedToPushDropdown.data('access-level-id'), - access_level: $allowedToPushInput.val() - }] - } + $.ajax({ + type: 'POST', + url: this.$wrap.data('url'), + dataType: 'json', + data: { + _method: 'PATCH', + protected_branch: { + merge_access_levels_attributes: [{ + id: this.$allowedToMergeDropdown.data('access-level-id'), + access_level: $allowedToMergeInput.val(), + }], + push_access_levels_attributes: [{ + id: this.$allowedToPushDropdown.data('access-level-id'), + access_level: $allowedToPushInput.val(), + }], }, - error() { - $.scrollTo(0); - new Flash('Failed to update branch!'); - } - }).always(() => { - this.$allowedToMergeDropdown.enable(); - this.$allowedToPushDropdown.enable(); - }); - } - }; -})(window); + }, + error() { + new Flash('Failed to update branch!', null, $('.js-protected-branches-list')); + }, + }).always(() => { + this.$allowedToMergeDropdown.enable(); + this.$allowedToPushDropdown.enable(); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js index 336fa6c57a7..b40d3827c30 100644 --- a/app/assets/javascripts/protected_branches/protected_branch_edit_list.js +++ b/app/assets/javascripts/protected_branches/protected_branch_edit_list.js @@ -1,18 +1,18 @@ -/* eslint-disable arrow-parens, no-param-reassign, no-new, comma-dangle */ +/* eslint-disable no-new */ -(global => { - global.gl = global.gl || {}; +import ProtectedBranchEdit from './protected_branch_edit'; - gl.ProtectedBranchEditList = class { - constructor() { - this.$wrap = $('.protected-branches-list'); +export default class ProtectedBranchEditList { + constructor() { + this.$wrap = $('.protected-branches-list'); + this.initEditForm(); + } - // Build edit forms - this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { - new gl.ProtectedBranchEdit({ - $wrap: $(el) - }); + initEditForm() { + this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => { + new ProtectedBranchEdit({ + $wrap: $(el), }); - } - }; -})(window); + }); + } +} diff --git a/app/assets/javascripts/protected_branches/protected_branches_bundle.js b/app/assets/javascripts/protected_branches/protected_branches_bundle.js deleted file mode 100644 index 874d70a1431..00000000000 --- a/app/assets/javascripts/protected_branches/protected_branches_bundle.js +++ /dev/null @@ -1,5 +0,0 @@ -import './protected_branch_access_dropdown'; -import './protected_branch_create'; -import './protected_branch_dropdown'; -import './protected_branch_edit'; -import './protected_branch_edit_list'; diff --git a/app/assets/javascripts/protected_tags/index.js b/app/assets/javascripts/protected_tags/index.js index 61e7ba53862..b1618e24e49 100644 --- a/app/assets/javascripts/protected_tags/index.js +++ b/app/assets/javascripts/protected_tags/index.js @@ -1,2 +1,9 @@ -export { default as ProtectedTagCreate } from './protected_tag_create'; -export { default as ProtectedTagEditList } from './protected_tag_edit_list'; +/* eslint-disable no-unused-vars */ + +import ProtectedTagCreate from './protected_tag_create'; +import ProtectedTagEditList from './protected_tag_edit_list'; + +$(() => { + const protectedtTagCreate = new ProtectedTagCreate(); + const protectedtTagEditList = new ProtectedTagEditList(); +}); diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss index e1873506bec..9f3e278ebfc 100644 --- a/app/assets/stylesheets/new_nav.scss +++ b/app/assets/stylesheets/new_nav.scss @@ -41,10 +41,22 @@ header.navbar-gitlab-new { } } + .logo-text { + line-height: initial; + + svg { + width: 55px; + height: 15px; + margin: 0; + fill: $white-light; + } + } + &:hover, &:focus { - color: $tanuki-yellow; - text-decoration: none; + .logo-text svg { + fill: $tanuki-yellow; + } } } } @@ -274,7 +286,7 @@ header.navbar-gitlab-new { .breadcrumbs { display: flex; - min-height: 60px; + min-height: 61px; color: $gl-text-color; border-bottom: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index c1423965d0a..a3e07a36c33 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -742,7 +742,8 @@ pre.light-well { } } -.protected-tags-list { +.protected-tags-list, +.protected-branches-list { .dropdown-menu-toggle { width: 100%; max-width: 300px; diff --git a/app/helpers/triggers_helper.rb b/app/helpers/triggers_helper.rb index a48d4475e97..ce435ca2241 100644 --- a/app/helpers/triggers_helper.rb +++ b/app/helpers/triggers_helper.rb @@ -8,6 +8,6 @@ module TriggersHelper end def service_trigger_url(service) - "#{Settings.gitlab.url}/api/v3/projects/#{service.project_id}/services/#{service.to_param}/trigger" + "#{Settings.gitlab.url}/api/v4/projects/#{service.project_id}/services/#{service.to_param}/trigger" end end diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml index e80d10dc8f1..bfd7dd25a7d 100644 --- a/app/views/devise/shared/_omniauth_box.html.haml +++ b/app/views/devise/shared/_omniauth_box.html.haml @@ -7,6 +7,6 @@ %span.light - has_icon = provider_has_icon?(provider) = link_to provider_image_tag(provider), omniauth_authorize_path(:user, provider), method: :post, class: 'oauth-login' + (has_icon ? ' oauth-image-link' : ' btn'), id: "oauth-login-#{provider}" - %fieldset + %fieldset.prepend-top-10 = check_box_tag :remember_me - = label_tag :remember_me, 'Remember Me' + = label_tag :remember_me, 'Remember me' diff --git a/app/views/layouts/header/_new.html.haml b/app/views/layouts/header/_new.html.haml index 4697d91724b..76367bd9734 100644 --- a/app/views/layouts/header/_new.html.haml +++ b/app/views/layouts/header/_new.html.haml @@ -6,8 +6,8 @@ %h1.title = link_to root_path, title: 'Dashboard' do = brand_header_logo - %span.hidden-xs - GitLab + %span.logo-text.hidden-xs + = render 'shared/logo_type.svg' - if current_user = render "layouts/nav/new_dashboard" diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml index 97c0407a01d..7343d6e039c 100644 --- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml +++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml @@ -4,7 +4,7 @@ = pipeline_schedule.description %td.branch-name-cell = icon('code-fork') - - if pipeline_schedule.ref + - if pipeline_schedule.ref.present? = link_to pipeline_schedule.ref, project_ref_path(@project, pipeline_schedule.ref), class: "ref-name" %td - if pipeline_schedule.last_pipeline diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml index 5c00bb6883c..2a0704bc7af 100644 --- a/app/views/projects/protected_branches/shared/_branches_list.html.haml +++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-branches-list +.panel.panel-default.protected-branches-list.js-protected-branches-list - if @protected_branches.empty? .panel-heading %h3.panel-title @@ -23,6 +23,8 @@ - if can_admin_project %th %tbody + %tr + %td.flash-container{ colspan: 5 } = yield = paginate @protected_branches, theme: 'gitlab' diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml index b619fa57e05..9f0c4f3b3a8 100644 --- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml +++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml @@ -1,4 +1,4 @@ -= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f| += form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'new-protected-branch js-new-protected-branch' } do |f| .panel.panel-default .panel-heading %h3.panel-title diff --git a/app/views/projects/protected_tags/shared/_tags_list.html.haml b/app/views/projects/protected_tags/shared/_tags_list.html.haml index 6e3cd4ada71..3f42ae58438 100644 --- a/app/views/projects/protected_tags/shared/_tags_list.html.haml +++ b/app/views/projects/protected_tags/shared/_tags_list.html.haml @@ -1,4 +1,4 @@ -.panel.panel-default.protected-tags-list +.panel.panel-default.protected-tags-list.js-protected-tags-list - if @protected_tags.empty? .panel-heading %h3.panel-title diff --git a/app/views/shared/_logo_type.svg b/app/views/shared/_logo_type.svg new file mode 100644 index 00000000000..7e9276a0a83 --- /dev/null +++ b/app/views/shared/_logo_type.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 617 169" enable-background="new 0 0 617 169"><path d="m315.26 2.969h-21.8l.1 162.5h88.3v-20.1h-66.5l-.1-142.4"/><path d="m-511.44-6.331c-5.5 5.7-14.6 11.4-27 11.4-16.6 0-23.3-8.2-23.3-18.9 0-16.1 11.2-23.8 35-23.8 4.5 0 11.7.5 15.4 1.2v30.1h-.1m-22.6-98.5c-17.6 0-33.8 6.2-46.4 16.7l7.7 13.4c8.9-5.2 19.8-10.4 35.5-10.4 17.9 0 25.8 9.2 25.8 24.6v7.9c-3.5-.7-10.7-1.2-15.1-1.2-38.2 0-57.6 13.4-57.6 41.4 0 25.1 15.4 37.7 38.7 37.7 15.7 0 30.8-7.2 36-18.9l4 15.9h15.4v-83.2c-.1-26.3-11.5-43.9-44-43.9" transform="translate(977.33 143.28)"/><path d="m-542.14 5.969c-8.2 0-15.4-1-20.8-3.5v-67.3-7.8c7.4-6.2 16.6-10.7 28.3-10.7 21.1 0 29.2 14.9 29.2 39 0 34.2-13.1 50.3-36.7 50.3m9.2-110.6c-19.5 0-30 13.3-30 13.3v-21l-.1-27.8h-9.8-11.5l.1 158.5c10.7 4.5 25.3 6.9 41.2 6.9 40.7 0 60.3-26 60.3-70.9-.1-35.5-18.2-59-50.2-59" transform="translate(1099.77 143.13)"/><path d="m-506.14-123.03c19.3 0 31.8 6.4 39.9 12.9l9.4-16.3c-12.7-11.2-29.9-17.2-48.3-17.2-46.4 0-78.9 28.3-78.9 85.4 0 59.8 35.1 83.1 75.2 83.1 20.1 0 37.2-4.7 48.4-9.4l-.5-63.9v-7.5-12.6h-59.5v20.1h38l.5 48.5c-5 2.5-13.6 4.5-25.3 4.5-32.2 0-53.8-20.3-53.8-63-.1-43.5 22.2-64.6 54.9-64.6" transform="translate(584.04 143.63)"/><path d="m-562.14-139.63h-21.3l.1 27.3v11.2 6.5 11.4 65 .2c0 26.3 11.4 43.9 43.9 43.9 4.5 0 8.9-.4 13.1-1.2v-19.1c-3.1.5-6.4.7-9.9.7-17.9 0-25.8-9.2-25.8-24.6v-65h35.7v-17.8h-35.7l-.1-38.5" transform="translate(793.57 142.58)"/><path d="m155.96 165.47h21.3v-124h-21.3v124"/><path d="m155.96 24.369h21.3v-21.3h-21.3v21.3"/></svg> diff --git a/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml new file mode 100644 index 00000000000..e843bbac239 --- /dev/null +++ b/changelogs/unreleased/34549-extract-devise-mappings-into-helper.yml @@ -0,0 +1,4 @@ +--- +title: Extract "@request.env[devise.mapping] = Devise.mappings[:user]" to a test helper +merge_request: 12742 +author: Jacopo Beschi @jacopo-beschi diff --git a/changelogs/unreleased/fix-re2-infinite-loop-nick.yml b/changelogs/unreleased/fix-re2-infinite-loop-nick.yml new file mode 100644 index 00000000000..3c314ab2718 --- /dev/null +++ b/changelogs/unreleased/fix-re2-infinite-loop-nick.yml @@ -0,0 +1,4 @@ +--- +title: Fix an infinite loop when handling user-supplied regular expressions +merge_request: +author: diff --git a/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml b/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml new file mode 100644 index 00000000000..334d8ca4d9e --- /dev/null +++ b/changelogs/unreleased/fix-sm-32790-pipeline_schedules-pages-throwing-error-500.yml @@ -0,0 +1,4 @@ +--- +title: Fix pipeline_schedules pages throwing error 500 (when ref is empty) +merge_request: 12983 +author: diff --git a/config/webpack.config.js b/config/webpack.config.js index 1113241e402..a7d92bc53b7 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -56,7 +56,7 @@ var config = { pipelines_details: './pipelines/pipeline_details_bundle.js', profile: './profile/profile_bundle.js', prometheus_metrics: './prometheus_metrics', - protected_branches: './protected_branches/protected_branches_bundle.js', + protected_branches: './protected_branches', protected_tags: './protected_tags', sidebar: './sidebar/sidebar_bundle.js', schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js', diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index c8987dea5e2..3449f9e15ce 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -228,7 +228,7 @@ Tip: If you want to limit access to the nested members of an Active Directory group you can use the following syntax: ``` -(memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com) +(memberOf:1.2.840.113556.1.4.1941:=CN=My Group,DC=Example,DC=com) ``` Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md index 72a34aa7de9..e67db9ff142 100644 --- a/doc/development/background_migrations.md +++ b/doc/development/background_migrations.md @@ -31,6 +31,18 @@ It's also possible for different migrations to be executed at the same time. This means that different background migrations should not migrate data in a way that would cause conflicts. +## Idempotence + +Background migrations are executed in a context of a Sidekiq process. +Usual Sidekiq rules apply, especially the rule that jobs should be small +and idempotent. + +See [Sidekiq best practices guidelines](https://github.com/mperham/sidekiq/wiki/Best-Practices) +for more details. + +Make sure that in case that your migration job is going to be retried data +integrity is guarateed. + ## How It Works Background migrations are simple classes that define a `perform` method. A @@ -212,3 +224,27 @@ end This migration will then process any jobs for the ExtractServicesUrl migration and continue once all jobs have been processed. Once done you can safely remove the `services.properties` column. + +## Testing + +It is required to write tests for background migrations' scheduling migration +(either a regular migration or a post deployment migration), background +migration itself and a cleanup migration. You can use the `:migration` RSpec +tag when testing a regular / post deployment migration. +See [README][migrations-readme]. + +When you do that, keep in mind that `before` and `after` RSpec hooks are going +to migrate you database down and up, which can result in other background +migrations being called. That means that using `spy` test doubles with +`have_received` is encouraged, instead of using regular test doubles, because +your expectations defined in a `it` block can conflict with what is being +called in RSpec hooks. See [gitlab-org/gitlab-ce#35351][issue-rspec-hooks] +for more details. + +## Best practices + +1. Make sure that background migration jobs are idempotent. +1. Make sure that tests you write are not false positives. + +[migrations-readme]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/migrations/README.md +[issue-rspec-hooks]: https://gitlab.com/gitlab-org/gitlab-ce/issues/35351 diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md index e3a20f29a09..1e9fdbc65e2 100644 --- a/doc/development/sidekiq_style_guide.md +++ b/doc/development/sidekiq_style_guide.md @@ -36,3 +36,10 @@ slow jobs blocking work (even for different jobs) on the shared queue. Each Sidekiq worker must be tested using RSpec, just like any other class. These tests should be placed in `spec/workers`. + +## Removing or renaming queues + +Try to avoid renaming or removing queues in minor and patch releases. +During online update instance can have pending jobs and removing the queue can +lead to those jobs being stuck forever. If you can't write migration for those +Sidekiq jobs, please consider doing rename or remove queue in major release only.
\ No newline at end of file diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index c9b5f58c557..cdacf9839e5 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -68,6 +68,7 @@ module API delete ":id/access_requests/:user_id" do source = find_source(source_type, params[:id]) + status 204 ::Members::DestroyService.new(source, current_user, params) .execute(:requesters) end diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index 56f19f89642..5a028fc9d0b 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -88,6 +88,7 @@ module API unauthorized! unless award.user == current_user || current_user.admin? + status 204 award.destroy end end diff --git a/lib/api/broadcast_messages.rb b/lib/api/broadcast_messages.rb index 395c401203c..9980aec4752 100644 --- a/lib/api/broadcast_messages.rb +++ b/lib/api/broadcast_messages.rb @@ -91,6 +91,7 @@ module API delete ':id' do message = find_message + status 204 message.destroy end end diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb index d5c2f3d5094..42e7c1486b0 100644 --- a/lib/api/deploy_keys.rb +++ b/lib/api/deploy_keys.rb @@ -125,6 +125,7 @@ module API key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) not_found!('Deploy Key') unless key + status 204 key.destroy end end diff --git a/lib/api/environments.rb b/lib/api/environments.rb index 945771d46f3..c774a5c6685 100644 --- a/lib/api/environments.rb +++ b/lib/api/environments.rb @@ -79,6 +79,7 @@ module API environment = user_project.environments.find(params[:environment_id]) + status 204 environment.destroy end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index ebbaed0cbb7..49c3b2278c7 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -125,6 +125,8 @@ module API delete ":id" do group = find_group!(params[:id]) authorize! :admin_group, group + + status 204 ::Groups::DestroyService.new(group, current_user).execute end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 64be08094ed..14b26f28ebf 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -224,6 +224,7 @@ module API not_found!('Issue') unless issue authorize!(:destroy_issue, issue) + status 204 issue.destroy end diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 20b25529d0c..4520c98d951 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -56,6 +56,7 @@ module API label = user_project.labels.find_by(title: params[:name]) not_found!('Label') unless label + status 204 label.destroy end diff --git a/lib/api/members.rb b/lib/api/members.rb index c200e46a328..bb970b7cd54 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -96,6 +96,7 @@ module API # Ensure that memeber exists source.members.find_by!(user_id: params[:user_id]) + status 204 ::Members::DestroyService.new(source, current_user, declared_params).execute end end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index ac33b2b801c..6e2e13e0a24 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -137,6 +137,7 @@ module API merge_request = find_project_merge_request(params[:merge_request_iid]) authorize!(:destroy_merge_request, merge_request) + status 204 merge_request.destroy end diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 01ca62b593f..65ff89edf65 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -131,6 +131,7 @@ module API note = user_project.notes.find(params[:note_id]) authorize! :admin_note, note + status 204 ::Notes::DestroyService.new(user_project, current_user).execute(note) end end diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb index 7a345289617..649dd891f56 100644 --- a/lib/api/project_hooks.rb +++ b/lib/api/project_hooks.rb @@ -96,6 +96,7 @@ module API delete ":id/hooks/:hook_id" do hook = user_project.hooks.find(params.delete(:hook_id)) + status 204 hook.destroy end end diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 3320eadff0d..f3d905b0068 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -116,6 +116,7 @@ module API not_found!('Snippet') unless snippet authorize! :admin_project_snippet, snippet + status 204 snippet.destroy end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index c459257158d..89dda88d3f5 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -361,6 +361,7 @@ module API authorize! :remove_fork_project, user_project if user_project.forked? + status 204 user_project.forked_project_link.destroy else not_modified! @@ -405,6 +406,7 @@ module API link = user_project.project_group_links.find_by(group_id: params[:group_id]) not_found!('Group Link') unless link + status 204 link.destroy end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index 4552115b3e2..405d25ca3c1 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -45,6 +45,7 @@ module API end delete '/' do authenticate_runner! + status 204 Ci::Runner.find_by_token(params[:token]).destroy end diff --git a/lib/api/runners.rb b/lib/api/runners.rb index db6c7c59092..5bf5a18e42f 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -79,6 +79,7 @@ module API runner = get_runner(params[:id]) authenticate_delete_runner!(runner) + status 204 runner.destroy! end end @@ -134,6 +135,7 @@ module API runner = runner_project.runner forbidden!("Only one project associated with the runner. Please remove the runner instead") if runner.projects.count == 1 + status 204 runner_project.destroy end end diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index fd634037a77..35ece56c65c 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -123,6 +123,7 @@ module API authorize! :destroy_personal_snippet, snippet + status 204 snippet.destroy end diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index ed7b23b474a..c0179037440 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -66,6 +66,7 @@ module API hook = SystemHook.find_by(id: params[:id]) not_found!('System hook') unless hook + status 204 hook.destroy end end diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb index a9f2ca2608e..cb0619105e1 100644 --- a/lib/api/triggers.rb +++ b/lib/api/triggers.rb @@ -142,6 +142,7 @@ module API trigger = user_project.triggers.find(params.delete(:trigger_id)) return not_found!('Trigger') unless trigger + status 204 trigger.destroy end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 81c68ea2658..a590f2692a2 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -235,6 +235,7 @@ module API key = user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key + status 204 key.destroy end @@ -306,6 +307,7 @@ module API user = User.find_by(id: params[:id]) not_found!('User') unless user + status 204 user.delete_async(deleted_by: current_user, params: params) end @@ -406,6 +408,7 @@ module API requires :impersonation_token_id, type: Integer, desc: 'The ID of the impersonation token' end delete ':impersonation_token_id' do + status 204 find_impersonation_token.revoke! end end @@ -483,6 +486,7 @@ module API key = current_user.keys.find_by(id: params[:key_id]) not_found!('Key') unless key + status 204 key.destroy end @@ -534,6 +538,7 @@ module API email = current_user.emails.find_by(id: params[:email_id]) not_found!('Email') unless email + status 204 Emails::DestroyService.new(current_user, email: email.email).execute end diff --git a/lib/api/variables.rb b/lib/api/variables.rb index 7fa528fb2d3..7c0fdd3d1be 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -88,6 +88,7 @@ module API variable = user_project.variables.find_by(key: params[:key]) not_found!('Variable') unless variable + status 204 variable.destroy end end diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb index 5d6977106d6..aaba034474c 100644 --- a/lib/gitlab/ci/trace/stream.rb +++ b/lib/gitlab/ci/trace/stream.rb @@ -74,6 +74,7 @@ module Gitlab match = "" reverse_line do |line| + line.chomp! matches = regex.scan(line) next unless matches.is_a?(Array) next if matches.empty? diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb index edd7795eef0..85e6db0a689 100644 --- a/lib/gitlab/ee_compat_check.rb +++ b/lib/gitlab/ee_compat_check.rb @@ -237,6 +237,10 @@ module Gitlab branch_name.parameterize << '.patch' end + def patch_url + "https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/#{ENV['CI_JOB_ID']}/artifacts/raw/ee_compat_check/patches/#{ce_patch_name}" + end + def step(desc, cmd = nil) puts "\n=> #{desc}\n" @@ -303,14 +307,11 @@ module Gitlab 2. Apply your branch's patch to EE - # In the CE repo - $ git fetch origin master - $ git diff --binary origin/master...HEAD -- > #{ce_branch}.patch - # In the EE repo $ git fetch origin master $ git checkout -b #{ee_branch_prefix} origin/master - $ git apply --3way path/to/#{ce_branch}.patch + $ wget #{patch_url} + $ git apply --3way #{ce_patch_name} At this point you might have conflicts such as: @@ -324,7 +325,7 @@ module Gitlab If the patch couldn't be applied cleanly, use the following command: # In the EE repo - $ git apply --reject path/to/#{ce_branch}.patch + $ git apply --reject #{ce_patch_name} This option makes git apply the parts of the patch that are applicable, and leave the rejected hunks in corresponding `.rej` files. diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index 76a562f356e..09511cc6504 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -98,17 +98,13 @@ module Gitlab # Commit.between(repo, '29eda46b', 'master') # def between(repo, base, head) - commits = Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled| + Gitlab::GitalyClient.migrate(:commits_between) do |is_enabled| if is_enabled repo.gitaly_commit_client.between(base, head) else - repo.commits_between(base, head) + repo.commits_between(base, head).map { |c| decorate(c) } end end - - commits.map do |commit| - decorate(commit) - end rescue Rugged::ReferenceError [] end @@ -135,6 +131,16 @@ module Gitlab # # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/326 def find_all(repo, options = {}) + Gitlab::GitalyClient.migrate(:find_all_commits) do |is_enabled| + if is_enabled + find_all_by_gitaly(repo, options) + else + find_all_by_rugged(repo, options) + end + end + end + + def find_all_by_rugged(repo, options = {}) actual_options = options.dup allowed_options = [:ref, :max_count, :skip, :order] @@ -173,6 +179,10 @@ module Gitlab [] end + def find_all_by_gitaly(repo, options = {}) + Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options) + end + def decorate(commit, ref = nil) Gitlab::Git::Commit.new(commit, ref) end @@ -214,11 +224,12 @@ module Gitlab def initialize(raw_commit, head = nil) raise "Nil as raw commit passed" unless raw_commit - if raw_commit.is_a?(Hash) + case raw_commit + when Hash init_from_hash(raw_commit) - elsif raw_commit.is_a?(Rugged::Commit) + when Rugged::Commit init_from_rugged(raw_commit) - elsif raw_commit.is_a?(Gitaly::GitCommit) + when Gitlab::GitalyClient::Commit init_from_gitaly(raw_commit) else raise "Invalid raw commit type: #{raw_commit.class}" @@ -298,7 +309,14 @@ module Gitlab end def parents - raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) } + case raw_commit + when Rugged::Commit + raw_commit.parents.map { |c| Gitlab::Git::Commit.new(c) } + when Gitlab::GitalyClient::Commit + parent_ids.map { |oid| self.class.find(raw_commit.repository, oid) }.compact + else + raise NotImplementedError, "commit source doesn't support #parents" + end end def stats diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb index 8122ff0e81f..8e959c57c7c 100644 --- a/lib/gitlab/git/tree.rb +++ b/lib/gitlab/git/tree.rb @@ -17,30 +17,13 @@ module Gitlab def where(repository, sha, path = nil) path = nil if path == '' || path == '/' - commit = repository.lookup(sha) - root_tree = commit.tree - - tree = if path - id = find_id_by_path(repository, root_tree.oid, path) - if id - repository.lookup(id) - else - [] - end - else - root_tree - end - - tree.map do |entry| - new( - id: entry[:oid], - root_id: root_tree.oid, - name: entry[:name], - type: entry[:type], - mode: entry[:filemode].to_s(8), - path: path ? File.join(path, entry[:name]) : entry[:name], - commit_id: sha - ) + Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled| + if is_enabled + client = Gitlab::GitalyClient::CommitService.new(repository) + client.tree_entries(repository, sha, path) + else + tree_entries_from_rugged(repository, sha, path) + end end end @@ -74,6 +57,34 @@ module Gitlab entry[:oid] end end + + def tree_entries_from_rugged(repository, sha, path) + commit = repository.lookup(sha) + root_tree = commit.tree + + tree = if path + id = find_id_by_path(repository, root_tree.oid, path) + if id + repository.lookup(id) + else + [] + end + else + root_tree + end + + tree.map do |entry| + new( + id: entry[:oid], + root_id: root_tree.oid, + name: entry[:name], + type: entry[:type], + mode: entry[:filemode].to_s(8), + path: path ? File.join(path, entry[:name]) : entry[:name], + commit_id: sha + ) + end + end end def initialize(options) diff --git a/lib/gitlab/gitaly_client/commit.rb b/lib/gitlab/gitaly_client/commit.rb new file mode 100644 index 00000000000..61fe462d762 --- /dev/null +++ b/lib/gitlab/gitaly_client/commit.rb @@ -0,0 +1,14 @@ +module Gitlab + module GitalyClient + class Commit + attr_reader :repository, :gitaly_commit + + delegate :id, :subject, :body, :author, :committer, :parent_ids, to: :gitaly_commit + + def initialize(repository, gitaly_commit) + @repository = repository + @gitaly_commit = gitaly_commit + end + end + end +end diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb index b749955cddc..c6e52b530b3 100644 --- a/lib/gitlab/gitaly_client/commit_service.rb +++ b/lib/gitlab/gitaly_client/commit_service.rb @@ -60,6 +60,31 @@ module Gitlab entry end + def tree_entries(repository, revision, path) + request = Gitaly::GetTreeEntriesRequest.new( + repository: @gitaly_repo, + revision: revision, + path: path.presence || '.' + ) + + response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request) + + response.flat_map do |message| + message.entries.map do |gitaly_tree_entry| + entry_path = gitaly_tree_entry.path.dup + Gitlab::Git::Tree.new( + id: gitaly_tree_entry.oid, + root_id: gitaly_tree_entry.root_oid, + type: gitaly_tree_entry.type.downcase, + mode: gitaly_tree_entry.mode.to_s(8), + name: File.basename(entry_path), + path: entry_path, + commit_id: gitaly_tree_entry.commit_oid + ) + end + end + end + def commit_count(ref) request = Gitaly::CountCommitsRequest.new( repository: @gitaly_repo, @@ -80,6 +105,19 @@ module Gitlab consume_commits_response(response) end + def find_all_commits(opts = {}) + request = Gitaly::FindAllCommitsRequest.new( + repository: @gitaly_repo, + revision: opts[:ref].to_s, + max_count: opts[:max_count].to_i, + skip: opts[:skip].to_i + ) + request.order = opts[:order].upcase if opts[:order].present? + + response = GitalyClient.call(@repository.storage, :commit_service, :find_all_commits, request) + consume_commits_response(response) + end + private def commit_diff_request_params(commit, options = {}) @@ -94,7 +132,12 @@ module Gitlab end def consume_commits_response(response) - response.flat_map { |r| r.commits } + response.flat_map do |message| + message.commits.map do |gitaly_commit| + commit = GitalyClient::Commit.new(@repository, gitaly_commit) + Gitlab::Git::Commit.new(commit) + end + end end end end diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb index 8b43f0053d6..925b2158a22 100644 --- a/lib/gitlab/untrusted_regexp.rb +++ b/lib/gitlab/untrusted_regexp.rb @@ -22,13 +22,28 @@ module Gitlab end def scan(text) - scan_regexp.scan(text).map do |match| - if regexp.number_of_capturing_groups == 0 - match.first - else - match - end + text = text.dup # modified in-place + results = [] + + loop do + match = scan_regexp.match(text) + break unless match + + # Ruby scan returns empty strings, not nil + groups = match.to_a.map(&:to_s) + + results << + if regexp.number_of_capturing_groups.zero? + groups[0] + else + groups[1..-1] + end + + text.slice!(0, match.end(0) || 1) + break unless text.present? end + + results end def replace(text, rewrite) @@ -43,7 +58,7 @@ module Gitlab # groups, so work around it def scan_regexp @scan_regexp ||= - if regexp.number_of_capturing_groups == 0 + if regexp.number_of_capturing_groups.zero? RE2::Regexp.new('(' + regexp.source + ')') else regexp diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb index 964aa0fe1bc..28b2d86eed2 100644 --- a/lib/tasks/gitlab/task_helpers.rb +++ b/lib/tasks/gitlab/task_helpers.rb @@ -153,7 +153,6 @@ module Gitlab clone_repo(repo, target_dir) unless Dir.exist?(target_dir) checkout_version(version, target_dir) - reset_to_version(version, target_dir) end def clone_repo(repo, target_dir) @@ -161,12 +160,8 @@ module Gitlab end def checkout_version(version, target_dir) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet]) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout --quiet #{version}]) - end - - def reset_to_version(version, target_dir) - run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} reset --hard #{version}]) + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} fetch --quiet origin #{version}]) + run_command!(%W[#{Gitlab.config.git.bin_path} -C #{target_dir} checkout -f --quiet FETCH_HEAD --]) end end end diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index e61cf0e5152..05173ed12c0 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -7,13 +7,13 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-28 13:32+0200\n" +"POT-Creation-Date: 2017-07-05 08:50-0500\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-11 09:10-0400\n" -"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n" "Language-Team: Chinese (Taiwan) (https://translate.zanata.org/project/view/GitLab)\n" +"PO-Revision-Date: 2017-07-20 09:50-0400\n" +"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n" "Language: zh-TW\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=1; plural=0\n" @@ -624,6 +624,12 @@ msgstr "所有" msgid "PipelineSchedules|Inactive" msgstr "未啟用" +msgid "PipelineSchedules|Input variable key" +msgstr "變數名稱" + +msgid "PipelineSchedules|Input variable value" +msgstr "變數值" + msgid "PipelineSchedules|Next Run" msgstr "下次執行時間" @@ -633,12 +639,18 @@ msgstr "無" msgid "PipelineSchedules|Provide a short description for this pipeline" msgstr "請簡單說明此流水線 (pipeline) " +msgid "PipelineSchedules|Remove variable row" +msgstr "刪除變數" + msgid "PipelineSchedules|Take ownership" msgstr "取得所有權" msgid "PipelineSchedules|Target" msgstr "目標" +msgid "PipelineSchedules|Variables" +msgstr "變數" + msgid "PipelineSheduleIntervalPattern|Custom" msgstr "自訂" @@ -759,13 +771,13 @@ msgid "Revert this merge request" msgstr "還原此合併請求 (merge request) " msgid "Save pipeline schedule" -msgstr "保存流水線 (pipeline) 排程" +msgstr "儲存流水線 (pipeline) 排程" msgid "Schedule a new pipeline" msgstr "建立流水線 (pipeline) 排程" msgid "Scheduling Pipelines" -msgstr "流水線 (pipeline) 計劃" +msgstr "流水線 (pipeline) 排程" msgid "Search branches and tags" msgstr "搜尋分支 (branch) 和標籤" @@ -849,8 +861,7 @@ msgid "" "specific branches or tags. Those scheduled pipelines will inherit limited " "project access based on their associated user." msgstr "" -"在指定了特定分支 (branch) 或標籤後,此處的流水線 (pipeline) 排程會不斷地重複執行。\n" -"流水線排程的存取權限與專案本身相同。" +"在指定了特定分支 (branch) 或標籤後,此處的流水線 (pipeline) 排程會不斷地重複執行。流水線排程的存取權限與專案本身相同。" msgid "" "The planning stage shows the time from the previous step to pushing your " @@ -1097,6 +1108,14 @@ msgid "Withdraw Access Request" msgstr "取消權限申請" msgid "" +"You are going to remove %{group_name}.\n" +"Removed groups CANNOT be restored!\n" +"Are you ABSOLUTELY sure?" +msgstr "即將要刪除 %{group_name}。\n" +"被刪除的群組完全無法救回來喔!\n" +"真的「100%確定」要這麼做嗎?" + +msgid "" "You are going to remove %{project_name_with_namespace}.\n" "Removed project CANNOT be restored!\n" "Are you ABSOLUTELY sure?" @@ -48,7 +48,14 @@ module QA module Main autoload :Entry, 'qa/page/main/entry' autoload :Menu, 'qa/page/main/menu' - autoload :Groups, 'qa/page/main/groups' + end + + module Dashboard + autoload :Groups, 'qa/page/dashboard/groups' + end + + module Group + autoload :Show, 'qa/page/group/show' end module Project diff --git a/qa/qa/page/main/groups.rb b/qa/qa/page/dashboard/groups.rb index 169c5ebc967..3690f40dcfe 100644 --- a/qa/qa/page/main/groups.rb +++ b/qa/qa/page/dashboard/groups.rb @@ -1,9 +1,11 @@ module QA module Page - module Main + module Dashboard class Groups < Page::Base def prepare_test_namespace - return if page.has_content?(Runtime::Namespace.name) + if page.has_content?(Runtime::Namespace.name) + return click_link(Runtime::Namespace.name) + end click_on 'New group' diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb new file mode 100644 index 00000000000..296c311d7c6 --- /dev/null +++ b/qa/qa/page/group/show.rb @@ -0,0 +1,11 @@ +module QA + module Page + module Group + class Show < Page::Base + def go_to_new_project + click_link 'New Project' + end + end + end + end +end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index f7c2086d0dd..7ce4e9009f5 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -14,13 +14,6 @@ module QA within_user_menu { click_link 'Admin area' } end - def go_to_new_project - within_user_menu do - find('.header-new-dropdown-toggle').click - click_link('New project') - end - end - def sign_out within_user_menu do find('.header-user-dropdown-toggle').click diff --git a/qa/qa/scenario/gitlab/project/create.rb b/qa/qa/scenario/gitlab/project/create.rb index 99d0fc42a94..b860701c304 100644 --- a/qa/qa/scenario/gitlab/project/create.rb +++ b/qa/qa/scenario/gitlab/project/create.rb @@ -13,8 +13,8 @@ module QA def perform Page::Main::Menu.act { go_to_groups } - Page::Main::Groups.act { prepare_test_namespace } - Page::Main::Menu.act { go_to_new_project } + Page::Dashboard::Groups.act { prepare_test_namespace } + Page::Group::Show.act { go_to_new_project } Page::Project::New.perform do |page| page.choose_test_namespace diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 2b4e8723b48..a22fd8eaf9b 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' describe SessionsController do + include DeviseHelpers + describe '#new' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end context 'when auto sign-in is enabled' do @@ -34,7 +36,7 @@ describe SessionsController do describe '#create' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end context 'when using standard authentications' do @@ -257,7 +259,7 @@ describe SessionsController do describe '#new' do before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end it 'redirects correctly for referer on same host with params' do diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index 1cd1f016674..4740402dc01 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -21,6 +21,16 @@ feature 'Issues > User uses quick actions', feature: true, js: true do wait_for_requests end + describe 'time tracking' do + let(:issue) { create(:issue, project: project) } + + before do + visit project_issue_path(project, issue) + end + + it_behaves_like 'issuable time tracker' + end + describe 'adding a due date from note' do let(:issue) { create(:issue, project: project) } @@ -99,32 +109,6 @@ feature 'Issues > User uses quick actions', feature: true, js: true do end end - describe 'Issuable time tracking' do - let(:issue) { create(:issue, project: project) } - - before do - project.team << [user, :developer] - end - - context 'Issue' do - before do - visit project_issue_path(project, issue) - end - - it_behaves_like 'issuable time tracker' - end - - context 'Merge Request' do - let(:merge_request) { create(:merge_request, source_project: project) } - - before do - visit project_merge_request_path(project, merge_request) - end - - it_behaves_like 'issuable time tracker' - end - end - describe 'toggling the WIP prefix from the title from note' do let(:issue) { create(:issue, project: project) } diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb index 434f5a7c0ac..b2187e01bdb 100644 --- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb +++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb @@ -24,6 +24,14 @@ feature 'Merge Requests > User uses quick actions', feature: true, js: true do wait_for_requests end + describe 'time tracking' do + before do + visit project_merge_request_path(project, merge_request) + end + + it_behaves_like 'issuable time tracker' + end + describe 'toggling the WIP prefix in the title from note' do context 'when the current user can toggle the WIP prefix' do it 'adds the WIP: prefix to the title' do diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb index 42764e808e6..0064c9ef25e 100644 --- a/spec/features/oauth_login_spec.rb +++ b/spec/features/oauth_login_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' feature 'OAuth Login', js: true do + include DeviseHelpers + def enter_code(code) fill_in 'user_otp_attempt', with: code click_button 'Verify code' @@ -8,7 +10,7 @@ feature 'OAuth Login', js: true do def stub_omniauth_config(provider) OmniAuth.config.add_mock(provider, OmniAuth::AuthHash.new(provider: provider.to_s, uid: "12345")) - Rails.application.env_config['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: Rails.application) Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider] end diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 033ccf06124..de6dd8fc8a6 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -70,6 +70,17 @@ feature 'Pipeline Schedules', :feature, js: true do expect(first('.branch-name-cell').text).to eq('') end end + + context 'when ref is empty' do + before do + pipeline_schedule.update_attribute(:ref, '') + visit_pipelines_schedules + end + + it 'shows a list of the pipeline schedules with empty ref column' do + expect(first('.branch-name-cell').text).to eq('') + end + end end describe 'POST /projects/pipeline_schedules/new' do @@ -128,6 +139,19 @@ feature 'Pipeline Schedules', :feature, js: true do end end end + + context 'when ref is empty' do + before do + pipeline_schedule.update_attribute(:ref, '') + edit_pipeline_schedule + end + + it 'shows the pipeline schedule with default ref' do + page.within('.js-target-branch-dropdown') do + expect(first('.dropdown-toggle-text').text).to eq('master') + end + end + end end context 'when user creates a new pipeline schedule with variables' do diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb index 584d3ed8f42..3319b0fedf3 100644 --- a/spec/features/projects/services/mattermost_slash_command_spec.rb +++ b/spec/features/projects/services/mattermost_slash_command_spec.rb @@ -159,7 +159,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do it 'shows the correct trigger url' do value = find_field('request_url').value - expect(value).to match("api/v3/projects/#{project.id}/services/mattermost_slash_commands/trigger") + expect(value).to match("api/v4/projects/#{project.id}/services/mattermost_slash_commands/trigger") end it 'shows a token placeholder' do diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb index 4efe484262a..71f5a8d7a4e 100644 --- a/spec/features/projects/services/slack_slash_command_spec.rb +++ b/spec/features/projects/services/slack_slash_command_spec.rb @@ -40,6 +40,6 @@ feature 'Slack slash commands', feature: true do it 'shows the correct trigger url' do value = find_field('url').value - expect(value).to match("api/v3/projects/#{project.id}/services/slack_slash_commands/trigger") + expect(value).to match("api/v4/projects/#{project.id}/services/slack_slash_commands/trigger") end end diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/javascripts/fixtures/u2f.rb index c9c0b891237..e3d7986f2cf 100644 --- a/spec/javascripts/fixtures/u2f.rb +++ b/spec/javascripts/fixtures/u2f.rb @@ -10,10 +10,12 @@ context 'U2F' do end describe SessionsController, '(JavaScript fixtures)', type: :controller do + include DeviseHelpers + render_views before do - @request.env['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: @request) end it 'u2f/authenticate.html.raw' do |example| diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb index 13f0338b6aa..3a132fb9989 100644 --- a/spec/lib/gitlab/ci/trace/stream_spec.rb +++ b/spec/lib/gitlab/ci/trace/stream_spec.rb @@ -300,5 +300,12 @@ describe Gitlab::Ci::Trace::Stream do include_examples 'malicious regexp' end + + context 'multi-line data with rooted regexp' do + let(:data) { "\n65%\n" } + let(:regex) { '^(\d+)\%$' } + + it { is_expected.to eq('65') } + end end end diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 60de91324f0..730fdb112d9 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -91,7 +91,7 @@ describe Gitlab::Git::Commit, seed_helper: true do committer: committer ) end - let(:commit) { described_class.new(gitaly_commit) } + let(:commit) { described_class.new(Gitlab::GitalyClient::Commit.new(repository, gitaly_commit)) } it { expect(commit.short_id).to eq(id[0..10]) } it { expect(commit.id).to eq(id) } @@ -290,69 +290,85 @@ describe Gitlab::Git::Commit, seed_helper: true do end describe '.find_all' do - it 'should return a return a collection of commits' do - commits = described_class.find_all(repository) + shared_examples 'finding all commits' do + it 'should return a return a collection of commits' do + commits = described_class.find_all(repository) - expect(commits).not_to be_empty - expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) - end - - context 'while applying a sort order based on the `order` option' do - it "allows ordering topologically (no parents shown before their children)" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) - - described_class.find_all(repository, order: :topo) + expect(commits).to all( be_a_kind_of(Gitlab::Git::Commit) ) end - it "allows ordering by date" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) - - described_class.find_all(repository, order: :date) + context 'max_count' do + subject do + commits = Gitlab::Git::Commit.find_all( + repository, + max_count: 50 + ) + + commits.map(&:id) + end + + it 'has 33 elements' do + expect(subject.size).to eq(33) + end + + it 'includes the expected commits' do + expect(subject).to include( + SeedRepo::Commit::ID, + SeedRepo::Commit::PARENT_ID, + SeedRepo::FirstCommit::ID + ) + end end - it "applies no sorting by default" do - expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) - - described_class.find_all(repository) + context 'ref + max_count + skip' do + subject do + commits = Gitlab::Git::Commit.find_all( + repository, + ref: 'master', + max_count: 50, + skip: 1 + ) + + commits.map(&:id) + end + + it 'has 24 elements' do + expect(subject.size).to eq(24) + end + + it 'includes the expected commits' do + expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID) + expect(subject).not_to include(SeedRepo::LastCommit::ID) + end end end - context 'max_count' do - subject do - commits = Gitlab::Git::Commit.find_all( - repository, - max_count: 50 - ) + context 'when Gitaly find_all_commits feature is enabled' do + it_behaves_like 'finding all commits' + end - commits.map { |c| c.id } - end + context 'when Gitaly find_all_commits feature is disabled', skip_gitaly_mock: true do + it_behaves_like 'finding all commits' - it 'has 31 elements' do - expect(subject.size).to eq(33) - end - it { is_expected.to include(SeedRepo::Commit::ID) } - it { is_expected.to include(SeedRepo::Commit::PARENT_ID) } - it { is_expected.to include(SeedRepo::FirstCommit::ID) } - end + context 'while applying a sort order based on the `order` option' do + it "allows ordering topologically (no parents shown before their children)" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_TOPO) - context 'ref + max_count + skip' do - subject do - commits = Gitlab::Git::Commit.find_all( - repository, - ref: 'master', - max_count: 50, - skip: 1 - ) + described_class.find_all(repository, order: :topo) + end - commits.map { |c| c.id } - end + it "allows ordering by date" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_DATE | Rugged::SORT_TOPO) + + described_class.find_all(repository, order: :date) + end + + it "applies no sorting by default" do + expect_any_instance_of(Rugged::Walker).to receive(:sorting).with(Rugged::SORT_NONE) - it 'has 23 elements' do - expect(subject.size).to eq(24) + described_class.find_all(repository) + end end - it { is_expected.to include(SeedRepo::Commit::ID) } - it { is_expected.to include(SeedRepo::FirstCommit::ID) } - it { is_expected.not_to include(SeedRepo::LastCommit::ID) } end end end diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb index 4b76a43e6b5..98ddd3c3664 100644 --- a/spec/lib/gitlab/git/tree_spec.rb +++ b/spec/lib/gitlab/git/tree_spec.rb @@ -1,8 +1,9 @@ require "spec_helper" describe Gitlab::Git::Tree, seed_helper: true do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } + context :repo do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH) } let(:tree) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID) } it { expect(tree).to be_kind_of Array } @@ -74,4 +75,24 @@ describe Gitlab::Git::Tree, seed_helper: true do it { expect(submodule.name).to eq('gitlab-shell') } end end + + describe '#where' do + context 'with gitaly disabled' do + before do + allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false) + end + + it 'calls #tree_entries_from_rugged' do + expect(described_class).to receive(:tree_entries_from_rugged) + + described_class.where(repository, SeedRepo::Commit::ID, '/') + end + end + + it 'gets the tree entries from GitalyClient' do + expect_any_instance_of(Gitlab::GitalyClient::CommitService).to receive(:tree_entries) + + described_class.where(repository, SeedRepo::Commit::ID, '/') + end + end end diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb index b3b4a1e2218..0868c793a33 100644 --- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb @@ -2,9 +2,13 @@ require 'spec_helper' describe Gitlab::GitalyClient::CommitService do let(:project) { create(:project, :repository) } + let(:storage_name) { project.repository_storage } + let(:relative_path) { project.path_with_namespace + '.git' } let(:repository) { project.repository } let(:repository_message) { repository.gitaly_repository } - let(:commit) { project.commit('913c66a37b4a45b9769037c55c2d238bd0942d2e') } + let(:revision) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } + let(:commit) { project.commit(revision) } + let(:client) { described_class.new(repository) } describe '#diff_from_parent' do context 'when a commit has a parent' do @@ -20,7 +24,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) - described_class.new(repository).diff_from_parent(commit) + client.diff_from_parent(commit) end end @@ -38,12 +42,12 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_diff).with(request, kind_of(Hash)) - described_class.new(repository).diff_from_parent(initial_commit) + client.diff_from_parent(initial_commit) end end it 'returns a Gitlab::Git::DiffCollection' do - ret = described_class.new(repository).diff_from_parent(commit) + ret = client.diff_from_parent(commit) expect(ret).to be_kind_of(Gitlab::Git::DiffCollection) end @@ -53,7 +57,7 @@ describe Gitlab::GitalyClient::CommitService do expect(Gitlab::Git::DiffCollection).to receive(:new).with(kind_of(Enumerable), options) - described_class.new(repository).diff_from_parent(commit, options) + client.diff_from_parent(commit, options) end end @@ -68,7 +72,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) - described_class.new(repository).commit_deltas(commit) + client.commit_deltas(commit) end end @@ -83,7 +87,7 @@ describe Gitlab::GitalyClient::CommitService do expect_any_instance_of(Gitaly::DiffService::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([]) - described_class.new(repository).commit_deltas(initial_commit) + client.commit_deltas(initial_commit) end end end @@ -91,6 +95,7 @@ describe Gitlab::GitalyClient::CommitService do describe '#between' do let(:from) { 'master' } let(:to) { '4b825dc642cb6eb9a060e54bf8d69288fbee4904' } + it 'sends an RPC request' do request = Gitaly::CommitsBetweenRequest.new( repository: repository_message, from: from, to: to @@ -102,4 +107,17 @@ describe Gitlab::GitalyClient::CommitService do described_class.new(repository).between(from, to) end end + + describe '#tree_entries' do + let(:path) { '/' } + + it 'sends a get_tree_entries message' do + expect_any_instance_of(Gitaly::CommitService::Stub) + .to receive(:get_tree_entries) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return([]) + + client.tree_entries(repository, revision, path) + end + end end diff --git a/spec/lib/gitlab/untrusted_regexp_spec.rb b/spec/lib/gitlab/untrusted_regexp_spec.rb index 66045917cb3..a2ef2a27e4c 100644 --- a/spec/lib/gitlab/untrusted_regexp_spec.rb +++ b/spec/lib/gitlab/untrusted_regexp_spec.rb @@ -46,10 +46,28 @@ describe Gitlab::UntrustedRegexp do context 'malicious regexp' do let(:text) { malicious_text } let(:regexp) { malicious_regexp } - + include_examples 'malicious regexp' end + context 'empty regexp' do + let(:regexp) { '' } + let(:text) { 'foo' } + + it 'returns an array of empty matches' do + is_expected.to eq(['', '', '']) + end + end + + context 'empty capture group regexp' do + let(:regexp) { '()' } + let(:text) { 'foo' } + + it 'returns arrays of empty matches in an array' do + is_expected.to eq([[''], [''], ['']]) + end + end + context 'no capture group' do let(:regexp) { '.+' } let(:text) { 'foo' } diff --git a/spec/support/devise_helpers.rb b/spec/support/devise_helpers.rb new file mode 100644 index 00000000000..890a2d9d287 --- /dev/null +++ b/spec/support/devise_helpers.rb @@ -0,0 +1,14 @@ +module DeviseHelpers + # explicitly tells Devise which mapping to use + # this is needed when we are testing a Devise controller bypassing the router + def set_devise_mapping(context:) + env = + if context.respond_to?(:env_config) + context.env_config + elsif context.respond_to?(:env) + context.env + end + + env['devise.mapping'] = Devise.mappings[:user] if env + end +end diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb index b410a652126..c714d1b08a6 100644 --- a/spec/support/login_helpers.rb +++ b/spec/support/login_helpers.rb @@ -1,4 +1,6 @@ module LoginHelpers + include DeviseHelpers + # Internal: Log in as a specific user or a new user of a specific role # # user_or_role - User object, or a role to create (e.g., :admin, :user) @@ -62,7 +64,7 @@ module LoginHelpers visit new_user_session_path expect(page).to have_content('Sign in with') - check 'Remember Me' if remember_me + check 'remember_me' if remember_me click_link "oauth-login-#{provider}" end @@ -106,7 +108,7 @@ module LoginHelpers end def stub_omniauth_saml_config(messages) - Rails.application.env_config['devise.mapping'] = Devise.mappings[:user] + set_devise_mapping(context: Rails.application) Rails.application.routes.disable_clear_and_finalize = true Rails.application.routes.draw do post '/users/auth/saml' => 'omniauth_callbacks#saml' diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index 66e598e2691..d5bc12f3bc5 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -5,7 +5,7 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') - within('.new_protected_branch') do + within('.js-new-protected-branch') do allowed_to_push_button = find(".js-allowed-to-push") unless allowed_to_push_button.text == access_type_name @@ -50,7 +50,7 @@ shared_examples "protected branches > access control > CE" do set_protected_branch_name('master') - within('.new_protected_branch') do + within('.js-new-protected-branch') do allowed_to_merge_button = find(".js-allowed-to-merge") unless allowed_to_merge_button.text == access_type_name diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb index 91cc684d032..d34617be474 100644 --- a/spec/tasks/gitlab/task_helpers_spec.rb +++ b/spec/tasks/gitlab/task_helpers_spec.rb @@ -20,7 +20,6 @@ describe Gitlab::TaskHelpers do it 'checkout the version and reset to it' do expect(subject).to receive(:checkout_version).with(tag, clone_path) - expect(subject).to receive(:reset_to_version).with(tag, clone_path) subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end @@ -31,7 +30,6 @@ describe Gitlab::TaskHelpers do it 'checkout the version and reset to it with a branch name' do expect(subject).to receive(:checkout_version).with(branch, clone_path) - expect(subject).to receive(:reset_to_version).with(branch, clone_path) subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path) end @@ -70,20 +68,11 @@ describe Gitlab::TaskHelpers do describe '#checkout_version' do it 'clones the repo in the target dir' do expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet]) + .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet origin #{tag}]) expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}]) + .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout -f --quiet FETCH_HEAD --]) subject.checkout_version(tag, clone_path) end end - - describe '#reset_to_version' do - it 'resets --hard to the given version' do - expect(subject) - .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}]) - - subject.reset_to_version(tag, clone_path) - end - end end |