diff options
Diffstat (limited to 'app')
41 files changed, 276 insertions, 148 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue index bb66ab36283..41670b45798 100644 --- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue +++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue @@ -48,10 +48,13 @@ export default { noteableType: this.noteableType, noteTargetLine: this.noteTargetLine, diffViewType: this.diffViewType, - diffFile: this.getDiffFileByHash(this.diffFileHash), + diffFile: this.diffFile, linePosition: this.linePosition, }; }, + diffFile() { + return this.getDiffFileByHash(this.diffFileHash); + }, }, mounted() { if (this.isLoggedIn) { @@ -102,6 +105,7 @@ export default { :line-code="line.line_code" :line="line" :help-page-path="helpPagePath" + :diff-file="diffFile" save-button-title="Comment" class="diff-comment-form" @handleFormUpdateAddToReview="addToReview" diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index 57d6b181bd7..471323bfc83 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -61,6 +61,11 @@ export default { required: false, default: null, }, + diffFile: { + type: Object, + required: false, + default: null, + }, helpPagePath: { type: String, required: false, @@ -102,9 +107,42 @@ export default { } return '#'; }, + diffParams() { + if (this.diffFile) { + return { + filePath: this.diffFile.file_path, + refs: this.diffFile.diff_refs, + }; + } else if (this.note && this.note.position) { + return { + filePath: this.note.position.new_path, + refs: this.note.position, + }; + } else if (this.discussion && this.discussion.diff_file) { + return { + filePath: this.discussion.diff_file.file_path, + refs: this.discussion.diff_file.diff_refs, + }; + } + + return null; + }, markdownPreviewPath() { const notable = this.getNoteableDataByProp('preview_note_path'); - return mergeUrlParams({ preview_suggestions: true }, notable); + + const previewSuggestions = this.line && this.diffParams; + const params = previewSuggestions + ? { + preview_suggestions: previewSuggestions, + line: this.line.new_line, + file_path: this.diffParams.filePath, + base_sha: this.diffParams.refs.base_sha, + start_sha: this.diffParams.refs.start_sha, + head_sha: this.diffParams.refs.head_sha, + } + : {}; + + return mergeUrlParams(params, notable); }, markdownDocsPath() { return this.getNotesDataByProp('markdownDocsPath'); @@ -234,8 +272,8 @@ export default { placeholder="Write a comment or drag your files here…" @keydown.meta.enter="handleKeySubmit()" @keydown.ctrl.enter="handleKeySubmit()" - @keydown.up="editMyLastNote()" - @keydown.esc="cancelHandler(true)" + @keydown.exact.up="editMyLastNote()" + @keydown.exact.esc="cancelHandler(true)" @input="onInput" ></textarea> </markdown-field> diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue index 706e6ca19c3..57125c78cf6 100644 --- a/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue +++ b/app/assets/javascripts/sidebar/components/todo_toggle/todo.vue @@ -50,6 +50,9 @@ export default { buttonLabel() { return this.isTodo ? MARK_TEXT : TODO_TEXT; }, + buttonTooltip() { + return !this.collapsed ? undefined : this.buttonLabel; + }, collapsedButtonIconClasses() { return this.isTodo ? 'todo-undone' : ''; }, @@ -69,7 +72,7 @@ export default { <button v-tooltip :class="buttonClasses" - :title="buttonLabel" + :title="buttonTooltip" :aria-label="buttonLabel" :data-issuable-id="issuableId" :data-issuable-type="issuableType" diff --git a/app/assets/javascripts/vue_shared/components/lib/utils/diff_utils.js b/app/assets/javascripts/vue_shared/components/lib/utils/diff_utils.js new file mode 100644 index 00000000000..d1aba99ac22 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/lib/utils/diff_utils.js @@ -0,0 +1,20 @@ +/* eslint-disable import/prefer-default-export */ + +function trimFirstCharOfLineContent(text) { + if (!text) { + return text; + } + + return text.replace(/^( |\+|-)/, ''); +} + +function cleanSuggestionLine(line = {}) { + return { + ...line, + text: trimFirstCharOfLineContent(line.text), + }; +} + +export function selectDiffLines(lines) { + return lines.filter(line => line.type !== 'match').map(line => cleanSuggestionLine(line)); +} diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index eccf73e227c..0f3b3568414 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -76,6 +76,7 @@ export default { hasSuggestion: false, markdownPreviewLoading: false, previewMarkdown: false, + suggestions: this.note.suggestions || [], }; }, computed: { @@ -109,9 +110,6 @@ export default { } return lineNumber; }, - suggestions() { - return this.note.suggestions || []; - }, lineType() { return this.line ? this.line.type : ''; }, @@ -175,6 +173,7 @@ export default { this.referencedCommands = data.references.commands; this.referencedUsers = data.references.users; this.hasSuggestion = data.references.suggestions && data.references.suggestions.length; + this.suggestions = data.references.suggestions; } this.$nextTick() diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index cc6ecdb0395..a5a5b2ef415 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -38,7 +38,7 @@ export default { ].join('\n'); }, mdSuggestion() { - return ['```suggestion', `{text}`, '```'].join('\n'); + return ['```suggestion:-0+0', `{text}`, '```'].join('\n'); }, }, mounted() { diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue index a351ca62c94..2eb4ec12a4a 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff.vue @@ -1,24 +1,14 @@ <script> import SuggestionDiffHeader from './suggestion_diff_header.vue'; +import SuggestionDiffRow from './suggestion_diff_row.vue'; +import { selectDiffLines } from '../lib/utils/diff_utils'; export default { components: { SuggestionDiffHeader, + SuggestionDiffRow, }, props: { - newLines: { - type: Array, - required: true, - }, - fromContent: { - type: String, - required: false, - default: '', - }, - fromLine: { - type: Number, - required: true, - }, suggestion: { type: Object, required: true, @@ -33,6 +23,11 @@ export default { required: true, }, }, + computed: { + lines() { + return selectDiffLines(this.suggestion.diff_lines); + }, + }, methods: { applySuggestion(callback) { this.$emit('apply', { suggestionId: this.suggestion.id, callback }); @@ -52,22 +47,11 @@ export default { /> <table class="mb-3 md-suggestion-diff js-syntax-highlight code"> <tbody> - <!-- Old Line --> - <tr class="line_holder old"> - <td class="diff-line-num old_line qa-old-diff-line-number old">{{ fromLine }}</td> - <td class="diff-line-num new_line old"></td> - <td class="line_content old"> - <span>{{ fromContent }}</span> - </td> - </tr> - <!-- New Line(s) --> - <tr v-for="(line, key) of newLines" :key="key" class="line_holder new"> - <td class="diff-line-num old_line new"></td> - <td class="diff-line-num new_line qa-new-diff-line-number new">{{ line.lineNumber }}</td> - <td class="line_content new"> - <span>{{ line.content }}</span> - </td> - </tr> + <suggestion-diff-row + v-for="(line, index) of lines" + :key="`${index}-${line.text}`" + :line="line" + /> </tbody> </table> </div> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue new file mode 100644 index 00000000000..cafd3a515ea --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue @@ -0,0 +1,32 @@ +<script> +export default { + name: 'SuggestionDiffRow', + props: { + line: { + type: Object, + required: true, + }, + }, + computed: { + lineType() { + return this.line.type; + }, + }, +}; +</script> + +<template> + <tr class="line_holder" :class="lineType"> + <td class="diff-line-num old_line" :class="lineType"> + {{ line.old_line }} + </td> + <td class="diff-line-num new_line" :class="lineType"> + {{ line.new_line }} + </td> + <td class="line_content" :class="lineType"> + <span v-if="line.text">{{ line.text }}</span> + <!-- TODO: replace this hack with zero-width whitespace when we have rich_text from BE --> + <span v-else>​</span> + </td> + </tr> +</template> diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue index 177d78cb904..8d3705e1e4a 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue @@ -6,16 +6,6 @@ import Flash from '~/flash'; export default { components: { SuggestionDiff }, props: { - fromLine: { - type: Number, - required: false, - default: 0, - }, - fromContent: { - type: String, - required: false, - default: '', - }, lineType: { type: String, required: false, @@ -71,41 +61,19 @@ export default { suggestionElements.forEach((suggestionEl, i) => { const suggestionParentEl = suggestionEl.parentElement; - const newLines = this.extractNewLines(suggestionParentEl); - const diffComponent = this.generateDiff(newLines, i); + const diffComponent = this.generateDiff(i); diffComponent.$mount(suggestionParentEl); }); this.isRendered = true; }, - extractNewLines(suggestionEl) { - // extracts the suggested lines from the markdown - // calculates a line number for each line - - const newLines = suggestionEl.querySelectorAll('.line'); - const fromLine = this.suggestions.length ? this.suggestions[0].from_line : this.fromLine; - const lines = []; - - newLines.forEach((line, i) => { - const content = `${line.innerText}\n`; - const lineNumber = fromLine + i; - lines.push({ content, lineNumber }); - }); - - return lines; - }, - generateDiff(newLines, suggestionIndex) { - // generates the diff <suggestion-diff /> component - // all `suggestion` markdown will be swapped out by this component - + generateDiff(suggestionIndex) { const { suggestions, disabled, helpPagePath } = this; const suggestion = suggestions && suggestions[suggestionIndex] ? suggestions[suggestionIndex] : {}; - const fromContent = suggestion.from_content || this.fromContent; - const fromLine = suggestion.from_line || this.fromLine; const SuggestionDiffComponent = Vue.extend(SuggestionDiff); const suggestionDiff = new SuggestionDiffComponent({ - propsData: { newLines, fromLine, fromContent, disabled, suggestion, helpPagePath }, + propsData: { disabled, suggestion, helpPagePath }, }); suggestionDiff.$on('apply', ({ suggestionId, callback }) => { diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index b90db135b4a..efcd35a2e0e 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -287,7 +287,7 @@ list-style: none; padding: 0 1px; - a, + a:not(.btn), button, .menu-item { @include dropdown-link; @@ -351,6 +351,10 @@ // Expects up to 3 digits on the badge margin-right: 40px; } + + .dropdown-menu-content { + padding: $dropdown-item-padding-y $dropdown-item-padding-x; + } } .droplab-dropdown { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 02364180c35..54d985df9b5 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -154,11 +154,17 @@ .swipe-wrap { overflow: hidden; - border-left: 1px solid $gl-gray-400; + border-right: 1px solid $gl-gray-400; position: absolute; display: block; top: 13px; right: 7px; + + &.left-oriented { + /* only for commit view (different swipe viewer) */ + border-right: 0; + border-left: 1px solid $gl-gray-400; + } } .swipe-bar { diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb index f72d25fc54c..2a9729b6ffd 100644 --- a/app/controllers/concerns/preview_markdown.rb +++ b/app/controllers/concerns/preview_markdown.rb @@ -20,7 +20,7 @@ module PreviewMarkdown body: view_context.markdown(result[:text], markdown_params), references: { users: result[:users], - suggestions: result[:suggestions], + suggestions: SuggestionSerializer.new.represent_diff(result[:suggestions]), commands: view_context.markdown(result[:commands]) } } diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 4eeaeb860ee..3b4215b766e 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -23,7 +23,7 @@ class Projects::RepositoriesController < Projects::ApplicationController append_sha = false if @filename == shortname end - send_git_archive @repository, ref: @ref, format: params[:format], append_sha: append_sha + send_git_archive @repository, ref: @ref, path: params[:path], format: params[:format], append_sha: append_sha rescue => ex logger.error("#{self.class.name}: #{ex}") git_not_found! diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index 06d26309b5b..ecc34eacc7d 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -5,6 +5,7 @@ class GitlabSchema < GraphQL::Schema use Gitlab::Graphql::Authorize use Gitlab::Graphql::Present use Gitlab::Graphql::Connections + use Gitlab::Graphql::Tracing query(Types::QueryType) diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb index 18696293b97..de7d6570a3e 100644 --- a/app/graphql/types/ci/pipeline_type.rb +++ b/app/graphql/types/ci/pipeline_type.rb @@ -3,10 +3,12 @@ module Types module Ci class PipelineType < BaseObject - expose_permissions Types::PermissionTypes::Ci::Pipeline - graphql_name 'Pipeline' + authorize :read_pipeline + + expose_permissions Types::PermissionTypes::Ci::Pipeline + field :id, GraphQL::ID_TYPE, null: false field :iid, GraphQL::ID_TYPE, null: false diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb index 5ad3ea52930..adb137dfee3 100644 --- a/app/graphql/types/issue_type.rb +++ b/app/graphql/types/issue_type.rb @@ -2,10 +2,12 @@ module Types class IssueType < BaseObject - expose_permissions Types::PermissionTypes::Issue - graphql_name 'Issue' + authorize :read_issue + + expose_permissions Types::PermissionTypes::Issue + present_using IssuePresenter field :iid, GraphQL::ID_TYPE, null: false @@ -15,16 +17,14 @@ module Types field :author, Types::UserType, null: false, - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find }, - authorize: :read_user + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find } field :assignees, Types::UserType.connection_type, null: true field :labels, Types::LabelType.connection_type, null: true field :milestone, Types::MilestoneType, null: true, - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find }, - authorize: :read_milestone + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } field :due_date, Types::TimeType, null: true field :confidential, GraphQL::BOOLEAN_TYPE, null: false diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 1ed27a14e33..120ffe0dfde 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -2,12 +2,14 @@ module Types class MergeRequestType < BaseObject + graphql_name 'MergeRequest' + + authorize :read_merge_request + expose_permissions Types::PermissionTypes::MergeRequest present_using MergeRequestPresenter - graphql_name 'MergeRequest' - field :id, GraphQL::ID_TYPE, null: false field :iid, GraphQL::ID_TYPE, null: false field :title, GraphQL::STRING_TYPE, null: false @@ -48,7 +50,7 @@ module Types field :downvotes, GraphQL::INT_TYPE, null: false field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false - field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline, authorize: :read_pipeline + field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline field :pipelines, Types::Ci::PipelineType.connection_type, resolver: Resolvers::MergeRequestPipelinesResolver end diff --git a/app/graphql/types/milestone_type.rb b/app/graphql/types/milestone_type.rb index af31b572c9a..2772fbec86f 100644 --- a/app/graphql/types/milestone_type.rb +++ b/app/graphql/types/milestone_type.rb @@ -4,6 +4,8 @@ module Types class MilestoneType < BaseObject graphql_name 'Milestone' + authorize :read_milestone + field :description, GraphQL::STRING_TYPE, null: true field :title, GraphQL::STRING_TYPE, null: false field :state, GraphQL::STRING_TYPE, null: false diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index b96c2f3afb2..fbb4eddd13c 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -2,10 +2,12 @@ module Types class ProjectType < BaseObject - expose_permissions Types::PermissionTypes::Project - graphql_name 'Project' + authorize :read_project + + expose_permissions Types::PermissionTypes::Project + field :id, GraphQL::ID_TYPE, null: false field :full_path, GraphQL::ID_TYPE, null: false @@ -67,14 +69,12 @@ module Types field :merge_requests, Types::MergeRequestType.connection_type, null: true, - resolver: Resolvers::MergeRequestsResolver, - authorize: :read_merge_request + resolver: Resolvers::MergeRequestsResolver field :merge_request, Types::MergeRequestType, null: true, - resolver: Resolvers::MergeRequestsResolver.single, - authorize: :read_merge_request + resolver: Resolvers::MergeRequestsResolver.single field :issues, Types::IssueType.connection_type, @@ -88,7 +88,7 @@ module Types field :pipelines, Types::Ci::PipelineType.connection_type, - null: false, + null: true, resolver: Resolvers::ProjectPipelinesResolver end end diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 472fe5d6ec2..0f655ab9d03 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -7,8 +7,7 @@ module Types field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver, - description: "Find a project", - authorize: :read_project + description: "Find a project" field :metadata, Types::MetadataType, null: true, diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index a13e65207df..6b53554314b 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -4,6 +4,8 @@ module Types class UserType < BaseObject graphql_name 'User' + authorize :read_user + present_using UserPresenter field :name, GraphQL::STRING_TYPE, null: false diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index f2abb241753..009dd70c2c9 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -299,6 +299,10 @@ module ProjectsHelper }.to_json end + def directory? + @path.present? + end + private def get_project_nav_tabs(project, current_user) diff --git a/app/models/repository.rb b/app/models/repository.rb index 574ce12b309..51ab2247a03 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -299,13 +299,14 @@ class Repository end end - def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:) + def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:, path: nil) raw_repository.archive_metadata( ref, storage_path, project.path, format, - append_sha: append_sha + append_sha: append_sha, + path: path ) end diff --git a/app/presenters/ci/bridge_presenter.rb b/app/presenters/ci/bridge_presenter.rb new file mode 100644 index 00000000000..ee11cffe355 --- /dev/null +++ b/app/presenters/ci/bridge_presenter.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Ci + class BridgePresenter < CommitStatusPresenter + def detailed_status + @detailed_status ||= subject.detailed_status(user) + end + end +end diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index c3f7d4651fb..914ad628a99 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -42,6 +42,6 @@ class IssueEntity < IssuableEntity end expose :preview_note_path do |issue| - preview_markdown_path(issue.project, quick_actions_target_type: 'Issue', quick_actions_target_id: issue.iid) + preview_markdown_path(issue.project, target_type: 'Issue', target_id: issue.iid) end end diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index d673f8ae896..4831eb32c96 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -235,7 +235,7 @@ class MergeRequestWidgetEntity < IssuableEntity end expose :preview_note_path do |merge_request| - preview_markdown_path(merge_request.project, quick_actions_target_type: 'MergeRequest', quick_actions_target_id: merge_request.iid) + preview_markdown_path(merge_request.project, target_type: 'MergeRequest', target_id: merge_request.iid) end expose :merge_commit_path do |merge_request| diff --git a/app/serializers/suggestion_entity.rb b/app/serializers/suggestion_entity.rb index 4d0d4da10be..2dd62e19e29 100644 --- a/app/serializers/suggestion_entity.rb +++ b/app/serializers/suggestion_entity.rb @@ -3,6 +3,8 @@ class SuggestionEntity < API::Entities::Suggestion include RequestAwareEntity + unexpose :from_line, :to_line, :from_content, :to_content + expose :diff_lines, using: DiffLineEntity expose :current_user do expose :can_apply do |suggestion| Ability.allowed?(current_user, :apply_suggestion, suggestion) diff --git a/app/serializers/suggestion_serializer.rb b/app/serializers/suggestion_serializer.rb new file mode 100644 index 00000000000..010344f9fcd --- /dev/null +++ b/app/serializers/suggestion_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class SuggestionSerializer < BaseSerializer + entity SuggestionEntity + + def represent_diff(resource) + represent(resource, { only: [:diff_lines] }) + end +end diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb index c38b2656260..adaa68b1efb 100644 --- a/app/services/clusters/applications/base_helm_service.rb +++ b/app/services/clusters/applications/base_helm_service.rb @@ -13,16 +13,20 @@ module Clusters def log_error(error) meta = { - exception: error.class.name, error_code: error.respond_to?(:error_code) ? error.error_code : nil, service: self.class.name, app_id: app.id, project_ids: app.cluster.project_ids, - group_ids: app.cluster.group_ids, - message: error.message + group_ids: app.cluster.group_ids } - logger.error(meta) + logger_meta = meta.merge( + exception: error.class.name, + message: error.message, + backtrace: Gitlab::Profiler.clean_backtrace(error.backtrace) + ) + + logger.error(logger_meta) Gitlab::Sentry.track_acceptable_exception(error, extra: meta) end diff --git a/app/services/concerns/suggestible.rb b/app/services/concerns/suggestible.rb index 0b9822b1909..0cba9bf1b8a 100644 --- a/app/services/concerns/suggestible.rb +++ b/app/services/concerns/suggestible.rb @@ -2,10 +2,17 @@ module Suggestible extend ActiveSupport::Concern + include Gitlab::Utils::StrongMemoize # This translates into limiting suggestion changes to `suggestion:-100+100`. MAX_LINES_CONTEXT = 100.freeze + def diff_lines + strong_memoize(:diff_lines) do + Gitlab::Diff::SuggestionDiff.new(self).diff_lines + end + end + def fetch_from_content diff_file.new_blob_lines_between(from_line, to_line).join end diff --git a/app/services/groups/base_service.rb b/app/services/groups/base_service.rb index 8c8acce5ca5..019cd047ae9 100644 --- a/app/services/groups/base_service.rb +++ b/app/services/groups/base_service.rb @@ -7,5 +7,11 @@ module Groups def initialize(group, user, params = {}) @group, @current_user, @params = group, user, params.dup end + + private + + def remove_unallowed_params + # overridden in EE + end end end diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb index 99ead467f74..74aad3b1c94 100644 --- a/app/services/groups/create_service.rb +++ b/app/services/groups/create_service.rb @@ -8,6 +8,8 @@ module Groups end def execute + remove_unallowed_params + @group = Group.new(params) after_build_hook(@group, params) diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb index 787445180f0..73e1e00dc33 100644 --- a/app/services/groups/update_service.rb +++ b/app/services/groups/update_service.rb @@ -6,6 +6,7 @@ module Groups def execute reject_parent_id! + remove_unallowed_params return false unless valid_visibility_level_change?(group, params[:visibility_level]) diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 3e208241da5..f968e3693da 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -55,15 +55,7 @@ module MergeRequests end def create_pipeline_for(merge_request, user) - return unless Feature.enabled?(:ci_merge_request_pipeline, - merge_request.source_project, - default_enabled: true) - - ## - # UpdateMergeRequestsWorker could be retried by an exception. - # MR pipelines should not be recreated in such case. - return if merge_request.merge_request_pipeline_exists? - return if merge_request.has_no_commits? + return unless can_create_pipeline_for?(merge_request) create_detached_merge_request_pipeline(merge_request, user) end @@ -80,6 +72,16 @@ module MergeRequests end end + def can_create_pipeline_for?(merge_request) + ## + # UpdateMergeRequestsWorker could be retried by an exception. + # pipelines for merge request should not be recreated in such case. + return false if merge_request.merge_request_pipeline_exists? + return false if merge_request.has_no_commits? + + true + end + def can_use_merge_request_ref?(merge_request) Feature.enabled?(:ci_use_merge_request_ref, project, default_enabled: true) && !merge_request.for_fork? diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb index c1655c38095..7386530f45f 100644 --- a/app/services/preview_markdown_service.rb +++ b/app/services/preview_markdown_service.rb @@ -17,7 +17,7 @@ class PreviewMarkdownService < BaseService private def explain_quick_actions(text) - return text, [] unless %w(Issue MergeRequest Commit).include?(commands_target_type) + return text, [] unless %w(Issue MergeRequest Commit).include?(target_type) quick_actions_service = QuickActions::InterpretService.new(project, current_user) quick_actions_service.explain(text, find_commands_target) @@ -30,22 +30,34 @@ class PreviewMarkdownService < BaseService end def find_suggestions(text) - return [] unless params[:preview_suggestions] + return [] unless preview_sugestions? - Banzai::SuggestionsParser.parse(text) + position = Gitlab::Diff::Position.new(new_path: params[:file_path], + new_line: params[:line].to_i, + base_sha: params[:base_sha], + head_sha: params[:head_sha], + start_sha: params[:start_sha]) + + Gitlab::Diff::SuggestionsParser.parse(text, position: position, project: project) + end + + def preview_sugestions? + params[:preview_suggestions] && + target_type == 'MergeRequest' && + Ability.allowed?(current_user, :download_code, project) end def find_commands_target QuickActions::TargetService .new(project, current_user) - .execute(commands_target_type, commands_target_id) + .execute(target_type, target_id) end - def commands_target_type - params[:quick_actions_target_type] + def target_type + params[:target_type] end - def commands_target_id - params[:quick_actions_target_id] + def target_id + params[:target_id] end end diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb index 9a243e07936..00b51f92b12 100644 --- a/app/uploaders/records_uploads.rb +++ b/app/uploaders/records_uploads.rb @@ -46,6 +46,10 @@ module RecordsUploads File.join(store_dir, filename.to_s) end + def filename + upload&.path ? File.basename(upload.path) : super + end + private # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 4eb53faa6ff..acd63de2277 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -8,30 +8,20 @@ %span.sr-only= _('Select Archive Format') = sprite_icon("arrow-down") %ul.dropdown-menu.dropdown-menu-right{ role: 'menu' } - %li.dropdown-header - #{ _('Source code') } - %li - = link_to project_archive_path(project, id: tree_join(ref, archive_prefix), format: 'zip'), rel: 'nofollow', download: '' do - %span= _('Download zip') - %li - = link_to project_archive_path(project, id: tree_join(ref, archive_prefix), format: 'tar.gz'), rel: 'nofollow', download: '' do - %span= _('Download tar.gz') - %li - = link_to project_archive_path(project, id: tree_join(ref, archive_prefix), format: 'tar.bz2'), rel: 'nofollow', download: '' do - %span= _('Download tar.bz2') - %li - = link_to project_archive_path(project, id: tree_join(ref, archive_prefix), format: 'tar'), rel: 'nofollow', download: '' do - %span= _('Download tar') - + %li.dropdown-bold-header= _('Download source code') + %li.dropdown-menu-content + = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: nil + - if directory? + %li.separator + %li.dropdown-bold-header= _('Download this directory') + %li.dropdown-menu-content + = render 'projects/buttons/download_links', project: project, ref: ref, archive_prefix: archive_prefix, path: @path - if pipeline && pipeline.latest_builds_with_artifacts.any? - %li.dropdown-header Artifacts + %li.separator + %li.dropdown-bold-header= _('Download artifacts') - unless pipeline.latest? - - latest_pipeline = project.pipeline_for(ref) - %li - .unclickable= ci_status_for_statuseable(latest_pipeline) - %li.dropdown-header Previous Artifacts + %span.unclickable= ci_status_for_statuseable(project.pipeline_for(ref)) + %li.dropdown-header= _('Previous Artifacts') - pipeline.latest_builds_with_artifacts.each do |job| %li - = link_to latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' do - %span - #{s_('DownloadArtifacts|Download')} '#{job.name}' + = link_to job.name, latest_succeeded_project_artifacts_path(project, "#{ref}/download", job: job.name), rel: 'nofollow', download: '' diff --git a/app/views/projects/buttons/_download_links.html.haml b/app/views/projects/buttons/_download_links.html.haml new file mode 100644 index 00000000000..47a1704f946 --- /dev/null +++ b/app/views/projects/buttons/_download_links.html.haml @@ -0,0 +1,9 @@ +%ul + %li.d-inline-block.m-0.p-0 + = link_to 'zip', project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: 'zip'), rel: 'nofollow', download: '', class: 'btn btn-primary btn-xs' + %li.d-inline-block.m-0.p-0 + = link_to 'tar.gz', project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: 'tar.gz'), rel: 'nofollow', download: '', class: 'btn btn-xs' + %li.d-inline-block.m-0.p-0 + = link_to 'tar.bz2', project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: 'tar.bz2'), rel: 'nofollow', download: '', class: 'btn btn-xs' + %li.d-inline-block.m-0.p-0 + = link_to 'tar', project_archive_path(project, id: tree_join(ref, archive_prefix), path: path, format: 'tar'), rel: 'nofollow', download: '', class: 'btn btn-xs' diff --git a/app/views/projects/diffs/_replaced_image_diff.html.haml b/app/views/projects/diffs/_replaced_image_diff.html.haml index 6dffc7c4390..70521ed892e 100644 --- a/app/views/projects/diffs/_replaced_image_diff.html.haml +++ b/app/views/projects/diffs/_replaced_image_diff.html.haml @@ -37,7 +37,7 @@ .swipe-frame .frame.deleted = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false) - .swipe-wrap + .swipe-wrap.left-oriented = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path } %span.swipe-bar %span.top-handle diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml index 25df2fe5cd6..b11cb8a3076 100644 --- a/app/views/shared/form_elements/_description.html.haml +++ b/app/views/shared/form_elements/_description.html.haml @@ -5,7 +5,7 @@ - supports_quick_actions = model.new_record? - if supports_quick_actions - - preview_url = preview_markdown_path(project, quick_actions_target_type: model.class.name) + - preview_url = preview_markdown_path(project, target_type: model.class.name) - else - preview_url = preview_markdown_path(project) diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml index 6a1eea85fde..d91bc6e57c9 100644 --- a/app/views/shared/notes/_form.html.haml +++ b/app/views/shared/notes/_form.html.haml @@ -1,7 +1,7 @@ - supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true) - supports_quick_actions = note_supports_quick_actions?(@note) - if supports_quick_actions - - preview_url = preview_markdown_path(@project, quick_actions_target_type: @note.noteable_type, quick_actions_target_id: @note.noteable_id) + - preview_url = preview_markdown_path(@project, target_type: @note.noteable_type, target_id: @note.noteable_id) - else - preview_url = preview_markdown_path(@project) |