summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/boards/components/board.js26
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue5
-rw-r--r--app/assets/javascripts/boards/components/issue_count.vue36
-rw-r--r--app/assets/javascripts/boards/constants.js2
-rw-r--r--app/assets/javascripts/boards/mixins/is_wip_limits.js7
-rw-r--r--app/assets/javascripts/boards/models/list.js3
-rw-r--r--app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue2
-rw-r--r--app/assets/stylesheets/framework/wells.scss2
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/assets/stylesheets/pages/issues/issue_count_badge.scss1
-rw-r--r--app/assets/stylesheets/pages/projects.scss4
-rw-r--r--app/assets/stylesheets/pages/tree.scss7
-rw-r--r--app/controllers/concerns/boards_actions.rb5
-rw-r--r--app/views/dashboard/projects/_blank_state_welcome.html.haml24
-rw-r--r--app/views/shared/boards/components/_board.html.haml26
-rw-r--r--changelogs/unreleased/13768-fix-redo-icn.yml5
-rw-r--r--changelogs/unreleased/35468-fixdetail-margins.yml5
-rw-r--r--changelogs/unreleased/fix-env-doc-link.yml5
-rw-r--r--changelogs/unreleased/sh-avoid-project-count-new-user.yml5
-rw-r--r--doc/administration/auth/ldap-ee.md21
-rw-r--r--doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.pngbin0 -> 21779 bytes
-rw-r--r--doc/administration/gitaly/index.md8
-rw-r--r--doc/topics/autodevops/index.md13
-rw-r--r--doc/user/group/saml_sso/index.md19
-rw-r--r--lib/banzai/filter/markdown_engines/common_mark.rb4
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/factories/lists.rb1
-rw-r--r--spec/features/boards/boards_spec.rb12
-rw-r--r--spec/frontend/boards/components/issue_count_spec.js85
-rw-r--r--spec/javascripts/boards/board_list_spec.js52
32 files changed, 350 insertions, 48 deletions
diff --git a/Gemfile b/Gemfile
index 44ddf068e11..0d7ccb0e455 100644
--- a/Gemfile
+++ b/Gemfile
@@ -139,7 +139,7 @@ gem 'html-pipeline', '~> 2.8'
gem 'deckar01-task_list', '2.2.1'
gem 'gitlab-markup', '~> 1.7.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
-gem 'commonmarker', '~> 0.17'
+gem 'commonmarker', '~> 0.20'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12'
diff --git a/Gemfile.lock b/Gemfile.lock
index cf78043b1ac..a568e0c59e0 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -157,7 +157,7 @@ GEM
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
colored2 (3.1.2)
- commonmarker (0.17.13)
+ commonmarker (0.20.1)
ruby-enum (~> 0.5)
concord (0.1.5)
adamantium (~> 0.2.0)
@@ -1146,7 +1146,7 @@ DEPENDENCIES
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.5)
chronic (~> 0.10.2)
- commonmarker (~> 0.17)
+ commonmarker (~> 0.20)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 3.0)
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 58759fd1efe..64941103eb7 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,15 +1,19 @@
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
+import { GlButtonGroup, GlButton, GlTooltip } from '@gitlab/ui';
import { n__, s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
+import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
import AccessorUtilities from '../../lib/utils/accessor';
import BoardBlankState from './board_blank_state.vue';
import BoardDelete from './board_delete';
import BoardList from './board_list.vue';
+import IssueCount from './issue_count.vue';
import boardsStore from '../stores/boards_store';
import { getBoardSortableDefaultOptions, sortableEnd } from '../mixins/sortable_default_options';
+import { ListType } from '../constants';
export default Vue.extend({
components: {
@@ -17,10 +21,15 @@ export default Vue.extend({
BoardDelete,
BoardList,
Icon,
+ GlButtonGroup,
+ IssueCount,
+ GlButton,
+ GlTooltip,
},
directives: {
Tooltip,
},
+ mixins: [isWipLimitsOn],
props: {
list: {
type: Object,
@@ -53,6 +62,11 @@ export default Vue.extend({
isLoggedIn() {
return Boolean(gon.current_user_id);
},
+ showListHeaderButton() {
+ return (
+ !this.disabled && this.list.type !== ListType.closed && this.list.type !== ListType.blank
+ );
+ },
counterTooltip() {
const { issuesSize } = this.list;
return `${n__('%d issue', '%d issues', issuesSize)}`;
@@ -61,11 +75,19 @@ export default Vue.extend({
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand');
},
isNewIssueShown() {
+ return this.list.type === ListType.backlog || this.showListHeaderButton;
+ },
+ isSettingsShown() {
return (
- this.list.type === 'backlog' ||
- (!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank')
+ this.list.type !== ListType.backlog &&
+ this.showListHeaderButton &&
+ this.list.isExpanded &&
+ this.isWipLimitsOn
);
},
+ showBoardListAndBoardInfo() {
+ return this.list.type !== ListType.blank && this.list.type !== ListType.promotion;
+ },
uniqueKey() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `boards.${this.boardId}.${this.list.type}.${this.list.id}`;
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index b8439bc8741..1e54d4d6b7d 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -71,6 +71,9 @@ export default {
total: this.list.issuesSize,
});
},
+ issuesSizeExceedsMax() {
+ return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount;
+ },
},
watch: {
filters: {
@@ -435,7 +438,7 @@ export default {
ref="list"
:data-board="list.id"
:data-board-type="list.type"
- :class="{ 'is-smaller': showIssueForm }"
+ :class="{ 'is-smaller': showIssueForm, 'bg-danger-100': issuesSizeExceedsMax }"
class="board-list w-100 h-100 list-unstyled mb-0 p-1 js-board-list"
>
<board-card
diff --git a/app/assets/javascripts/boards/components/issue_count.vue b/app/assets/javascripts/boards/components/issue_count.vue
new file mode 100644
index 00000000000..c50a3c1c0d3
--- /dev/null
+++ b/app/assets/javascripts/boards/components/issue_count.vue
@@ -0,0 +1,36 @@
+<script>
+export default {
+ name: 'IssueCount',
+ props: {
+ maxIssueCount: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ issuesSize: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ },
+ computed: {
+ isMaxLimitSet() {
+ return this.maxIssueCount !== 0;
+ },
+ issuesExceedMax() {
+ return this.isMaxLimitSet && this.issuesSize > this.maxIssueCount;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="issue-count">
+ <span class="js-issue-size" :class="{ 'text-danger': issuesExceedMax }">
+ {{ issuesSize }}
+ </span>
+ <span v-if="isMaxLimitSet" class="js-max-issue-size">
+ {{ maxIssueCount }}
+ </span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 3c66c7a0660..dcecfe5e1bb 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -4,6 +4,8 @@ export const ListType = {
backlog: 'backlog',
closed: 'closed',
label: 'label',
+ promotion: 'promotion',
+ blank: 'blank',
};
export default {
diff --git a/app/assets/javascripts/boards/mixins/is_wip_limits.js b/app/assets/javascripts/boards/mixins/is_wip_limits.js
new file mode 100644
index 00000000000..f172179d3c7
--- /dev/null
+++ b/app/assets/javascripts/boards/mixins/is_wip_limits.js
@@ -0,0 +1,7 @@
+export default {
+ computed: {
+ isWipLimitsOn() {
+ return false;
+ },
+ },
+};
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index bb8c8e68297..34e0d0a83ea 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -52,6 +52,9 @@ class List {
this.loadingMore = false;
this.issues = obj.issues || [];
this.issuesSize = obj.issuesSize ? obj.issuesSize : 0;
+ this.maxIssueCount = Object.hasOwnProperty.call(obj, 'max_issue_count')
+ ? obj.max_issue_count
+ : 0;
this.defaultAvatar = defaultAvatar;
if (obj.label) {
diff --git a/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue b/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue
index f0112a5a623..dc7c9d9f174 100644
--- a/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue
+++ b/app/assets/javascripts/related_merge_requests/components/related_merge_requests.vue
@@ -72,7 +72,7 @@ export default {
{{ __('Related merge requests') }}
</span>
<div v-if="totalCount" class="d-inline-flex lh-100 align-middle">
- <div class="mr-count-badge">
+ <div class="mr-count-badge border-width-1px border-style-solid border-color-default">
<div class="mr-count-badge-count">
<svg class="s16 mr-1 text-secondary">
<icon name="merge-request" class="mr-1 text-secondary" />
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index 434cbd6d21c..3eff1807403 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -3,7 +3,7 @@
color: $gl-text-color;
border: 1px solid $border-color;
border-radius: $border-radius-default;
- margin-bottom: $gl-padding;
+ margin-bottom: $gl-padding-8;
.card.card-body-segment {
padding: $gl-padding;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index d26979bc174..90c2e369ccd 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -187,6 +187,10 @@
font-size: 1em;
border-bottom: 1px solid $border-color;
padding: $gl-padding-8 $gl-padding;
+
+ .js-max-issue-size::before {
+ content: '/';
+ }
}
.board-title-text {
diff --git a/app/assets/stylesheets/pages/issues/issue_count_badge.scss b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
index 64ca61f7094..569f323abd8 100644
--- a/app/assets/stylesheets/pages/issues/issue_count_badge.scss
+++ b/app/assets/stylesheets/pages/issues/issue_count_badge.scss
@@ -2,7 +2,6 @@
.mr-count-badge {
display: inline-flex;
border-radius: $border-radius-base;
- border: 1px solid $border-color;
padding: 5px $gl-padding-8;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d96cc163738..55e4c051a6b 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -748,7 +748,7 @@
display: inline-block;
&:not(:last-child) {
- margin-right: $gl-padding;
+ margin-right: $gl-padding-8;
}
&.right {
@@ -798,7 +798,7 @@
}
.btn {
- margin-top: $gl-padding;
+ margin-top: $gl-padding-8;
padding: $gl-btn-vert-padding $gl-btn-padding;
line-height: $gl-btn-line-height;
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 5664f46484e..6e7081aa4ec 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,6 +1,6 @@
.tree-holder {
.nav-block {
- margin: 10px 0;
+ margin: 16px 0;
.btn .fa,
.btn svg {
@@ -15,8 +15,13 @@
}
.tree-controls {
+ display: flex;
text-align: right;
+ .btn {
+ margin-left: 8px;
+ }
+
.btn-group {
margin-left: 10px;
}
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
index a093d0d6e7f..3a9a59b0ad7 100644
--- a/app/controllers/concerns/boards_actions.rb
+++ b/app/controllers/concerns/boards_actions.rb
@@ -9,6 +9,7 @@ module BoardsActions
before_action :boards, only: :index
before_action :board, only: :show
+ before_action :push_wip_limits, only: :index
end
def index
@@ -24,6 +25,10 @@ module BoardsActions
private
+ # Noop on FOSS
+ def push_wip_limits
+ end
+
def boards
strong_memoize(:boards) do
Boards::ListService.new(parent, current_user).execute
diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml
index 913f0e8cfae..003e6f18b33 100644
--- a/app/views/dashboard/projects/_blank_state_welcome.html.haml
+++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml
@@ -1,5 +1,3 @@
-- public_project_count = ProjectsFinder.new(current_user: current_user).execute.count
-
.blank-state-row
- if current_user.can_create_project?
= link_to new_project_path, class: "blank-state blank-state-link" do
@@ -30,19 +28,15 @@
%p.blank-state-text
Groups are the best way to manage projects and members.
- - if public_project_count > 0
- = link_to trending_explore_projects_path, class: "blank-state blank-state-link" do
- .blank-state-icon
- = custom_icon("globe", size: 50)
- .blank-state-body
- %h3.blank-state-title
- Explore public projects
- %p.blank-state-text
- There are
- = number_with_delimiter(public_project_count)
- public projects on this server.
- Public projects are an easy way to allow
- everyone to have read-only access.
+ = link_to trending_explore_projects_path, class: "blank-state blank-state-link" do
+ .blank-state-icon
+ = custom_icon("globe", size: 50)
+ .blank-state-body
+ %h3.blank-state-title
+ Explore public projects
+ %p.blank-state-text
+ Public projects are an easy way to allow
+ everyone to have read-only access.
= link_to "https://docs.gitlab.com/", class: "blank-state blank-state-link" do
.blank-state-icon
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index ffa24d1c041..eb9b7f6c48a 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -42,23 +42,27 @@
%button.board-delete.no-drag.p-0.border-0.has-tooltip.float-right{ type: "button", title: _("Delete list"), ":class": "{ 'd-none': !list.isExpanded }", "aria-label" => _("Delete list"), data: { placement: "bottom" }, "@click.stop" => "deleteBoard" }
= icon("trash")
- .issue-count-badge.no-drag.text-secondary{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"', ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
+ .issue-count-badge.pr-0.no-drag.text-secondary{ "v-if" => "showBoardListAndBoardInfo", ":title": "counterTooltip", "v-tooltip": true, data: { placement: "top" } }
%span.d-inline-flex
%span.issue-count-badge-count
%icon.mr-1{ name: "issues" }
- {{ list.issuesSize }}
+ %issue-count{ ":maxIssueCount" => "list.maxIssueCount",
+ ":issuesSize" => "list.issuesSize" }
= render_if_exists "shared/boards/components/list_weight"
- %button.issue-count-badge-add-button.no-drag.btn.btn-sm.btn-default.ml-1.has-tooltip{ type: "button",
- "@click" => "showNewIssueForm",
- "v-if" => "isNewIssueShown",
- ":class": "{ 'd-none': !list.isExpanded }",
- "aria-label" => _("New issue"),
- "title" => _("New issue"),
- data: { placement: "top", container: "body" } }
- = icon("plus")
+ %gl-button-group.board-list-button-group.pl-2{ "v-if" => "isNewIssueShown || isSettingsShown" }
+ %gl-button.issue-count-badge-add-button.no-drag{ type: "button",
+ "@click" => "showNewIssueForm",
+ "v-if" => "isNewIssueShown",
+ ":class": "{ 'd-none': !list.isExpanded, 'rounded-right': isNewIssueShown && !isSettingsShown }",
+ "aria-label" => _("New issue"),
+ "ref" => "newIssueBtn" }
+ = icon("plus")
+ %gl-tooltip{ ":target" => "() => $refs.newIssueBtn" }
+ = _("New Issue")
+ = render_if_exists 'shared/boards/components/list_settings'
- %board-list{ "v-if" => 'list.type !== "blank" && list.type !== "promotion"',
+ %board-list{ "v-if" => "showBoardListAndBoardInfo",
":list" => "list",
":issues" => "list.issues",
":loading" => "list.loading",
diff --git a/changelogs/unreleased/13768-fix-redo-icn.yml b/changelogs/unreleased/13768-fix-redo-icn.yml
new file mode 100644
index 00000000000..3ef194bc4b0
--- /dev/null
+++ b/changelogs/unreleased/13768-fix-redo-icn.yml
@@ -0,0 +1,5 @@
+---
+title: Replacing incorrect icon for Retry in Pipeline list page
+merge_request: 20510
+author:
+type: changed
diff --git a/changelogs/unreleased/35468-fixdetail-margins.yml b/changelogs/unreleased/35468-fixdetail-margins.yml
new file mode 100644
index 00000000000..74333524ed1
--- /dev/null
+++ b/changelogs/unreleased/35468-fixdetail-margins.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes to inconsistent margins/sapcing in the project detail page
+merge_request: 20395
+author:
+type: changed
diff --git a/changelogs/unreleased/fix-env-doc-link.yml b/changelogs/unreleased/fix-env-doc-link.yml
new file mode 100644
index 00000000000..0ee877a3029
--- /dev/null
+++ b/changelogs/unreleased/fix-env-doc-link.yml
@@ -0,0 +1,5 @@
+---
+title: Fix documentation link from empty environment dashboard
+merge_request: 20415
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-avoid-project-count-new-user.yml b/changelogs/unreleased/sh-avoid-project-count-new-user.yml
new file mode 100644
index 00000000000..916a3579fb6
--- /dev/null
+++ b/changelogs/unreleased/sh-avoid-project-count-new-user.yml
@@ -0,0 +1,5 @@
+---
+title: Disable public project counts on welcome page
+merge_request: 20517
+author:
+type: performance
diff --git a/doc/administration/auth/ldap-ee.md b/doc/administration/auth/ldap-ee.md
index b1d4c8e4298..ba104a4c574 100644
--- a/doc/administration/auth/ldap-ee.md
+++ b/doc/administration/auth/ldap-ee.md
@@ -110,12 +110,23 @@ following.
1. [Restart GitLab][restart] for the changes to take effect.
-To take advantage of group sync, group owners or maintainers will need to create an
-LDAP group link in their group **Settings > LDAP Groups** page.
+To take advantage of group sync, group owners or maintainers will need to [create one
+or more LDAP group links](#adding-group-links).
-Multiple LDAP groups and [filters](#filters-premium-only) can be linked with
-a single GitLab group. When the link is created, an access level/role is
-specified (Guest, Reporter, Developer, Maintainer, or Owner).
+### Adding group links
+
+Once [group sync has been configured](#group-sync) on the instance, one or more LDAP
+groups can be linked to a GitLab group to grant their members access to its
+contents.
+
+Group owners or maintainers can add and use LDAP group links by:
+
+1. Navigating to the group's **Settings > LDAP Synchronization** page. Here, one or more
+ LDAP groups and [filters](#filters-premium-only) can be linked to this GitLab group,
+ each one with a configured [permission level](../../user/permissions.md#group-members-permissions)
+ for its members.
+1. Updating the group's membership by navigating to the group's **Settings > Members**
+ page and clicking **Sync now**.
### Filters **(PREMIUM ONLY)**
diff --git a/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png
new file mode 100644
index 00000000000..4d2c5cdb00c
--- /dev/null
+++ b/doc/administration/gitaly/img/gitlab_gitaly_version_mismatch_v12_4.png
Binary files differ
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 40e7a8d4da2..314f5aad82e 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -718,6 +718,14 @@ Note that `enforced="true"`, meaning that authentication is being enforced.
## Troubleshooting Gitaly
+### Checking versions when using standalone Gitaly nodes
+
+When using standalone Gitaly nodes, you must make sure they are the same version
+as GitLab to ensure full compatibility. Check **Admin Area > Gitaly Servers** on
+your GitLab instance and confirm all Gitaly Servers are `Up to date`.
+
+![Gitaly standalone software versions diagram](img/gitlab_gitaly_version_mismatch_v12_4.png)
+
### `gitaly-debug`
The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 93549ac4de5..f386c5ae42a 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -766,10 +766,17 @@ or a `.buildpacks` file in your project:
and add the URL of the buildpack to use on a line in the file. If you want to
use multiple buildpacks, you can enter them in, one on each line.
-CAUTION: **Caution:**
-Using multiple buildpacks isn't yet supported by Auto DevOps.
+#### Multiple buildpacks
+
+Using multiple buildpacks isn't fully supported by Auto DevOps because, when using the `.buildpacks`
+file, Auto Test will not work.
+
+The buildpack [heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/),
+which is used under the hood to parse the `.buildpacks` file, doesn't provide the necessary commands
+`bin/test-compile` and `bin/test`.
-CAUTION: **Caution:** When using the `.buildpacks` file, Auto Test will not work. The buildpack [heroku-buildpack-multi](https://github.com/heroku/heroku-buildpack-multi/) (which is used under the hood to parse the `.buildpacks` file) doesn't provide the necessary commands `bin/test-compile` and `bin/test`. Make sure to provide the project variable `BUILDPACK_URL` instead.
+If your goal is to use only a single custom buildpack, you should provide the project variable
+`BUILDPACK_URL` instead.
### Custom `Dockerfile`
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 6fd56414796..fef47fba3a1 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -123,6 +123,25 @@ NOTE: **Note:** GitLab is unable to provide support for IdPs that are not listed
When [configuring your identify provider](#configuring-your-identity-provider), please consider the notes below for specific providers to help avoid common issues and as a guide for terminology used.
+### Azure setup notes
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU).
+
+| GitLab Setting | Azure Field |
+|--------------|----------------|
+| Identifier | Identifier (Entity ID) |
+| Assertion consumer service URL | Reply URL (Assertion Consumer Service URL) |
+| Identity provider single sign on URL | Login URL |
+| Certificate fingerprint | Thumbprint |
+
+We recommend:
+
+- **Unique User Identifier (Name identifier)** set to `user.objectID`.
+- **nameid-format** set to persistent.
+
+Set other user attributes and claims according to the [assertions table](#assertions).
+
### Okta setup notes
| GitLab Setting | Okta Field |
diff --git a/lib/banzai/filter/markdown_engines/common_mark.rb b/lib/banzai/filter/markdown_engines/common_mark.rb
index d3af776db05..7be52fc497f 100644
--- a/lib/banzai/filter/markdown_engines/common_mark.rb
+++ b/lib/banzai/filter/markdown_engines/common_mark.rb
@@ -29,7 +29,9 @@ module Banzai
# If in the future the syntax is about to be made GitHub-compatible, please, add `:GITHUB_PRE_LANG` render option below
# and remove `code_block` method from `lib/banzai/renderer/common_mark/html.rb`.
RENDER_OPTIONS = [
- :DEFAULT # default rendering system. Nothing special.
+ # as of commonmarker 0.18.0, we need to use :UNSAFE to get the same as the original :DEFAULT
+ # https://github.com/gjtorikian/commonmarker/pull/81
+ :UNSAFE
].freeze
RENDER_OPTIONS_SOURCEPOS = RENDER_OPTIONS + [
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 16f8ae20805..4f0e112a49d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10291,6 +10291,9 @@ msgstr ""
msgid "List"
msgstr ""
+msgid "List Settings"
+msgstr ""
+
msgid "List Your Gitea Repositories"
msgstr ""
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index 8785d3f0468..eb6f0f27917 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -5,6 +5,7 @@ FactoryBot.define do
board
label
list_type { :label }
+ max_issue_count { 0 }
sequence(:position)
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e26582d3444..c740e4e26d9 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -72,7 +72,6 @@ describe 'Issue Boards', :js do
let!(:closed) { create(:label, project: project, name: 'Closed') }
let!(:accepting) { create(:label, project: project, name: 'Accepting Merge Requests') }
let!(:a_plus) { create(:label, project: project, name: 'A+') }
-
let!(:list1) { create(:list, board: board, label: planning, position: 0) }
let!(:list2) { create(:list, board: board, label: development, position: 1) }
@@ -289,6 +288,17 @@ describe 'Issue Boards', :js do
expect(page).to have_selector('.avatar', count: 1)
end
end
+
+ context 'list header' do
+ let(:total_planning_issues) { "8" }
+
+ it 'shows issue count on the list' do
+ page.within(find(".board:nth-child(2)")) do
+ expect(page.find('.js-issue-size')).to have_text(total_planning_issues)
+ expect(page).not_to have_selector('.js-max-issue-size')
+ end
+ end
+ end
end
context 'new list' do
diff --git a/spec/frontend/boards/components/issue_count_spec.js b/spec/frontend/boards/components/issue_count_spec.js
new file mode 100644
index 00000000000..819d878f4e2
--- /dev/null
+++ b/spec/frontend/boards/components/issue_count_spec.js
@@ -0,0 +1,85 @@
+import { shallowMount } from '@vue/test-utils';
+import IssueCount from '~/boards/components/issue_count.vue';
+
+describe('IssueCount', () => {
+ let vm;
+ let maxIssueCount;
+ let issuesSize;
+
+ const createComponent = props => {
+ vm = shallowMount(IssueCount, { propsData: props });
+ };
+
+ afterEach(() => {
+ maxIssueCount = 0;
+ issuesSize = 0;
+
+ if (vm) vm.destroy();
+ });
+
+ describe('when maxIssueCount is zero', () => {
+ beforeEach(() => {
+ issuesSize = 3;
+
+ createComponent({ maxIssueCount: 0, issuesSize });
+ });
+
+ it('contains issueSize in the template', () => {
+ expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
+ });
+
+ it('does not contains maxIssueCount in the template', () => {
+ expect(vm.contains('.js-max-issue-size')).toBe(false);
+ });
+ });
+
+ describe('when maxIssueCount is greater than zero', () => {
+ beforeEach(() => {
+ maxIssueCount = 2;
+ issuesSize = 1;
+
+ createComponent({ maxIssueCount, issuesSize });
+ });
+
+ afterEach(() => {
+ vm.destroy();
+ });
+
+ it('contains issueSize in the template', () => {
+ expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
+ });
+
+ it('contains maxIssueCount in the template', () => {
+ expect(vm.find('.js-max-issue-size').text()).toEqual(String(maxIssueCount));
+ });
+
+ it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
+ expect(vm.classes('.text-danger')).toBe(false);
+ });
+ });
+
+ describe('when issueSize is greater than maxIssueCount', () => {
+ beforeEach(() => {
+ issuesSize = 3;
+ maxIssueCount = 2;
+
+ createComponent({ maxIssueCount, issuesSize });
+ });
+
+ afterEach(() => {
+ vm.destroy();
+ });
+
+ it('contains issueSize in the template', () => {
+ expect(vm.find('.js-issue-size').text()).toEqual(String(issuesSize));
+ });
+
+ it('contains maxIssueCount in the template', () => {
+ expect(vm.find('.js-max-issue-size').text()).toEqual(String(maxIssueCount));
+ });
+
+ it('has text-danger class', () => {
+ expect(vm.find('.text-danger').text()).toEqual(String(issuesSize));
+ });
+ });
+});
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 37e96e97279..b4e1d3b97b1 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -207,4 +207,56 @@ describe('Board list component', () => {
.catch(done.fail);
});
});
+
+ describe('max issue count warning', () => {
+ beforeEach(done => {
+ ({ mock, component } = createComponent({
+ done,
+ listProps: { type: 'closed', collapsed: true, issuesSize: 50 },
+ }));
+ });
+
+ afterEach(() => {
+ mock.restore();
+ component.$destroy();
+ });
+
+ describe('when issue count exceeds max issue count', () => {
+ it('sets background to bg-danger-100', done => {
+ component.list.issuesSize = 4;
+ component.list.maxIssueCount = 3;
+
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector('.bg-danger-100')).not.toBeNull();
+
+ done();
+ });
+ });
+ });
+
+ describe('when list issue count does NOT exceed list max issue count', () => {
+ it('does not sets background to bg-danger-100', done => {
+ component.list.issuesSize = 2;
+ component.list.maxIssueCount = 3;
+
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector('.bg-danger-100')).toBeNull();
+
+ done();
+ });
+ });
+ });
+
+ describe('when list max issue count is 0', () => {
+ it('does not sets background to bg-danger-100', done => {
+ component.list.maxIssueCount = 0;
+
+ Vue.nextTick(() => {
+ expect(component.$el.querySelector('.bg-danger-100')).toBeNull();
+
+ done();
+ });
+ });
+ });
+ });
});