diff options
27 files changed, 216 insertions, 154 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd40b82434..c57ba82e38c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,17 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. -## 8.15.0 (2017-01-22) +## 8.15.1 (2016-12-23) + +- Push payloads schedule at most 100 commits, instead of all commits. +- Fix Mattermost command creation by specifying username. +- Do not override incoming webhook for mattermost and slack. +- Adds background color for disabled state to merge when succeeds dropdown. !8222 +- Standardises font-size for titles in Issues, Merge Requests and Merge Request widget. !8235 +- Fix Pipeline builds list blank on MR. !8255 +- Do not show retried builds in pipeline stage dropdown. !8260 + +## 8.15.0 (2016-12-22) - Whitelist next project names: notes, services. - Use Grape's new Route methods. diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 6ac4ec6ea0d..b512da0939f 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -43,7 +43,7 @@ ul.notes { } .system-note-message { - display: inline; + display: inline-block; &::first-letter { text-transform: lowercase; @@ -55,7 +55,7 @@ ul.notes { } p { - display: inline; + display: inline-block; margin: 0; &::first-letter { @@ -151,10 +151,6 @@ ul.notes { } } } - - .note-headline-light { - display: inline; - } } .discussion-body { @@ -452,11 +448,6 @@ ul.notes { border-radius: $border-radius-base; } -.diff-file .note .note-actions { - right: 0; - top: 0; -} - /** * Line note button on the side of diffs @@ -590,3 +581,19 @@ ul.notes { } } } + +// Merge request notes in diffs +.diff-file { + + // Diff is side by side + .notes_content.parallel .note-header .note-headline-light { + display: block; + position: relative; + } + + // Diff is inline + .notes_content .note-header .note-headline-light { + display: inline-block; + position: relative; + } +} diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 93c8b6c9a1e..5f8874289cc 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -201,7 +201,7 @@ width: 8px; position: absolute; right: -7px; - bottom: 10px; + top: 10px; border-bottom: 2px solid $border-color; } } @@ -784,11 +784,72 @@ .mini-pipeline-graph { .builds-dropdown { background-color: transparent; - border: none; padding: 0; color: $gl-text-color-light; border: none; margin: 0; + + &:focus, + &:hover { + outline: none; + margin-right: -8px; + + .ci-status-icon { + width: 32px; + padding: 0 8px 0 0; + transition: width 0.1s cubic-bezier(0.25, 0, 1, 1); + + + .dropdown-caret { + visibility: visible; + opacity: 1; + } + } + } + + &:focus, + &:active { + .ci-status-icon-success { + background-color: rgba($gl-success, .1); + } + + .ci-status-icon-failed { + background-color: rgba($gl-danger, .1); + } + + .ci-status-icon-pending, + .ci-status-icon-success_with_warnings { + background-color: rgba($gl-warning, .1); + } + + .ci-status-icon-running { + background-color: rgba($blue-normal, .1); + } + + .ci-status-icon-canceled, + .ci-status-icon-disabled, + .ci-status-icon-not-found { + background-color: rgba($gl-gray, .1); + } + + .ci-status-icon-created, + .ci-status-icon-skipped { + background-color: rgba($gray-darkest, .1); + } + } + + .mini-pipeline-graph-icon-container { + .dropdown-caret { + font-size: 11px; + position: absolute; + top: 6px; + left: 20px; + margin-right: -6px; + z-index: 2; + visibility: hidden; + opacity: 0; + transition: visibility 0.1s, opacity 0.1s linear; + } + } } .dropdown-build .build-content { @@ -849,7 +910,7 @@ height: 22px; position: relative; z-index: 2; - transition: all 0.2s cubic-bezier(0.25, 0, 1, 1); + transition: all 0.1s cubic-bezier(0.25, 0, 1, 1); svg { top: -1px; @@ -862,75 +923,6 @@ height: 22px; } -.builds-dropdown { - &:focus { - outline: none; - margin-right: -8px; - - .ci-status-icon { - width: 32px; - padding: 0 8px 0 0; - transition: width 0.2s cubic-bezier(0.25, 0, 1, 1); - - + .dropdown-caret { - display: inline-block; - } - } - } - - &:focus, - &:active { - .ci-status-icon-success { - background-color: rgba($gl-success, .1); - } - - .ci-status-icon-failed { - background-color: rgba($gl-danger, .1); - } - - .ci-status-icon-pending, - .ci-status-icon-success_with_warnings { - background-color: rgba($gl-warning, .1); - } - - .ci-status-icon-running { - background-color: rgba($blue-normal, .1); - } - - .ci-status-icon-canceled, - .ci-status-icon-disabled, - .ci-status-icon-not-found { - background-color: rgba($gl-gray, .1); - } - - .ci-status-icon-created, - .ci-status-icon-skipped { - background-color: rgba($gray-darkest, .1); - } - } - - .mini-pipeline-graph-icon-container { - .ci-status-icon:hover, - .ci-status-icon:focus { - width: 32px; - padding: 0 8px 0 0; - - + .dropdown-caret { - display: inline-block; - } - } - - .dropdown-caret { - font-size: 11px; - position: relative; - top: 3px; - left: -14px; - margin-right: -6px; - display: none; - z-index: 2; - } - } -} .terminal-icon { margin-left: 3px; diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index f2f6453b3b9..6894a5763ff 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -93,11 +93,8 @@ module Ci .select("max(#{quoted_table_name}.id)") .group(:ref, :sha) - if ref - where(id: max_id, ref: ref) - else - where(id: max_id) - end + relation = ref ? where(ref: ref) : self + relation.where(id: max_id).order(id: :desc) end def self.latest_status(ref = nil) diff --git a/app/models/project.rb b/app/models/project.rb index 26fa20f856d..72fdd4514c4 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -418,7 +418,7 @@ class Project < ActiveRecord::Base repository.commit(ref) end - # ref can't be HEAD, can only be branch/tag name or SHA + # ref can't be HEAD or SHA, can only be branch/tag name def latest_successful_builds_for(ref = default_branch) latest_pipeline = pipelines.latest_successful_for(ref) diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index b0556987721..b7ef44c3054 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -49,11 +49,13 @@ class ChatNotificationService < Service return false unless message - opt = {} + channel_name = get_channel_field(object_kind).presence || channel - opt[:channel] = get_channel_field(object_kind).presence || channel || default_channel - opt[:username] = username if username - notifier = Slack::Notifier.new(webhook, opt) + opts = {} + opts[:channel] = channel_name if channel_name + opts[:username] = username if username + + notifier = Slack::Notifier.new(webhook, opts) notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback) true @@ -71,7 +73,7 @@ class ChatNotificationService < Service fields.reject { |field| field[:name].end_with?('channel') } end - def default_channel + def default_channel_placeholder raise NotImplementedError end @@ -103,7 +105,7 @@ class ChatNotificationService < Service def build_event_channels supported_events.reduce([]) do |channels, event| - channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel } + channels << { type: 'text', name: event_channel_name(event), placeholder: default_channel_placeholder } end end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb index 0650f930402..ee8a0b55275 100644 --- a/app/models/project_services/mattermost_service.rb +++ b/app/models/project_services/mattermost_service.rb @@ -35,7 +35,7 @@ class MattermostService < ChatNotificationService ] end - def default_channel + def default_channel_placeholder "#town-square" end end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 0583470d3b5..76d233a3cca 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -34,7 +34,7 @@ class SlackService < ChatNotificationService ] end - def default_channel + def default_channel_placeholder "#general" end end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 185556c12cc..6bbc3a9d9ff 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -3,6 +3,9 @@ class GitPushService < BaseService include Gitlab::CurrentSettings include Gitlab::Access + # The N most recent commits to process in a single push payload. + PROCESS_COMMIT_LIMIT = 100 + # This method will be called after each git update # and only if the provided user and project are present in GitLab. # @@ -77,6 +80,16 @@ class GitPushService < BaseService ProjectCacheWorker.perform_async(@project.id, types) end + # Schedules processing of commit messages. + def process_commit_messages + default = is_default_branch? + + push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit| + ProcessCommitWorker. + perform_async(project.id, current_user.id, commit.to_hash, default) + end + end + protected def execute_related_hooks @@ -128,17 +141,6 @@ class GitPushService < BaseService end end - # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, - # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. - def process_commit_messages - default = is_default_branch? - - @push_commits.each do |commit| - ProcessCommitWorker. - perform_async(project.id, current_user.id, commit.to_hash, default) - end - end - def build_push_data @push_data ||= Gitlab::DataBuilder::Push.build( @project, diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 778a32e6345..399cf85cd0f 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -10,7 +10,7 @@ .timeline-content .note-header = link_to_member(note.project, note.author, avatar: false) - .inline.note-headline-light + .note-headline-light = note.author.to_reference - unless note.system commented diff --git a/changelogs/unreleased/25905-mr-when-succeeds-dropdown.yml b/changelogs/unreleased/25905-mr-when-succeeds-dropdown.yml deleted file mode 100644 index 39ce0b66768..00000000000 --- a/changelogs/unreleased/25905-mr-when-succeeds-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Adds background color for disabled state to merge when succeeds dropdown -merge_request: 8222 -author: diff --git a/changelogs/unreleased/25906-title-size.yml b/changelogs/unreleased/25906-title-size.yml deleted file mode 100644 index d92d06197e9..00000000000 --- a/changelogs/unreleased/25906-title-size.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Standardises font-size for titles in Issues, Merge Requests and Merge Request widget -merge_request: 8235 -author: diff --git a/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml b/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml new file mode 100644 index 00000000000..54a461c24ed --- /dev/null +++ b/changelogs/unreleased/25930-discussion-actions-overlap-header-text.yml @@ -0,0 +1,4 @@ +--- +title: Fix discussion overlap text in regular screens +merge_request: 8273 +author: diff --git a/changelogs/unreleased/25961-spec-list-blank.yml b/changelogs/unreleased/25961-spec-list-blank.yml deleted file mode 100644 index 835def027a7..00000000000 --- a/changelogs/unreleased/25961-spec-list-blank.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Pipeline builds list blank on MR -merge_request: 8255 -author: diff --git a/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml b/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml new file mode 100644 index 00000000000..501f0b25a21 --- /dev/null +++ b/changelogs/unreleased/26018-mini-pipeline-hover-cross-broswer.yml @@ -0,0 +1,4 @@ +--- +title: Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari +merge_request: 8282 +author: diff --git a/changelogs/unreleased/fix-hide-retried-builds-in-pipeline-stage-dropdown.yml b/changelogs/unreleased/fix-hide-retried-builds-in-pipeline-stage-dropdown.yml deleted file mode 100644 index 66256d7ed0e..00000000000 --- a/changelogs/unreleased/fix-hide-retried-builds-in-pipeline-stage-dropdown.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Do not show retried builds in pipeline stage dropdown -merge_request: 8260 -author: diff --git a/changelogs/unreleased/fix-latest-pipeine-ordering.yml b/changelogs/unreleased/fix-latest-pipeine-ordering.yml new file mode 100644 index 00000000000..3dbd1ba036a --- /dev/null +++ b/changelogs/unreleased/fix-latest-pipeine-ordering.yml @@ -0,0 +1,4 @@ +--- +title: Fix finding the latest pipeline +merge_request: 8286 +author: diff --git a/changelogs/unreleased/fix-mattermost-username.yml b/changelogs/unreleased/fix-mattermost-username.yml deleted file mode 100644 index ca298e4d008..00000000000 --- a/changelogs/unreleased/fix-mattermost-username.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix Mattermost command creation by specifying username -merge_request: -author: diff --git a/config/initializers/workhorse_multipart.rb b/config/initializers/workhorse_multipart.rb index 3e2f25c354a..8de7140e3d4 100644 --- a/config/initializers/workhorse_multipart.rb +++ b/config/initializers/workhorse_multipart.rb @@ -1,3 +1,19 @@ Rails.application.configure do |config| config.middleware.use(Gitlab::Middleware::Multipart) end + +module Gitlab + module StrongParameterScalars + GITLAB_PERMITTED_SCALAR_TYPES = [::UploadedFile] + + def permitted_scalar?(value) + super || GITLAB_PERMITTED_SCALAR_TYPES.any? { |type| value.is_a?(type) } + end + end +end + +module ActionController + class Parameters + prepend Gitlab::StrongParameterScalars + end +end diff --git a/lib/gitlab/chat_commands/deploy.rb b/lib/gitlab/chat_commands/deploy.rb index 6bb854dc080..0f70323810d 100644 --- a/lib/gitlab/chat_commands/deploy.rb +++ b/lib/gitlab/chat_commands/deploy.rb @@ -49,8 +49,9 @@ module Gitlab end def url(subject) - polymorphic_url( - [ subject.project.namespace.becomes(Namespace), subject.project, subject ]) + project = subject.project + + namespace_project_build_url(project.namespace.becomes(Namespace), project, subject) end end end diff --git a/lib/gitlab/chat_commands/presenter.rb b/lib/gitlab/chat_commands/presenter.rb index caceaa25391..8930a21f406 100644 --- a/lib/gitlab/chat_commands/presenter.rb +++ b/lib/gitlab/chat_commands/presenter.rb @@ -30,12 +30,12 @@ module Gitlab if subject.is_a?(Gitlab::ChatCommands::Result) show_result(subject) elsif subject.respond_to?(:count) - if subject.many? - multiple_resources(subject) - elsif subject.none? + if subject.none? not_found + elsif subject.one? + single_resource(subject.first) else - single_resource(subject) + multiple_resources(subject) end else single_resource(subject) @@ -71,9 +71,9 @@ module Gitlab end def multiple_resources(resources) - resources.map! { |resource| title(resource) } + titles = resources.map { |resource| title(resource) } - message = header_with_list("Multiple results were found:", resources) + message = header_with_list("Multiple results were found:", titles) ephemeral_response(message) end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 5c958455604..b071fe480e6 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -371,23 +371,25 @@ describe 'Issues', feature: true do describe 'when I want to reset my incoming email token' do let(:project1) { create(:project, namespace: @user.namespace) } - let(:issue) { create(:issue, project: project1) } + let!(:issue) { create(:issue, project: project1) } before do - allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true) + stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") project1.team << [@user, :master] - project1.issues << issue visit namespace_project_issues_path(@user.namespace, project1) end it 'changes incoming email address token', js: true do find('.issue-email-modal-btn').click previous_token = find('input#issue_email').value - find('.incoming-email-token-reset').click - wait_for_ajax - expect(find('input#issue_email').value).not_to eq(previous_token) + expect(page).to have_no_field('issue_email', with: previous_token) + new_token = project1.new_issue_address(@user.reload) + expect(page).to have_field( + 'issue_email', + with: new_token + ) end end diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/chat_commands/command_spec.rb index a0ec8884635..a2d84977f58 100644 --- a/spec/lib/gitlab/chat_commands/command_spec.rb +++ b/spec/lib/gitlab/chat_commands/command_spec.rb @@ -54,6 +54,30 @@ describe Gitlab::ChatCommands::Command, service: true do end end + context 'searching for an issue' do + let(:params) { { text: 'issue search find me' } } + let!(:issue) { create(:issue, project: project, title: 'find me') } + + before do + project.team << [user, :master] + end + + context 'a single issue is found' do + it 'presents the issue' do + expect(subject[:text]).to match(issue.title) + end + end + + context 'multiple issues found' do + let!(:issue2) { create(:issue, project: project, title: "someone find me") } + + it 'shows a link to the new issue' do + expect(subject[:text]).to match(issue.title) + expect(subject[:text]).to match(issue2.title) + end + end + end + context 'when trying to do deployment' do let(:params) { { text: 'deploy staging to production' } } let!(:build) { create(:ci_build, project: project) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index dc377d15f15..b28da6daabf 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -424,20 +424,18 @@ describe Ci::Pipeline, models: true do context 'when no ref is specified' do let(:pipelines) { described_class.latest.all } - it 'returns the latest pipeline for the same ref and different sha' do - expect(pipelines.map(&:sha)).to contain_exactly('A', 'B', 'C') - expect(pipelines.map(&:status)). - to contain_exactly('success', 'failed', 'skipped') + it 'gives the latest pipelines for the same ref and different sha in reverse chronological order' do + expect(pipelines.map(&:sha)).to eq(%w[C B A]) + expect(pipelines.map(&:status)).to eq(%w[skipped failed success]) end end context 'when ref is specified' do let(:pipelines) { described_class.latest('ref').all } - it 'returns the latest pipeline for ref and different sha' do - expect(pipelines.map(&:sha)).to contain_exactly('A', 'B') - expect(pipelines.map(&:status)). - to contain_exactly('success', 'failed') + it 'gives the latest pipelines for ref and different sha in reverse chronological order' do + expect(pipelines.map(&:sha)).to eq(%w[B A]) + expect(pipelines.map(&:status)).to eq(%w[failed success]) end end end diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb index 672ced68681..c879edddfdd 100644 --- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb +++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb @@ -6,7 +6,7 @@ describe MattermostSlashCommandsService, :models do context 'Mattermost API' do let(:project) { create(:empty_project) } let(:service) { project.build_mattermost_slash_commands_service } - let(:user) { create(:user)} + let(:user) { create(:user) } before do Mattermost::Session.base_uri("http://mattermost.example.com") diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index e7624e70725..3303e808a9c 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -604,6 +604,25 @@ describe GitPushService, services: true do end end + describe '#process_commit_messages' do + let(:service) do + described_class.new(project, + user, + oldrev: sample_commit.parent_id, + newrev: sample_commit.id, + ref: 'refs/heads/master') + end + + it 'only schedules a limited number of commits' do + allow(service).to receive(:push_commits). + and_return(Array.new(1000, double(:commit, to_hash: {}))) + + expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times + + service.process_commit_messages + end + end + def execute_service(project, user, oldrev, newrev, ref) service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref ) service.execute diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb index 8582aea5fe5..74d9b8c6313 100644 --- a/spec/support/slack_mattermost_notifications_shared_examples.rb +++ b/spec/support/slack_mattermost_notifications_shared_examples.rb @@ -105,7 +105,7 @@ RSpec.shared_examples 'slack or mattermost notifications' do allow(chat_service).to receive(:username).and_return(username) expect(Slack::Notifier).to receive(:new). - with(webhook_url, username: username, channel: chat_service.default_channel). + with(webhook_url, username: username). and_return( double(:slack_service).as_null_object ) |