summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/commit/image_file.js49
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue1
-rw-r--r--app/assets/javascripts/monitoring/components/panel_type.vue4
-rw-r--r--app/assets/javascripts/notes/components/discussion_locked_widget.vue13
-rw-r--r--app/assets/javascripts/notes/mixins/issuable_state.js6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue3
-rw-r--r--app/controllers/autocomplete_controller.rb2
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/serializers/issue_entity.rb8
-rw-r--r--app/serializers/merge_request_noteable_entity.rb12
-rw-r--r--app/views/admin/groups/show.html.haml3
-rw-r--r--app/views/projects/pipelines/_info.html.haml5
-rw-r--r--changelogs/unreleased/24190-archived-project-warning-message.yml5
-rw-r--r--changelogs/unreleased/commit-box-child-label.yml5
-rw-r--r--changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml5
-rw-r--r--changelogs/unreleased/winh-var-image_file.yml5
-rw-r--r--changelogs/unreleased/xanf-add-group-link.yml5
-rw-r--r--doc/user/gitlab_com/index.md2
-rw-r--r--doc/user/project/repository/git_blame.md50
-rw-r--r--doc/user/project/repository/git_history.md67
-rw-r--r--doc/user/project/repository/img/file_blame_button_v12_6.pngbin0 -> 22175 bytes
-rw-r--r--doc/user/project/repository/img/file_blame_output_v12_6.pngbin0 -> 96929 bytes
-rw-r--r--doc/user/project/repository/img/file_history_button_v12_6.pngbin0 -> 22175 bytes
-rw-r--r--doc/user/project/repository/img/file_history_output_v12_6.pngbin0 -> 84738 bytes
-rw-r--r--doc/user/project/repository/index.md2
-rw-r--r--locale/gitlab.pot12
-rw-r--r--package.json2
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb40
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb21
-rw-r--r--spec/features/admin/admin_groups_spec.rb8
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_noteable.json10
-rw-r--r--spec/frontend/monitoring/panel_type_spec.js76
-rw-r--r--spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js6
-rw-r--r--spec/frontend/sidebar/user_data_mock.js6
-rw-r--r--spec/serializers/issue_entity_spec.rb32
-rw-r--r--spec/serializers/merge_request_serializer_spec.rb19
-rw-r--r--yarn.lock74
38 files changed, 421 insertions, 143 deletions
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 6be3a62ff00..0d10c896a47 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -166,6 +166,7 @@ export default () => {
humanTotalTimeSpent,
weight,
epic,
+ assignees,
} = convertObjectPropsToCamelCase(data);
newIssue.setFetchingState('subscriptions', false);
@@ -179,6 +180,7 @@ export default () => {
subscribed,
weight,
epic,
+ assignees,
});
})
.catch(() => {
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 60c2059a876..a28e17f7a56 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */
+/* eslint-disable func-names, no-else-return, consistent-return, one-var, no-return-assign */
import $ from 'jquery';
@@ -51,7 +51,7 @@ export default class ImageFile {
}
// eslint-disable-next-line class-methods-use-this
initDraggable($el, padding, callback) {
- var dragging = false;
+ let dragging = false;
const $body = $('body');
const $offsetEl = $el.parent();
const dragStart = function() {
@@ -88,14 +88,12 @@ export default class ImageFile {
}
static prepareFrames(view) {
- var maxHeight, maxWidth;
- maxWidth = 0;
- maxHeight = 0;
+ let maxWidth = 0;
+ let maxHeight = 0;
$('.frame', view)
.each((index, frame) => {
- var height, width;
- width = $(frame).width();
- height = $(frame).height();
+ const width = $(frame).width();
+ const height = $(frame).height();
maxWidth = width > maxWidth ? width : maxWidth;
return (maxHeight = height > maxHeight ? height : maxHeight);
})
@@ -110,8 +108,7 @@ export default class ImageFile {
'two-up': function() {
return $('.two-up.view .wrap', this.file).each((index, wrap) => {
$('img', wrap).each(function() {
- var currentWidth;
- currentWidth = $(this).width();
+ const currentWidth = $(this).width();
if (currentWidth > availWidth / 2) {
return $(this).width(availWidth / 2);
}
@@ -124,16 +121,14 @@ export default class ImageFile {
});
},
swipe() {
- var maxHeight, maxWidth;
- maxWidth = 0;
- maxHeight = 0;
+ let maxWidth = 0;
+ let maxHeight = 0;
return $('.swipe.view', this.file).each((index, view) => {
- var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding;
const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref;
- $swipeFrame = $('.swipe-frame', view);
- $swipeWrap = $('.swipe-wrap', view);
- $swipeBar = $('.swipe-bar', view);
+ const $swipeFrame = $('.swipe-frame', view);
+ const $swipeWrap = $('.swipe-wrap', view);
+ const $swipeBar = $('.swipe-bar', view);
$swipeFrame.css({
width: maxWidth + 16,
@@ -148,7 +143,7 @@ export default class ImageFile {
left: 1,
});
- wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
+ const wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
this.initDraggable($swipeBar, wrapPadding, (e, left) => {
if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) {
@@ -159,19 +154,17 @@ export default class ImageFile {
});
},
'onion-skin': function() {
- var dragTrackWidth, maxHeight, maxWidth;
+ let maxHeight, maxWidth;
maxWidth = 0;
maxHeight = 0;
- dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
+ const dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((index, view) => {
- var $frame, $track, $dragger, $frameAdded, framePadding;
-
const ref = ImageFile.prepareFrames(view);
[maxWidth, maxHeight] = ref;
- $frame = $('.onion-skin-frame', view);
- $frameAdded = $('.frame.added', view);
- $track = $('.drag-track', view);
- $dragger = $('.dragger', $track);
+ const $frame = $('.onion-skin-frame', view);
+ const $frameAdded = $('.frame.added', view);
+ const $track = $('.drag-track', view);
+ const $dragger = $('.dragger', $track);
$frame.css({
width: maxWidth + 16,
@@ -186,10 +179,10 @@ export default class ImageFile {
});
$frameAdded.css('opacity', 1);
- framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
+ const framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
this.initDraggable($dragger, framePadding, (e, left) => {
- var opacity = left / dragTrackWidth;
+ const opacity = left / dragTrackWidth;
if (opacity >= 0 && opacity <= 1) {
$dragger.css('left', left);
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
index 86f5559af8f..eb8945c1a57 100644
--- a/app/assets/javascripts/monitoring/components/embed.vue
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -97,7 +97,6 @@ export default {
v-for="(graphData, graphIndex) in charts"
:key="`panel-type-${graphIndex}`"
class="w-100"
- clipboard-text=""
:graph-data="graphData"
:group-id="dashboardUrl"
/>
diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue
index d0dce4f5116..ec6a41d0540 100644
--- a/app/assets/javascripts/monitoring/components/panel_type.vue
+++ b/app/assets/javascripts/monitoring/components/panel_type.vue
@@ -36,7 +36,8 @@ export default {
props: {
clipboardText: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
graphData: {
type: Object,
@@ -152,6 +153,7 @@ export default {
{{ __('Download CSV') }}
</gl-dropdown-item>
<gl-dropdown-item
+ v-if="clipboardText"
v-track-event="generateLinkToChartOptions(clipboardText)"
class="js-chart-link"
:data-clipboard-text="clipboardText"
diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
index 53f509185a8..8636984c6af 100644
--- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue
+++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
@@ -12,6 +12,9 @@ export default {
},
mixins: [Issuable, issuableStateMixin],
computed: {
+ projectArchivedWarning() {
+ return __('This project is archived and cannot be commented on.');
+ },
lockedIssueWarning() {
return sprintf(
__('This %{issuableDisplayName} is locked. Only project members can comment.'),
@@ -26,9 +29,15 @@ export default {
<div class="disabled-comment text-center">
<span class="issuable-note-warning inline">
<icon :size="16" name="lock" class="icon" />
- <span>
- {{ lockedIssueWarning }}
+ <span v-if="isProjectArchived">
+ {{ projectArchivedWarning }}
+ <gl-link :href="archivedProjectDocsPath" target="_blank" class="learn-more">
+ {{ __('Learn more') }}
+ </gl-link>
+ </span>
+ <span v-else>
+ {{ lockedIssueWarning }}
<gl-link :href="lockedIssueDocsPath" target="_blank" class="learn-more">
{{ __('Learn more') }}
</gl-link>
diff --git a/app/assets/javascripts/notes/mixins/issuable_state.js b/app/assets/javascripts/notes/mixins/issuable_state.js
index d97d9f6850a..0ca8c8c98a3 100644
--- a/app/assets/javascripts/notes/mixins/issuable_state.js
+++ b/app/assets/javascripts/notes/mixins/issuable_state.js
@@ -3,6 +3,12 @@ import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['getNoteableDataByProp']),
+ isProjectArchived() {
+ return this.getNoteableDataByProp('is_project_archived');
+ },
+ archivedProjectDocsPath() {
+ return this.getNoteableDataByProp('archived_project_docs_path');
+ },
lockedIssueDocsPath() {
return this.getNoteableDataByProp('locked_discussion_docs_path');
},
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
index aaac812f213..9a60172db2e 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
@@ -1,7 +1,6 @@
<script>
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
-import { joinPaths } from '~/lib/utils/url_utility';
import AssigneeAvatar from './assignee_avatar.vue';
export default {
@@ -60,7 +59,7 @@ export default {
};
},
assigneeUrl() {
- return joinPaths(`${this.rootPath}`, `${this.user.username}`);
+ return this.user.web_url;
},
},
};
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index ba8d2d18695..0df201ab506 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -53,7 +53,7 @@ class AutocompleteController < ApplicationController
private
def target_branch_params
- params.permit(:group_id, :project_id)
+ params.permit(:group_id, :project_id).select { |_, v| v.present? }
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 0b1d17a9e12..ce263566ea6 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -502,6 +502,10 @@ module Ci
builds.skipped.after_stage(stage_idx).find_each(&:process)
end
+ def child?
+ false
+ end
+
def latest?
return false unless git_ref && commit.present?
diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb
index a3d0298a495..98c0c703584 100644
--- a/app/serializers/issue_entity.rb
+++ b/app/serializers/issue_entity.rb
@@ -64,4 +64,12 @@ class IssueEntity < IssuableEntity
expose :locked_discussion_docs_path, if: -> (issue) { issue.discussion_locked? } do |issue|
help_page_path('user/discussions/index.md', anchor: 'lock-discussions')
end
+
+ expose :is_project_archived do |issue|
+ issue.project.archived?
+ end
+
+ expose :archived_project_docs_path, if: -> (issue) { issue.project.archived? } do |issue|
+ help_page_path('user/project/settings/index.md', anchor: 'archiving-a-project')
+ end
end
diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb
index 9504fdd8eac..8e7456ce059 100644
--- a/app/serializers/merge_request_noteable_entity.rb
+++ b/app/serializers/merge_request_noteable_entity.rb
@@ -42,6 +42,18 @@ class MergeRequestNoteableEntity < IssuableEntity
end
end
+ expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request|
+ help_page_path('user/discussions/index.md', anchor: 'lock-discussions')
+ end
+
+ expose :is_project_archived do |merge_request|
+ merge_request.project.archived?
+ end
+
+ expose :archived_project_docs_path, if: -> (merge_request) { merge_request.project.archived? } do |merge_request|
+ help_page_path('user/project/settings/index.md', anchor: 'archiving-a-project')
+ end
+
private
delegate :current_user, to: :request
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index f9cc118a252..160c3b4d06d 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -19,7 +19,8 @@
= group_icon(@group, class: "avatar s60")
%li
%span.light= _('Name:')
- %strong= @group.name
+ %strong
+ = link_to @group.name, group_path(@group)
%li
%span.light= _('Path:')
%strong
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 4eec81c9125..ce6ae765de9 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -20,6 +20,11 @@
.well-segment.qa-pipeline-badges
.icon-container
= sprite_icon('flag')
+ - if @pipeline.child?
+ %span.js-pipeline-child.badge.badge-primary.has-tooltip{ title: s_("Pipelines|This is a child pipeline within the parent pipeline") }
+ = s_('Pipelines|Child pipeline')
+ = surround '(', ')' do
+ = link_to s_('Pipelines|parent'), pipeline_path(@pipeline.triggered_by_pipeline), class: 'text-white text-underline'
- if @pipeline.latest?
%span.js-pipeline-url-latest.badge.badge-success.has-tooltip{ title: _("Latest pipeline for the most recent commit on this branch") }
latest
diff --git a/changelogs/unreleased/24190-archived-project-warning-message.yml b/changelogs/unreleased/24190-archived-project-warning-message.yml
new file mode 100644
index 00000000000..bfe1976451e
--- /dev/null
+++ b/changelogs/unreleased/24190-archived-project-warning-message.yml
@@ -0,0 +1,5 @@
+---
+title: Show correct warning on issue when project is archived
+merge_request: 20078
+author:
+type: fixed
diff --git a/changelogs/unreleased/commit-box-child-label.yml b/changelogs/unreleased/commit-box-child-label.yml
new file mode 100644
index 00000000000..99cfcae2016
--- /dev/null
+++ b/changelogs/unreleased/commit-box-child-label.yml
@@ -0,0 +1,5 @@
+---
+title: Add child label to commit box
+merge_request: 21323
+author:
+type: added
diff --git a/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml b/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml
new file mode 100644
index 00000000000..b91e5cfdde7
--- /dev/null
+++ b/changelogs/unreleased/fix_assignee_url_issue_board_sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Fix assignee url in issue board sidebar
+merge_request: 20992
+author: Lee Tickett
+type: fixed
diff --git a/changelogs/unreleased/winh-var-image_file.yml b/changelogs/unreleased/winh-var-image_file.yml
new file mode 100644
index 00000000000..7ca37d304fe
--- /dev/null
+++ b/changelogs/unreleased/winh-var-image_file.yml
@@ -0,0 +1,5 @@
+---
+title: Remove var from app/assets/javascripts/commit/image_file.js
+merge_request: 21649
+author: Abubakar Hassan
+type: other
diff --git a/changelogs/unreleased/xanf-add-group-link.yml b/changelogs/unreleased/xanf-add-group-link.yml
new file mode 100644
index 00000000000..5cb57c5d544
--- /dev/null
+++ b/changelogs/unreleased/xanf-add-group-link.yml
@@ -0,0 +1,5 @@
+---
+title: link to group on group admin page
+merge_request: 21709
+author:
+type: added
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 466e4e43bfc..525a8f7a94c 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -91,7 +91,7 @@ GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform
IP based firewall can be configured by looking up all
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
-[Static endpoints](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/5071) are being considered.
+[Static endpoints](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/97) are being considered.
## Shared Runners
diff --git a/doc/user/project/repository/git_blame.md b/doc/user/project/repository/git_blame.md
new file mode 100644
index 00000000000..454b3f86df9
--- /dev/null
+++ b/doc/user/project/repository/git_blame.md
@@ -0,0 +1,50 @@
+---
+type: reference, howto
+description: "Documentation on Git file blame."
+---
+
+# Git file blame
+
+> [Introduced](https://git.sphere.ly/staff/publicgitlab/commit/39c657930625ddc3ac8a921f01ffc83acadce68f) in GitLab 2.5
+
+[Git blame](https://git-scm.com/docs/git-blame) provides more information
+about every line in a file, including the last modified time, author, and
+commit hash.
+
+You can find the **Blame** button with each file in a project.
+
+![File blame button](img/file_blame_button_v12_6.png "Blame button")
+
+When you select the **Blame** button, you'll see a screen with the
+noted information:
+
+![Git blame output](img/file_blame_output_v12_6.png "Blame button output")
+
+If you hover over a commit in the UI, you'll see a precise date and time
+for that commit.
+
+## Associated `git` command
+
+If you're running `git` from the command line, the equivalent command is
+`git blame <filename>`. For example, if you want to find `blame` information
+about a `README.md` file in the local directory, run the following command:
+
+```bash
+git blame README.md
+```
+
+You'll see output similar to the following, which includes the commit time
+in UTC format:
+
+```bash
+62e2353a (Achilleas Pipinellis 2019-07-11 14:52:18 +0300 1) [![build status](https://gitlab.com/gitlab-org/gitlab-docs/badges/master/build.svg)](https://gitlab.com/gitlab-com/gitlab-docs/commits/master)
+fb0fc7d6 (Achilleas Pipinellis 2016-11-07 22:21:22 +0100 2)
+^764ca75 (Connor Shea 2016-10-05 23:40:24 -0600 3) # GitLab Documentation
+^764ca75 (Connor Shea 2016-10-05 23:40:24 -0600 4)
+0e62ed6d (Mike Jang 2019-11-26 21:44:53 +0000 5) This project hosts the repository used to generate the GitLab
+0e62ed6d (Mike Jang 2019-11-26 21:44:53 +0000 6) documentation website and deployed to https://docs.gitlab.com. It uses the
+```
+
+## File blame through the API
+
+You can also get this information over the [Git file blame REST API](../../../api/repository_files.md#get-file-blame-from-repository).
diff --git a/doc/user/project/repository/git_history.md b/doc/user/project/repository/git_history.md
new file mode 100644
index 00000000000..9cd3d0d4ed0
--- /dev/null
+++ b/doc/user/project/repository/git_history.md
@@ -0,0 +1,67 @@
+---
+type: reference, howto
+description: "Documentation on Git file history."
+---
+
+# Git file history
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/blob/9ba1224867665844b117fa037e1465bb706b3685/app/controllers/commits_controller.rb) in GitLab 0.8.0
+
+Git file History provides information about the commit history associated
+with a file.
+
+You can find the **History** button with each file in a project.
+
+![File history button](img/file_history_button_v12_6.png "History button")
+
+When you select the **History** button, you'll see a screen with the
+noted information:
+
+![Git log output](img/file_history_output_v12_6.png "History button output")
+
+If you hover over a commit in the UI, you'll see a precise date and time
+that commit was last modified.
+
+## Associated `git` command
+
+If you're running `git` from the command line, the equivalent command
+is `git log <filename>`. For example, if you want to find `history`
+information about a `README.md` file in the local directory, run the
+following command:
+
+```bash
+git log README.md
+```
+
+You'll see output similar to the following, which includes the commit
+time in UTC format:
+
+```bash
+commit 0e62ed6d9f39fa9bedf7efc6edd628b137fa781a
+Author: Mike Jang <mjang@gitlab.com>
+Date: Tue Nov 26 21:44:53 2019 +0000
+
+ Deemphasize GDK as a doc build tool
+
+commit 418879420b1e3a4662067bd07b64bb6988654697
+Author: Marcin Sedlak-Jakubowski <msedlakjakubowski@gitlab.com>
+Date: Mon Nov 4 19:58:27 2019 +0100
+
+ Fix typo
+
+commit 21cc1fef11349417ed515557748369cfb235fc81
+Author: Jacques Erasmus <jerasmus@gitlab.com>
+Date: Mon Oct 14 22:13:40 2019 +0000
+
+ Add support for modern JS
+
+ Added rollup to the project
+
+commit 2f5e895aebfa5678e51db303b97de56c51e3cebe
+Author: Achilleas Pipinellis <axil@gitlab.com>
+Date: Fri Sep 13 14:03:01 2019 +0000
+
+ Remove gitlab-foss Git URLs as we don't need them anymore
+
+ [ci skip]
+```
diff --git a/doc/user/project/repository/img/file_blame_button_v12_6.png b/doc/user/project/repository/img/file_blame_button_v12_6.png
new file mode 100644
index 00000000000..b5a18e6726f
--- /dev/null
+++ b/doc/user/project/repository/img/file_blame_button_v12_6.png
Binary files differ
diff --git a/doc/user/project/repository/img/file_blame_output_v12_6.png b/doc/user/project/repository/img/file_blame_output_v12_6.png
new file mode 100644
index 00000000000..4aca40353d5
--- /dev/null
+++ b/doc/user/project/repository/img/file_blame_output_v12_6.png
Binary files differ
diff --git a/doc/user/project/repository/img/file_history_button_v12_6.png b/doc/user/project/repository/img/file_history_button_v12_6.png
new file mode 100644
index 00000000000..b5a18e6726f
--- /dev/null
+++ b/doc/user/project/repository/img/file_history_button_v12_6.png
Binary files differ
diff --git a/doc/user/project/repository/img/file_history_output_v12_6.png b/doc/user/project/repository/img/file_history_output_v12_6.png
new file mode 100644
index 00000000000..9e9855203af
--- /dev/null
+++ b/doc/user/project/repository/img/file_history_output_v12_6.png
Binary files differ
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index cb7fe63db6f..fc422bb5aba 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -48,6 +48,8 @@ it's easier to do so [via GitLab UI](web_editor.md):
- [File templates](web_editor.md#template-dropdowns)
- [Create a directory](web_editor.md#create-a-directory)
- [Start a merge request](web_editor.md#tips)
+- [Find file history](git_history.md)
+- [Identify changes by line (Git blame)](git_blame.md)
**From the command line:**
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 162dda4d044..ecf5ca2d49a 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -12719,6 +12719,9 @@ msgstr ""
msgid "Pipelines|CI Lint"
msgstr ""
+msgid "Pipelines|Child pipeline"
+msgstr ""
+
msgid "Pipelines|Clear Runner Caches"
msgstr ""
@@ -12752,9 +12755,15 @@ msgstr ""
msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
msgstr ""
+msgid "Pipelines|This is a child pipeline within the parent pipeline"
+msgstr ""
+
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr ""
+msgid "Pipelines|parent"
+msgstr ""
+
msgid "Pipeline|Commit"
msgstr ""
@@ -18265,6 +18274,9 @@ msgstr ""
msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
msgstr ""
+msgid "This project is archived and cannot be commented on."
+msgstr ""
+
msgid "This project path either does not exist or is private."
msgstr ""
diff --git a/package.json b/package.json
index 059b6b4445f..b0b1207311a 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
"@gitlab/svgs": "^1.85.0",
"@gitlab/ui": "8.2.0",
"@gitlab/visual-review-tools": "1.2.0",
- "@sentry/browser": "^5.7.1",
+ "@sentry/browser": "^5.10.2",
"@sourcegraph/code-host-integration": "^0.0.14",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb
index dc7fa9f3859..bab6b1ac5fc 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/close_issue_spec.rb
@@ -3,19 +3,18 @@
module QA
context 'Plan' do
describe 'Close issue' do
- let(:issue_title) { 'issue title' }
- let(:commit_message) { 'Closes' }
+ let(:issue) do
+ Resource::Issue.fabricate_via_api! do |issue|
+ issue.title = 'Issue to be closed via pushing a commit'
+ end
+ end
+
+ let(:project) { issue.project }
+ let(:issue_id) { issue.api_response[:iid] }
before do
Flow::Login.sign_in
- issue = Resource::Issue.fabricate_via_api! do |issue|
- issue.title = issue_title
- end
-
- @project = issue.project
- @issue_id = issue.api_response[:iid]
-
# Initial commit should be pushed because
# the very first commit to the project doesn't close the issue
# https://gitlab.com/gitlab-org/gitlab-foss/issues/38965
@@ -23,24 +22,15 @@ module QA
end
it 'user closes an issue by pushing commit' do
- push_commit("#{commit_message} ##{@issue_id}", false)
-
- @project.visit!
- Page::Project::Show.perform do |show|
- show.click_commit(commit_message)
- end
- commit_sha = Page::Project::Commit::Show.perform(&:commit_sha)
+ push_commit("Closes ##{issue_id}", false)
- Page::Project::Menu.perform(&:click_issues)
- Page::Project::Issue::Index.perform do |index|
- index.click_closed_issues_link
- index.click_issue_link(issue_title)
- end
+ issue.visit!
Page::Project::Issue::Show.perform do |show|
- show.select_all_activities_filter
- expect(show).to have_element(:reopen_issue_button)
- expect(show).to have_content("closed via commit #{commit_sha}")
+ reopen_issue_button_visible = show.wait(reload: true) do
+ show.has_element?(:reopen_issue_button, wait: 1.0)
+ end
+ expect(reopen_issue_button_visible).to be_truthy
end
end
@@ -49,7 +39,7 @@ module QA
push.commit_message = commit_message
push.new_branch = new_branch
push.file_content = commit_message
- push.project = @project
+ push.project = project
end
end
end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 56c27b4e5eb..4227a4453a3 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -391,13 +391,24 @@ describe AutocompleteController do
end
context 'user with an accessible merge request but no scope' do
- it 'returns an error' do
- sign_in(user)
+ where(
+ params: [
+ {},
+ { group_id: ' ' },
+ { project_id: ' ' },
+ { group_id: ' ', project_id: ' ' }
+ ]
+ )
+
+ with_them do
+ it 'returns an error' do
+ sign_in(user)
- get :merge_request_target_branches
+ get :merge_request_target_branches, params: params
- expect(response).to have_gitlab_http_status(400)
- expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' })
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response).to eq({ 'error' => 'At least one of group_id or project_id must be specified' })
+ end
end
end
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index 34356a2ee90..257e5cb8bf0 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -94,6 +94,14 @@ describe 'Admin Groups' do
expect(page).to have_content("Group: #{group.name}")
expect(page).to have_content("ID: #{group.id}")
end
+
+ it 'has a link to the group' do
+ group = create(:group, :private)
+
+ visit admin_group_path(group)
+
+ expect(page).to have_link(group.name, href: group_path(group))
+ end
end
describe 'group edit' do
diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
index d37f5b864d7..c0eb320e67f 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
@@ -1,6 +1,9 @@
{
"type": "object",
- "properties" : {
+ "required": ["id", "iid", "title", "description", "merge_params", "state", "source_branch", "target_branch",
+ "diff_head_sha", "create_note_path", "preview_note_path", "can_receive_suggestion", "create_issue_to_resolve_discussions_path",
+ "new_blob_path", "current_user", "is_project_archived"],
+ "properties": {
"id": { "type": "integer" },
"iid": { "type": "integer" },
"title": { "type": "string" },
@@ -26,7 +29,10 @@
"can_update": { "type": "boolean" }
},
"additionalProperties": false
- }
+ },
+ "is_project_archived": { "type": "boolean" },
+ "locked_discussion_docs_path": { "type": "string" },
+ "archived_project_docs_path": { "type": "string" }
},
"additionalProperties": false
}
diff --git a/spec/frontend/monitoring/panel_type_spec.js b/spec/frontend/monitoring/panel_type_spec.js
index 7adecc56d18..c869d77673e 100644
--- a/spec/frontend/monitoring/panel_type_spec.js
+++ b/spec/frontend/monitoring/panel_type_spec.js
@@ -20,6 +20,16 @@ describe('Panel Type component', () => {
const dashboardWidth = 100;
const exampleText = 'example_text';
+ const createWrapper = props =>
+ shallowMount(PanelType, {
+ propsData: {
+ ...props,
+ },
+ store,
+ sync: false,
+ attachToDocument: true,
+ });
+
beforeEach(() => {
setTestTimeout(1000);
axiosMock = new AxiosMockAdapter(axios);
@@ -36,14 +46,9 @@ describe('Panel Type component', () => {
graphDataNoResult.metrics[0].result = [];
beforeEach(() => {
- panelType = shallowMount(PanelType, {
- propsData: {
- clipboardText: 'dashboard_link',
- dashboardWidth,
- graphData: graphDataNoResult,
- },
- sync: false,
- attachToDocument: true,
+ panelType = createWrapper({
+ dashboardWidth,
+ graphData: graphDataNoResult,
});
});
@@ -68,41 +73,30 @@ describe('Panel Type component', () => {
});
});
- describe('when Graph data is available', () => {
- const propsData = {
- clipboardText: exampleText,
- dashboardWidth,
- graphData: graphDataPrometheusQueryRange,
- };
-
- beforeEach(done => {
+ describe('when graph data is available', () => {
+ beforeEach(() => {
store = createStore();
- panelType = shallowMount(PanelType, {
- propsData,
- store,
- sync: false,
- attachToDocument: true,
+ panelType = createWrapper({
+ dashboardWidth,
+ graphData: graphDataPrometheusQueryRange,
});
- panelType.vm.$nextTick(done);
});
afterEach(() => {
panelType.destroy();
});
+ it('sets no clipboard copy link on dropdown by default', () => {
+ const link = () => panelType.find('.js-chart-link');
+ expect(link().exists()).toBe(false);
+ });
+
describe('Time Series Chart panel type', () => {
it('is rendered', () => {
expect(panelType.find(TimeSeriesChart).isVueInstance()).toBe(true);
expect(panelType.find(TimeSeriesChart).exists()).toBe(true);
});
- it('sets clipboard text on the dropdown', () => {
- const link = () => panelType.find('.js-chart-link');
- const clipboardText = () => link().element.dataset.clipboardText;
-
- expect(clipboardText()).toBe(exampleText);
- });
-
it('includes a default group id', () => {
expect(panelType.vm.groupId).toBe('panel-type-chart');
});
@@ -123,6 +117,30 @@ describe('Panel Type component', () => {
});
});
+ describe('when cliboard data is available', () => {
+ const clipboardText = 'A value to copy.';
+
+ beforeEach(() => {
+ store = createStore();
+ panelType = createWrapper({
+ clipboardText,
+ dashboardWidth,
+ graphData: graphDataPrometheusQueryRange,
+ });
+ });
+
+ afterEach(() => {
+ panelType.destroy();
+ });
+
+ it('sets clipboard text on the dropdown', () => {
+ const link = () => panelType.find('.js-chart-link');
+
+ expect(link().exists()).toBe(true);
+ expect(link().element.dataset.clipboardText).toBe(clipboardText);
+ });
+ });
+
describe('when downloading metrics data as CSV', () => {
beforeEach(done => {
graphDataPrometheusQueryRange.y_label = 'metric';
diff --git a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
index d800649bc1d..9b2e2e38366 100644
--- a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
+++ b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
@@ -1,12 +1,11 @@
import { shallowMount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
-import { joinPaths } from '~/lib/utils/url_utility';
import AssigneeAvatarLink from '~/sidebar/components/assignees/assignee_avatar_link.vue';
import AssigneeAvatar from '~/sidebar/components/assignees/assignee_avatar.vue';
import userDataMock from '../../user_data_mock';
const TOOLTIP_PLACEMENT = 'bottom';
-const { name: USER_NAME, username: USER_USERNAME } = userDataMock();
+const { name: USER_NAME } = userDataMock();
const TEST_ISSUABLE_TYPE = 'merge_request';
describe('AssigneeAvatarLink component', () => {
@@ -38,9 +37,8 @@ describe('AssigneeAvatarLink component', () => {
it('has the root url present in the assigneeUrl method', () => {
createComponent();
- const assigneeUrl = joinPaths(TEST_HOST, USER_USERNAME);
- expect(wrapper.attributes().href).toEqual(assigneeUrl);
+ expect(wrapper.attributes().href).toEqual(userDataMock().web_url);
});
it('renders assignee avatar', () => {
diff --git a/spec/frontend/sidebar/user_data_mock.js b/spec/frontend/sidebar/user_data_mock.js
index 8ad70bb3499..df90a65f6f9 100644
--- a/spec/frontend/sidebar/user_data_mock.js
+++ b/spec/frontend/sidebar/user_data_mock.js
@@ -1,9 +1,11 @@
+import { TEST_HOST } from 'helpers/test_constants';
+
export default () => ({
- avatar_url: 'mock_path',
+ avatar_url: `${TEST_HOST}/avatar/root.png`,
id: 1,
name: 'Root',
state: 'active',
username: 'root',
- web_url: '',
+ web_url: `${TEST_HOST}/root`,
can_merge: true,
});
diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb
index 224ed0b402f..a1868b2631b 100644
--- a/spec/serializers/issue_entity_spec.rb
+++ b/spec/serializers/issue_entity_spec.rb
@@ -92,4 +92,36 @@ describe IssueEntity do
end
end
end
+
+ context 'when issuable in active or archived project' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'when project is active' do
+ it 'returns archived false' do
+ expect(subject[:is_project_archived]).to eq(false)
+ end
+
+ it 'returns nil for archived project doc' do
+ response = described_class.new(resource, request: request).as_json
+
+ expect(response[:archived_project_docs_path]).to be nil
+ end
+ end
+
+ context 'when project is archived' do
+ before do
+ project.update(archived: true)
+ end
+
+ it 'returns archived true' do
+ expect(subject[:is_project_archived]).to eq(true)
+ end
+
+ it 'returns archived project doc' do
+ expect(subject[:archived_project_docs_path]).to eq('/help/user/project/settings/index.md#archiving-a-project')
+ end
+ end
+ end
end
diff --git a/spec/serializers/merge_request_serializer_spec.rb b/spec/serializers/merge_request_serializer_spec.rb
index a99f11168c0..9297df31842 100644
--- a/spec/serializers/merge_request_serializer_spec.rb
+++ b/spec/serializers/merge_request_serializer_spec.rb
@@ -48,7 +48,24 @@ describe MergeRequestSerializer do
let(:serializer) { 'noteable' }
it 'matches noteable merge request json schema' do
- expect(json_entity).to match_schema('entities/merge_request_noteable', strict: true)
+ expect(json_entity).to match_schema('entities/merge_request_noteable')
+ end
+
+ context 'when merge_request is locked' do
+ let(:resource) { create(:merge_request, :locked, description: "Description") }
+
+ it 'matches noteable merge request json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_noteable')
+ end
+ end
+
+ context 'when project is archived' do
+ let(:project) { create(:project, :archived, :repository) }
+ let(:resource) { create(:merge_request, source_project: project, target_project: project, description: "Description") }
+
+ it 'matches noteable merge request json schema' do
+ expect(json_entity).to match_schema('entities/merge_request_noteable')
+ end
end
end
diff --git a/yarn.lock b/yarn.lock
index 5b6038b6e04..be6b64930f2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -918,56 +918,56 @@
consola "^2.3.0"
node-fetch "^2.3.0"
-"@sentry/browser@^5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.7.1.tgz#1f8435e2a325d7a09f830065ebce40a2b3c708a4"
- integrity sha512-K0x1XhsHS8PPdtlVOLrKZyYvi5Vexs9WApdd214bO6KaGF296gJvH1mG8XOY0+7aA5i2A7T3ttcaJNDYS49lzw==
- dependencies:
- "@sentry/core" "5.7.1"
- "@sentry/types" "5.7.1"
- "@sentry/utils" "5.7.1"
+"@sentry/browser@^5.10.2":
+ version "5.10.2"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.10.2.tgz#0bbb05505c58ea998c833cffec3f922fe4b4fa58"
+ integrity sha512-r3eyBu2ln7odvWtXARCZPzpuGrKsD6U9F3gKTu4xdFkA0swSLUvS7AC2FUksj/1BE23y+eB/zzPT+RYJ58tidA==
+ dependencies:
+ "@sentry/core" "5.10.2"
+ "@sentry/types" "5.10.0"
+ "@sentry/utils" "5.10.2"
tslib "^1.9.3"
-"@sentry/core@5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.1.tgz#3eb2b7662cac68245931ee939ec809bf7a639d0e"
- integrity sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg==
+"@sentry/core@5.10.2":
+ version "5.10.2"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.10.2.tgz#1cb64489e6f8363c3249415b49d3f1289814825f"
+ integrity sha512-sKVeFH3v8K8xw2vM5MKMnnyAAwih+JSE3pbNL0CcCCA+/SwX+3jeAo2BhgXev2SAR/TjWW+wmeC9TdIW7KyYbg==
dependencies:
- "@sentry/hub" "5.7.1"
- "@sentry/minimal" "5.7.1"
- "@sentry/types" "5.7.1"
- "@sentry/utils" "5.7.1"
+ "@sentry/hub" "5.10.2"
+ "@sentry/minimal" "5.10.2"
+ "@sentry/types" "5.10.0"
+ "@sentry/utils" "5.10.2"
tslib "^1.9.3"
-"@sentry/hub@5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.1.tgz#a52acd9fead7f3779d96e9965c6978aecc8b9cad"
- integrity sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig==
+"@sentry/hub@5.10.2":
+ version "5.10.2"
+ resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.10.2.tgz#25d9f36b8f7c5cb65cf486737fa61dc9bf69b7e3"
+ integrity sha512-hSlZIiu3hcR/I5yEhlpN9C0nip+U7hiRzRzUQaBiHO4YG4TC58NqnOPR89D/ekiuHIXzFpjW9OQmqtAMRoSUYA==
dependencies:
- "@sentry/types" "5.7.1"
- "@sentry/utils" "5.7.1"
+ "@sentry/types" "5.10.0"
+ "@sentry/utils" "5.10.2"
tslib "^1.9.3"
-"@sentry/minimal@5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.1.tgz#56afc537737586929e25349765e37a367958c1e1"
- integrity sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g==
+"@sentry/minimal@5.10.2":
+ version "5.10.2"
+ resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.10.2.tgz#267c2f3aa6877a0fe7a86971942e83f3ee616580"
+ integrity sha512-GalixiM9sckYfompH5HHTp9XT2BcjawBkcl1DMEKUBEi37+kUq0bivOBmnN1G/I4/wWOUdnAI/kagDWaWpbZPg==
dependencies:
- "@sentry/hub" "5.7.1"
- "@sentry/types" "5.7.1"
+ "@sentry/hub" "5.10.2"
+ "@sentry/types" "5.10.0"
tslib "^1.9.3"
-"@sentry/types@5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.1.tgz#4c4c1d4d891b6b8c2c3c7b367d306a8b1350f090"
- integrity sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ==
+"@sentry/types@5.10.0":
+ version "5.10.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.10.0.tgz#4f0ba31b6e4d5371112c38279f11f66c73b43746"
+ integrity sha512-TW20GzkCWsP6uAxR2JIpIkiitCKyIOfkyDsKBeLqYj4SaZjfvBPnzgNCcYR0L0UsP1/Es6oHooZfIGSkp6GGxQ==
-"@sentry/utils@5.7.1":
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.1.tgz#cf37ad55f78e317665cd8680f202d307fa77f1d0"
- integrity sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q==
+"@sentry/utils@5.10.2":
+ version "5.10.2"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.10.2.tgz#261f575079d30aaf604e59f5f4de0aa21db22252"
+ integrity sha512-UcbbaFpYrGSV448lQ16Cr+W/MPuKUflQQUdrMCt5vgaf5+M7kpozlcji4GGGZUCXIA7oRP93ABoXj55s1OM9zw==
dependencies:
- "@sentry/types" "5.7.1"
+ "@sentry/types" "5.10.0"
tslib "^1.9.3"
"@sourcegraph/code-host-integration@^0.0.14":