diff options
23 files changed, 165 insertions, 56 deletions
diff --git a/CHANGELOG b/CHANGELOG index d133909748b..7a19af6b765 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ v 8.12.0 (unreleased) - Update gitlab shell secret file also when it is empty. !3774 (glensc) - Give project selection dropdowns responsive width, make non-wrapping. - Make push events have equal vertical spacing. + - API: Ensure invitees are not returned in Members API. - Add two-factor recovery endpoint to internal API !5510 - Pass the "Remember me" value to the U2F authentication form - Only update projects.last_activity_at once per hour when creating a new event @@ -24,9 +25,11 @@ v 8.12.0 (unreleased) - Change logo animation to CSS (ClemMakesApps) - Instructions for enabling Git packfile bitmaps !6104 - Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint + - Fix long comments in diffs messing with table width - Fix pagination on user snippets page - Run CI builds with the permissions of users !5735 - Fix sorting of issues in API + - Fix download artifacts button links !6407 - Sort project variables by key. !6275 (Diego Souza) - Ensure specs on sorting of issues in API are deterministic on MySQL - Added ability to use predefined CI variables for environment name @@ -56,6 +59,7 @@ v 8.12.0 (unreleased) - Add hover color to emoji icon (ClemMakesApps) - Increase ci_builds artifacts_size column to 8-byte integer to allow larger files - Add textarea autoresize after comment (ClemMakesApps) + - Do not write SSH public key 'comments' to authorized_keys !6381 - Refresh todos count cache when an Issue/MR is deleted - Fix branches page dropdown sort alignment (ClemMakesApps) - Hides merge request button on branches page is user doesn't have permissions @@ -602,6 +606,7 @@ v 8.10.0 - Export and import avatar as part of project import/export - Fix migration corrupting import data for old version upgrades - Show tooltip on GitLab export link in new project page + - Fix import_data wrongly saved as a result of an invalid import_url !5206 v 8.9.9 - Exclude some pending or inactivated rows in Member scopes @@ -622,12 +627,6 @@ v 8.9.6 - Keeps issue number when importing from Gitlab.com - Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska) -v 8.9.7 (unreleased) - - Fix import_data wrongly saved as a result of an invalid import_url - -v 8.9.6 - - Fix importing of events under notes for GitLab projects - v 8.9.5 - Add more debug info to import/export and memory killer. !5108 - Fixed avatar alignment in new MR view. !5095 @@ -1893,7 +1892,7 @@ v 8.1.3 - Use issue editor as cross reference comment author when issue is edited with a new mention - Add Facebook authentication -v 8.1.1 +v 8.1.2 - Fix cloning Wiki repositories via HTTP (Stan Hu) - Add migration to remove satellites directory - Fix specific runners visibility diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 6f4eebdf6f6..100435be135 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -0.8.1 +0.8.2 diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 10abeb50f4b..78d21c0552a 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -27,10 +27,11 @@ $(document).off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.toggleSidebar); $(window).off('resize.build').on('resize.build', this.hideSidebar); $(document).off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown); + $('#js-build-scroll > a').off('click').on('click', this.stepTrace); this.updateArtifactRemoveDate(); if ($('#build-trace').length) { this.getInitialBuildTrace(); - this.initScrollButtonAffix(); + this.initScrollButtons(); } if (this.build_status === "running" || this.build_status === "pending") { $('#autoscroll-button').on('click', function() { @@ -106,7 +107,7 @@ } }; - Build.prototype.initScrollButtonAffix = function() { + Build.prototype.initScrollButtons = function() { var $body, $buildScroll, $buildTrace; $buildScroll = $('#js-build-scroll'); $body = $('body'); @@ -165,6 +166,14 @@ this.populateJobs(stage); }; + Build.prototype.stepTrace = function(e) { + e.preventDefault(); + $currentTarget = $(e.currentTarget); + $.scrollTo($currentTarget.attr('href'), { + offset: -($('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight()) + }); + }; + return Build; })(); diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 087f27d9f4c..c05cda25bbd 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -607,13 +607,15 @@ selectedObject = this.renderedData[selectedIndex]; } } + field = []; + fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName(selectedObject) : this.options.fieldName; value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id; if (isInput) { field = $(this.el); - } else { - field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + escape(value) + "']"); + } else if(value) { + field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value.toString().replace(/'/g, '\\\'') + "']"); } - if (el.hasClass(ACTIVE_CLASS)) { + if (field.length && el.hasClass(ACTIVE_CLASS)) { el.removeClass(ACTIVE_CLASS); if (isInput) { field.val(''); @@ -623,7 +625,7 @@ } else if (el.hasClass(INDETERMINATE_CLASS)) { el.addClass(ACTIVE_CLASS); el.removeClass(INDETERMINATE_CLASS); - if (value == null) { + if (field.length && value == null) { field.remove(); } if (!field.length && fieldName) { @@ -636,7 +638,7 @@ this.dropdown.parent().find("input[name='" + fieldName + "']").remove(); } } - if (value == null) { + if (field.length && value == null) { field.remove(); } // Toggle active class for the tick mark @@ -644,7 +646,7 @@ if (value != null) { if (!field.length && fieldName) { this.addInput(fieldName, value, selectedObject); - } else { + } else if (field.length) { field.val(value).trigger('change'); } } @@ -794,4 +796,4 @@ }); }; -}).call(this); +}).call(this);
\ No newline at end of file diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index 29a967a35a0..3f15a117ca8 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -166,7 +166,7 @@ instance.addInput(this.fieldName, label.id); } } - if ($form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + escape(this.id(label)) + "']").length) { + if (this.id(label) && $form.find("input[type='hidden'][name='" + ($dropdown.data('fieldName')) + "'][value='" + this.id(label).toString().replace(/'/g, '\\\'') + "']").length) { selectedClass.push('is-active'); } if ($dropdown.hasClass('js-multiselect') && removesAll) { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 21cee2e3a70..b8ef76cc74e 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -68,6 +68,11 @@ border-collapse: separate; margin: 0; padding: 0; + table-layout: fixed; + + .diff-line-num { + width: 50px; + } .line_holder td { line-height: $code_line_height; @@ -98,10 +103,6 @@ } tr.line_holder.parallel { - .old_line, .new_line { - min-width: 50px; - } - td.line_content.parallel { width: 46%; } diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 5f5e071eb40..24de020917a 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -37,6 +37,6 @@ %li.dropdown-header Previous Artifacts - artifacts.each do |job| %li - = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, ref, 'download', job: job.name), rel: 'nofollow' do + = link_to latest_succeeded_namespace_project_artifacts_path(project.namespace, project, "#{ref}/download", job: job.name), rel: 'nofollow' do %i.fa.fa-download %span Download '#{job.name}' diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml index 24a1a616919..d34d28f6736 100644 --- a/app/views/shared/issuable/_label_dropdown.html.haml +++ b/app/views/shared/issuable/_label_dropdown.html.haml @@ -12,7 +12,7 @@ - if params[:label_name].present? - if params[:label_name].respond_to?('any?') - params[:label_name].each do |label| - = hidden_field_tag "label_name[]", u(label), id: nil + = hidden_field_tag "label_name[]", label, id: nil .dropdown %button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data} %span.dropdown-toggle-text diff --git a/doc/install/installation.md b/doc/install/installation.md index df98655c396..3ac813aa914 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -400,7 +400,7 @@ If you are not using Linux you may have to run `gmake` instead of 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 v0.8.1 + sudo -u git -H git checkout v0.8.2 sudo -u git -H make ### Initialize Database and Activate Advanced Features diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index 8017c36587e..686c7e8e7b5 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -82,7 +82,7 @@ GitLab 8.1. ```bash cd /home/git/gitlab-workhorse sudo -u git -H git fetch --all -sudo -u git -H git checkout v0.8.1 +sudo -u git -H git checkout v0.8.2 sudo -u git -H make ``` diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index d02b469dac8..29a97ccbd75 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -20,7 +20,7 @@ module API access_requesters = paginate(source.requesters.includes(:user)) - present access_requesters.map(&:user), with: Entities::AccessRequester, access_requesters: access_requesters + present access_requesters.map(&:user), with: Entities::AccessRequester, source: source end # Request access to the group/project diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0235ba3d580..92a6f29adb0 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -105,18 +105,18 @@ module API class Member < UserBasic expose :access_level do |user, options| - member = options[:member] || options[:members].find { |m| m.user_id == user.id } + member = options[:member] || options[:source].members.find_by(user_id: user.id) member.access_level end expose :expires_at do |user, options| - member = options[:member] || options[:members].find { |m| m.user_id == user.id } + member = options[:member] || options[:source].members.find_by(user_id: user.id) member.expires_at end end class AccessRequester < UserBasic expose :requested_at do |user, options| - access_requester = options[:access_requester] || options[:access_requesters].find { |m| m.user_id == user.id } + access_requester = options[:access_requester] || options[:source].requesters.find_by(user_id: user.id) access_requester.requested_at end end diff --git a/lib/api/members.rb b/lib/api/members.rb index 94c16710d9a..37f0a6512f4 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -18,11 +18,11 @@ module API get ":id/members" do source = find_source(source_type, params[:id]) - members = source.members.includes(:user) - members = members.joins(:user).merge(User.search(params[:query])) if params[:query] - members = paginate(members) + users = source.users + users = users.merge(User.search(params[:query])) if params[:query] + users = paginate(users) - present members.map(&:user), with: Entities::Member, members: members + present users, with: Entities::Member, source: source end # Get a group/project member diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index c412249a01e..79eac66b364 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -6,7 +6,12 @@ module Gitlab KeyAdder = Struct.new(:io) do def add_key(id, key) - key.gsub!(/[[:space:]]+/, ' ').strip! + key = Gitlab::Shell.strip_key(key) + # Newline and tab are part of the 'protocol' used to transmit id+key to the other end + if key.include?("\t") || key.include?("\n") + raise Error.new("Invalid key: #{key.inspect}") + end + io.puts("#{id}\t#{key}") end end @@ -16,6 +21,10 @@ module Gitlab @version_required ||= File.read(Rails.root. join('GITLAB_SHELL_VERSION')).strip end + + def strip_key(key) + key.split(/ /)[0, 2].join(' ') + end end # Init new repository @@ -107,7 +116,7 @@ module Gitlab # def add_key(key_id, key_content) Gitlab::Utils.system_silent([gitlab_shell_keys_path, - 'add-key', key_id, key_content]) + 'add-key', key_id, self.class.strip_key(key_content)]) end # Batch-add keys to authorized_keys diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb index 9100719da87..82cb8cef754 100644 --- a/lib/gitlab/ldap/adapter.rb +++ b/lib/gitlab/ldap/adapter.rb @@ -70,7 +70,7 @@ module Gitlab private def user_options(field, value, limit) - options = { attributes: %W(#{config.uid} cn mail dn) } + options = { attributes: user_attributes } options[:size] = limit if limit if field.to_sym == :dn @@ -98,6 +98,10 @@ module Gitlab filter end end + + def user_attributes + %W(#{config.uid} cn mail dn) + end end end end diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh index bc6e4d94061..fb4d8463981 100755 --- a/scripts/lint-doc.sh +++ b/scripts/lint-doc.sh @@ -10,6 +10,15 @@ then exit 1 fi +# Ensure that the CHANGELOG does not contain duplicate versions +DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^v [0-9.]+' CHANGELOG | sed 's| (unreleased)||' | sort | uniq -d) +if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ] +then + echo '✖ ERROR: Duplicate versions in CHANGELOG:' >&2 + echo "${DUPLICATE_CHANGELOG_VERSIONS}" >&2 + exit 1 +fi + echo "✔ Linting passed" exit 0 diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb index 0e9f814044e..69fda27cc61 100644 --- a/spec/features/issues/filter_issues_spec.rb +++ b/spec/features/issues/filter_issues_spec.rb @@ -101,7 +101,7 @@ describe 'Filter issues', feature: true do expect(find('.js-label-select .dropdown-toggle-text')).to have_content('No Label') end - it 'filters by no label' do + it 'filters by a label' do find('.dropdown-menu-labels a', text: label.title).click page.within '.labels-filter' do expect(page).to have_content label.title @@ -109,7 +109,7 @@ describe 'Filter issues', feature: true do expect(find('.js-label-select .dropdown-toggle-text')).to have_content(label.title) end - it 'filters by wont fix labels' do + it "filters by `won't fix` and another label" do find('.dropdown-menu-labels a', text: label.title).click page.within '.labels-filter' do expect(page).to have_content wontfix.title @@ -117,6 +117,33 @@ describe 'Filter issues', feature: true do end expect(find('.js-label-select .dropdown-toggle-text')).to have_content(wontfix.title) end + + it "filters by `won't fix` label followed by another label after page load" do + find('.dropdown-menu-labels a', text: wontfix.title).click + # Close label dropdown to load + find('body').click + expect(find('.filtered-labels')).to have_content(wontfix.title) + + find('.js-label-select').click + wait_for_ajax + find('.dropdown-menu-labels a', text: label.title).click + # Close label dropdown to load + find('body').click + expect(find('.filtered-labels')).to have_content(label.title) + + find('.js-label-select').click + wait_for_ajax + expect(find('.dropdown-menu-labels li', text: wontfix.title)).to have_css('.is-active') + expect(find('.dropdown-menu-labels li', text: label.title)).to have_css('.is-active') + end + + it "selects and unselects `won't fix`" do + find('.dropdown-menu-labels a', text: wontfix.title).click + find('.dropdown-menu-labels a', text: wontfix.title).click + # Close label dropdown to load + find('body').click + expect(page).not_to have_css('.filtered-labels') + end end describe 'Filter issues for assignee and label from issues#index' do diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb index 04058300570..92028c19361 100644 --- a/spec/features/projects/branches/download_buttons_spec.rb +++ b/spec/features/projects/branches/download_buttons_spec.rb @@ -33,7 +33,11 @@ feature 'Download buttons in branches page', feature: true do end scenario 'shows download artifacts button' do - expect(page).to have_link "Download '#{build.name}'" + href = latest_succeeded_namespace_project_artifacts_path( + project.namespace, project, 'binary-encoding/download', + job: 'build') + + expect(page).to have_link "Download '#{build.name}'", href: href end end end diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb index be5cebcd7c9..d7c29a7e074 100644 --- a/spec/features/projects/files/download_buttons_spec.rb +++ b/spec/features/projects/files/download_buttons_spec.rb @@ -34,7 +34,11 @@ feature 'Download buttons in files tree', feature: true do end scenario 'shows download artifacts button' do - expect(page).to have_link "Download '#{build.name}'" + href = latest_succeeded_namespace_project_artifacts_path( + project.namespace, project, "#{project.default_branch}/download", + job: 'build') + + expect(page).to have_link "Download '#{build.name}'", href: href end end end diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb index b26c0ea7a14..227ccf9459c 100644 --- a/spec/features/projects/main/download_buttons_spec.rb +++ b/spec/features/projects/main/download_buttons_spec.rb @@ -33,7 +33,11 @@ feature 'Download buttons in project main page', feature: true do end scenario 'shows download artifacts button' do - expect(page).to have_link "Download '#{build.name}'" + href = latest_succeeded_namespace_project_artifacts_path( + project.namespace, project, "#{project.default_branch}/download", + job: 'build') + + expect(page).to have_link "Download '#{build.name}'", href: href end end end diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb index 6e0022c179f..dd93d25c2c6 100644 --- a/spec/features/projects/tags/download_buttons_spec.rb +++ b/spec/features/projects/tags/download_buttons_spec.rb @@ -34,7 +34,11 @@ feature 'Download buttons in tags page', feature: true do end scenario 'shows download artifacts button' do - expect(page).to have_link "Download '#{build.name}'" + href = latest_succeeded_namespace_project_artifacts_path( + project.namespace, project, "#{tag}/download", + job: 'build') + + expect(page).to have_link "Download '#{build.name}'", href: href end end end diff --git a/spec/lib/gitlab/backend/shell_spec.rb b/spec/lib/gitlab/backend/shell_spec.rb index 6e5ba211382..07407f212aa 100644 --- a/spec/lib/gitlab/backend/shell_spec.rb +++ b/spec/lib/gitlab/backend/shell_spec.rb @@ -1,4 +1,5 @@ require 'spec_helper' +require 'stringio' describe Gitlab::Shell, lib: true do let(:project) { double('Project', id: 7, path: 'diaspora') } @@ -44,15 +45,38 @@ describe Gitlab::Shell, lib: true do end end + describe '#add_key' do + it 'removes trailing garbage' do + allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path) + expect(Gitlab::Utils).to receive(:system_silent).with( + [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar'] + ) + + gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage') + end + end + describe Gitlab::Shell::KeyAdder, lib: true do describe '#add_key' do - it 'normalizes space characters in the key' do - io = spy + it 'removes trailing garbage' do + io = spy(:io) adder = described_class.new(io) - adder.add_key('key-42', "sha-rsa foo\tbar\tbaz") + adder.add_key('key-42', "ssh-rsa foo bar\tbaz") + + expect(io).to have_received(:puts).with("key-42\tssh-rsa foo") + end + + it 'raises an exception if the key contains a tab' do + expect do + described_class.new(StringIO.new).add_key('key-42', "ssh-rsa\tfoobar") + end.to raise_error(Gitlab::Shell::Error) + end - expect(io).to have_received(:puts).with("key-42\tsha-rsa foo bar baz") + it 'raises an exception if the key contains a newline' do + expect do + described_class.new(StringIO.new).add_key('key-42', "ssh-rsa foobar\nssh-rsa pawned") + end.to raise_error(Gitlab::Shell::Error) end end end diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 1e365bf353a..92032f09b17 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -30,20 +30,29 @@ describe API::Members, api: true do let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) } end - context 'when authenticated as a non-member' do - %i[access_requester stranger].each do |type| - context "as a #{type}" do - it 'returns 200' do - user = public_send(type) - get api("/#{source_type.pluralize}/#{source.id}/members", user) + %i[master developer access_requester stranger].each do |type| + context "when authenticated as a #{type}" do + it 'returns 200' do + user = public_send(type) + get api("/#{source_type.pluralize}/#{source.id}/members", user) - expect(response).to have_http_status(200) - expect(json_response.size).to eq(2) - end + expect(response).to have_http_status(200) + expect(json_response.size).to eq(2) + expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id] end end end + it 'does not return invitees' do + create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil) + + get api("/#{source_type.pluralize}/#{source.id}/members", developer) + + expect(response).to have_http_status(200) + expect(json_response.size).to eq(2) + expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id] + end + it 'finds members with query string' do get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: master.username |