提交 85e95876 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@12-7-stable-ee

上级 680a5284
......@@ -4,7 +4,6 @@ entry.
## 12.7.0
- No changes.
### Security (6 changes, 2 of them are from the community)
- Ensure content matches extension on image uploads. !20697
......
......@@ -374,7 +374,7 @@ export default {
<div
:data-can-create-note="getNoteableData.current_user.can_create_note"
class="files d-flex"
class="files d-flex prepend-top-default"
>
<div
v-show="showTreeList"
......
<script>
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
......@@ -62,6 +63,9 @@ export default {
showDropdowns() {
return !this.commit && this.mergeRequestDiffs.length;
},
fileTreeIcon() {
return this.showTreeList ? 'collapse-left' : 'expand-left';
},
toggleFileBrowserTitle() {
return this.showTreeList ? __('Hide file browser') : __('Show file browser');
},
......@@ -87,7 +91,7 @@ export default {
</script>
<template>
<div class="mr-version-controls border-top">
<div class="mr-version-controls border-top border-bottom">
<div
class="mr-version-menus-container content-block"
:class="{
......@@ -104,17 +108,17 @@ export default {
:title="toggleFileBrowserTitle"
@click="toggleShowTreeList"
>
<icon name="file-tree" />
<icon :name="fileTreeIcon" />
</button>
<div v-if="showDropdowns" class="d-flex align-items-center compare-versions-container">
{{ __('Compare') }}
Changes between
<compare-versions-dropdown
:other-versions="mergeRequestDiffs"
:merge-request-version="mergeRequestDiff"
:show-commit-count="true"
class="mr-version-dropdown"
/>
{{ __('and') }}
and
<compare-versions-dropdown
:other-versions="comparableDiffs"
:base-version-path="baseVersionPath"
......
......@@ -123,20 +123,6 @@ export default {
}
return s__('MRDiff|Show full file');
},
changedFile() {
const {
new_path: changed,
deleted_file: deleted,
new_file: tempFile,
...diffFile
} = this.diffFile;
return {
...diffFile,
changed: Boolean(changed),
deleted,
tempFile,
};
},
},
mounted() {
polyfillSticky(this.$refs.header);
......@@ -235,7 +221,7 @@ export default {
<div
v-if="!diffFile.submodule && addMergeRequestButtons"
class="file-actions d-none d-sm-flex align-items-center"
class="file-actions d-none d-sm-block"
>
<diff-stats :added-lines="diffFile.added_lines" :removed-lines="diffFile.removed_lines" />
<div class="btn-group" role="group">
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import { n__ } from '~/locale';
export default {
components: { Icon },
props: {
addedLines: {
type: Number,
......@@ -19,7 +21,7 @@ export default {
},
computed: {
filesText() {
return n__('file', 'files', this.diffFilesLength);
return n__('File', 'Files', this.diffFilesLength);
},
isCompareVersionsHeader() {
return Boolean(this.diffFilesLength);
......@@ -37,21 +39,14 @@ export default {
}"
>
<div v-if="diffFilesLength !== null" class="diff-stats-group">
<span class="text-secondary bold">{{ diffFilesLength }} {{ filesText }}</span>
<icon name="doc-code" class="diff-stats-icon text-secondary" />
<strong>{{ diffFilesLength }} {{ filesText }}</strong>
</div>
<div
class="diff-stats-group cgreen d-flex align-items-center"
:class="{ bold: isCompareVersionsHeader }"
>
<span>+</span>
<span class="js-file-addition-line">{{ addedLines }}</span>
<div class="diff-stats-group cgreen">
<icon name="file-addition" class="diff-stats-icon" /> <strong>{{ addedLines }}</strong>
</div>
<div
class="diff-stats-group cred d-flex align-items-center"
:class="{ bold: isCompareVersionsHeader }"
>
<span>-</span>
<span class="js-file-deletion-line">{{ removedLines }}</span>
<div class="diff-stats-group cred">
<icon name="file-deletion" class="diff-stats-icon" /> <strong>{{ removedLines }}</strong>
</div>
</div>
</template>
......@@ -4,6 +4,7 @@ import { GlTooltipDirective } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
import FileRowStats from './file_row_stats.vue';
export default {
directives: {
......@@ -47,6 +48,9 @@ export default {
return acc;
}, []);
},
fileRowExtraComponent() {
return this.hideFileStats ? null : FileRowStats;
},
},
methods: {
...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']),
......@@ -54,8 +58,8 @@ export default {
this.search = '';
},
},
searchPlaceholder: sprintf(s__('MergeRequest|Search files (%{modifier_key}P)'), {
modifier_key: /Mac/i.test(navigator.userAgent) ? '' : 'Ctrl+',
searchPlaceholder: sprintf(s__('MergeRequest|Filter files or search with %{modifier_key}+p'), {
modifier_key: /Mac/i.test(navigator.userAgent) ? 'cmd' : 'ctrl',
}),
};
</script>
......@@ -93,6 +97,7 @@ export default {
:file="file"
:level="0"
:hide-extra-on-tree="true"
:extra-component="fileRowExtraComponent"
:show-changed-icon="true"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile"
......
......@@ -36,17 +36,12 @@ export default {
required: false,
default: true,
},
showChangedStatus: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
changedIcon() {
// False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
const suffix = this.showStagedIcon ? '-solid' : '';
const suffix = !this.file.changed && this.file.staged && this.showStagedIcon ? '-solid' : '';
return `${getCommitIconMap(this.file).icon}${suffix}`;
},
......@@ -91,8 +86,8 @@ export default {
<span
v-gl-tooltip.right
:title="tooltipTitle"
:class="[{ 'ml-auto': isCentered }, changedIconClass]"
class="file-changed-icon d-flex align-items-center "
:class="{ 'ml-auto': isCentered }"
class="file-changed-icon d-inline-block"
>
<icon v-if="showIcon" :name="changedIcon" :size="size" :class="changedIconClass" />
</span>
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import FileHeader from '~/vue_shared/components/file_row_header.vue';
import FileIcon from '~/vue_shared/components/file_icon.vue';
import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue';
......@@ -8,6 +9,7 @@ export default {
components: {
FileHeader,
FileIcon,
Icon,
ChangedFileIcon,
},
props: {
......@@ -24,7 +26,6 @@ export default {
required: false,
default: null,
},
hideExtraOnTree: {
type: Boolean,
required: false,
......@@ -142,17 +143,17 @@ export default {
@mouseleave="toggleDropdown(false)"
>
<div class="file-row-name-container">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated d-flex">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated">
<file-icon
v-if="!showChangedIcon || file.type === 'tree'"
class="file-row-icon text-secondary mr-1"
class="file-row-icon"
:file-name="file.name"
:loading="file.loading"
:folder="isTree"
:opened="file.opened"
:size="16"
/>
<file-icon v-else :file-name="file.name" :size="16" css-classes="top mr-1" />
<changed-file-icon v-else :file="file" :size="16" class="append-right-5" />
{{ file.name }}
</span>
<component
......@@ -162,7 +163,6 @@ export default {
:dropdown-open="dropdownOpen"
@toggle="toggleDropdown($event)"
/>
<changed-file-icon :file="file" :size="16" class="append-right-5" />
</div>
</div>
<template v-if="file.opened || file.isHeader">
......@@ -172,6 +172,7 @@ export default {
:file="childFile"
:level="childFilesLevel"
:hide-extra-on-tree="hideExtraOnTree"
:extra-component="extraComponent"
:show-changed-icon="showChangedIcon"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="clickedFile"
......
......@@ -14,9 +14,9 @@
cursor: pointer;
@media (min-width: map-get($grid-breakpoints, md)) {
// The `+11` is to ensure the file header border shows when scrolled -
// The `-1` below is to prevent two borders from clashing up against eachother -
// the bottom of the compare-versions header and the top of the file header
$mr-file-header-top: $mr-version-controls-height + $header-height + $mr-tabs-height + 11;
$mr-file-header-top: $mr-version-controls-height + $header-height + $mr-tabs-height - 1;
position: -webkit-sticky;
position: sticky;
......@@ -552,7 +552,7 @@ table.code {
.diff-stats {
align-items: center;
padding: 0 1rem;
padding: 0 0.25rem;
.diff-stats-group {
padding: 0 0.25rem;
......@@ -564,7 +564,7 @@ table.code {
&.is-compare-versions-header {
.diff-stats-group {
padding: 0 0.25rem;
padding: 0 0.5rem;
}
}
}
......@@ -1059,8 +1059,8 @@ table.code {
.diff-tree-list {
position: -webkit-sticky;
position: sticky;
$top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 11px;
top: $header-height + $mr-tabs-height + $mr-version-controls-height + 11px;
$top-pos: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
top: $header-height + $mr-tabs-height + $mr-version-controls-height + 10px;
max-height: calc(100vh - #{$top-pos});
z-index: 202;
......@@ -1097,7 +1097,10 @@ table.code {
.tree-list-scroll {
max-height: 100%;
padding-top: $grid-size;
padding-bottom: $grid-size;
border-top: 1px solid $border-color;
border-bottom: 1px solid $border-color;
overflow-y: scroll;
overflow-x: auto;
}
......
......@@ -708,7 +708,7 @@
.mr-version-controls {
position: relative;
z-index: 203;
background: $white-light;
background: $gray-light;
color: $gl-text-color;
margin-top: -1px;
......@@ -732,7 +732,7 @@
}
.content-block {
padding: $gl-padding;
padding: $gl-padding-top $gl-padding;
border-bottom: 0;
}
......
......@@ -394,6 +394,11 @@ class User < ApplicationRecord
Gitlab::CurrentSettings.minimum_password_length..Devise.password_length.max
end
# Generate a random password that conforms to the current password length settings
def random_password
Devise.friendly_token(password_length.max)
end
# Devise method overridden to allow sign in with email or username
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
......
......@@ -222,6 +222,7 @@ class ProjectPolicy < BasePolicy
enable :read_deployment
enable :read_merge_request
enable :read_sentry_issue
enable :update_sentry_issue
enable :read_prometheus
end
......
......@@ -7,7 +7,7 @@ module ErrorTracking
return unauthorized if unauthorized
begin
response = fetch
response = perform
rescue Sentry::Client::Error => e
return error(e.message, :bad_request)
rescue Sentry::Client::MissingKeysError => e
......@@ -22,7 +22,7 @@ module ErrorTracking
private
def fetch
def perform
raise NotImplementedError,
"#{self.class} does not implement #{__method__}"
end
......@@ -62,5 +62,9 @@ module ErrorTracking
def can_read?
can?(current_user, :read_sentry_issue, project)
end
def can_update?
can?(current_user, :update_sentry_issue, project)
end
end
end
......@@ -4,7 +4,7 @@ module ErrorTracking
class IssueDetailsService < ErrorTracking::BaseService
private
def fetch
def perform
project_error_tracking_setting.issue_details(issue_id: params[:issue_id])
end
......
......@@ -4,7 +4,7 @@ module ErrorTracking
class IssueLatestEventService < ErrorTracking::BaseService
private
def fetch
def perform
project_error_tracking_setting.issue_latest_event(issue_id: params[:issue_id])
end
......
......@@ -4,6 +4,16 @@ module ErrorTracking
class IssueUpdateService < ErrorTracking::BaseService
private
def perform
response = fetch
unless parse_errors(response).present?
response[:closed_issue_iid] = update_related_issue&.iid
end
response
end
def fetch
project_error_tracking_setting.update_issue(
issue_id: params[:issue_id],
......@@ -11,12 +21,58 @@ module ErrorTracking
)
end
def update_related_issue
issue = related_issue
return unless issue
close_and_create_note(issue)
end
def close_and_create_note(issue)
return unless resolving? && issue.opened?
processed_issue = close_issue(issue)
return unless processed_issue.reset.closed?
create_system_note(processed_issue)
processed_issue
end
def close_issue(issue)
Issues::CloseService
.new(project, current_user)
.execute(issue, system_note: false)
end
def create_system_note(issue)
SystemNoteService.close_after_error_tracking_resolve(issue, project, current_user)
end
def related_issue
SentryIssueFinder
.new(project, current_user: current_user)
.execute(params[:issue_id])
&.issue
end
def resolving?
update_params[:status] == 'resolved'
end
def update_params
params.except(:issue_id)
end
def parse_response(response)
{ updated: response[:updated].present? }
{
updated: response[:updated].present?,
closed_issue_iid: response[:closed_issue_iid]
}
end
def check_permissions
return error('Error Tracking is not enabled') unless enabled?
return error('Access denied', :unauthorized) unless can_update?
end
end
end
......@@ -12,7 +12,7 @@ module ErrorTracking
private
def fetch
def perform
project_error_tracking_setting.list_sentry_issues(
issue_status: issue_status,
limit: limit,
......
......@@ -12,7 +12,7 @@ module ErrorTracking
private
def fetch
def perform
project_error_tracking_setting.list_sentry_projects
end
......
......@@ -3,9 +3,6 @@
module PagesDomains
class CreateAcmeOrderService
attr_reader :pages_domain
# TODO: remove this hack after https://gitlab.com/gitlab-org/gitlab/issues/30146 is implemented
# This makes GitLab automatically retry the certificate obtaining process every 2 hours if process wasn't finished
SHORT_EXPIRATION_DELAY = 2.hours
def initialize(pages_domain)
@pages_domain = pages_domain
......@@ -20,7 +17,7 @@ module PagesDomains
private_key = OpenSSL::PKey::RSA.new(4096)
saved_order = pages_domain.acme_orders.create!(
url: order.url,
expires_at: [order.expires, SHORT_EXPIRATION_DELAY.from_now].min,
expires_at: order.expires,
private_key: private_key.to_pem,
challenge_token: challenge.token,
......
......@@ -99,6 +99,12 @@ module SystemNoteService
::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).change_time_spent
end
def close_after_error_tracking_resolve(issue, project, author)
body = _('resolved the corresponding error and closed the issue.')
create_note(NoteSummary.new(issue, project, author, body, action: 'closed'))
end
def change_status(noteable, project, author, status, source = nil)
::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_status(status, source)
end
......
......@@ -23,7 +23,7 @@ module Users
@reset_token = user.generate_reset_token if params[:reset_password]
if user_params[:force_random_password]
random_password = Devise.friendly_token.first(User.password_length.min)
random_password = User.random_password
user.password = user.password_confirmation = random_password
end
end
......
---
title: Fixes random passwords generated not conforming to minimum_password_length setting
merge_request: 23387
author:
type: fixed
---
title: Remove invalid data from jira_tracker_data table
merge_request: 23621
author:
type: fixed
---
title: Allow users to sign out on a read-only instance
merge_request: 23545
author:
type: fixed
---
title: Close Issue when resolving corresponding Sentry error
merge_request: 22744
author:
type: added
---
title: Fix loading of sub-epics caused by wrong subscription check.
merge_request: 23184
author:
type: fixed
---
title: Reverts MR diff redesign which fixes Web IDE visual bugs including file dropdown
not showing up
merge_request: 23428
author:
type: fixed
---
title: Fix Bitbucket Server importer error handler
merge_request: 23310
author:
type: fixed
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveInvalidJiraData < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
sql = "DELETE FROM jira_tracker_data WHERE \
(length(encrypted_api_url) > 0 AND encrypted_api_url_iv IS NULL) \
OR (length(encrypted_url) > 0 AND encrypted_url_iv IS NULL) \
OR (length(encrypted_username) > 0 AND encrypted_username_iv IS NULL) \
OR (length(encrypted_password) > 0 AND encrypted_password_iv IS NULL)"
execute(sql)
end
def down
# We need to figure out why migrating data to jira_tracker_data table
# failed and then can recreate the data
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_01_17_112554) do
ActiveRecord::Schema.define(version: 2020_01_23_155929) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......
......@@ -121,7 +121,9 @@ In this example we can see that server processed an HTTP request with URL
## `api_json.log`
Introduced in GitLab 10.0, this file lives in
> Introduced in GitLab 10.0.
This file lives in
`/var/log/gitlab/gitlab-rails/api_json.log` for Omnibus GitLab packages or in
`/home/git/gitlab/log/api_json.log` for installations from source.
......@@ -159,6 +161,21 @@ October 07, 2014 11:25: User "Claudie Hodkiewicz" (nasir_stehr@olson.co.uk) was
October 07, 2014 11:25: Project "project133" was removed
```
## `application_json.log`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/22812) in GitLab 12.7.
This file lives in `/var/log/gitlab/gitlab-rails/application_json.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/application_json.log` for
installations from source.
It contains the JSON version of the logs in `application.log` like the example below:
``` json
{"severity":"INFO","time":"2020-01-14T13:35:15.466Z","correlation_id":"3823a1550b64417f9c9ed8ee0f48087e","message":"User \"Administrator\" (admin@example.com) was created"}
{"severity":"INFO","time":"2020-01-14T13:35:15.466Z","correlation_id":"78e3df10c9a18745243d524540bd5be4","message":"Project \"project133\" was removed"}
```
## `integrations_json.log`
This file lives in `/var/log/gitlab/gitlab-rails/integrations_json.log` for
......@@ -174,7 +191,9 @@ It contains information about [integrations](../user/project/integrations/projec
## `kubernetes.log`
Introduced in GitLab 11.6. This file lives in
> Introduced in GitLab 11.6.
This file lives in
`/var/log/gitlab/gitlab-rails/kubernetes.log` for Omnibus GitLab
packages or in `/home/git/gitlab/log/kubernetes.log` for
installations from source.
......@@ -320,13 +339,17 @@ It logs information whenever a [repository check is run][repocheck] on a project
## `importer.log`
Introduced in GitLab 11.3. This file lives in `/var/log/gitlab/gitlab-rails/importer.log` for
> Introduced in GitLab 11.3.
This file lives in `/var/log/gitlab/gitlab-rails/importer.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/importer.log` for
installations from source.
## `auth.log`
Introduced in GitLab 12.0. This file lives in `/var/log/gitlab/gitlab-rails/auth.log` for
> Introduced in GitLab 12.0.
This file lives in `/var/log/gitlab/gitlab-rails/auth.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/auth.log` for
installations from source.
......@@ -356,7 +379,9 @@ GraphQL queries are recorded in that file. For example:
## `migrations.log`
Introduced in GitLab 12.3. This file lives in `/var/log/gitlab/gitlab-rails/migrations.log` for
> Introduced in GitLab 12.3.
This file lives in `/var/log/gitlab/gitlab-rails/migrations.log` for
Omnibus GitLab packages or in `/home/git/gitlab/log/migrations.log` for
installations from source.
......@@ -406,7 +431,9 @@ It is stored at:
## `elasticsearch.log`
Introduced in GitLab 12.6. This file lives in
> Introduced in GitLab 12.6.
This file lives in
`/var/log/gitlab/gitlab-rails/elasticsearch.log` for Omnibus GitLab
packages or in `/home/git/gitlab/log/elasticsearch.log` for installations
from source.
......
......@@ -129,8 +129,14 @@ Example response:
}
```
Deploy Keys are bound to the creating user, so if you query with a deploy key
fingerprint you get additional information about the projects using that key:
## Get user by deploy key fingerprint
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/119209) in GitLab 12.7.
Deploy keys are bound to the creating user, so if you query with a deploy key
fingerprint you get additional information about the projects using that key.
Example request:
```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://gitlab.example.com/api/v4/keys?fingerprint=SHA256%3AnUhzNyftwADy8AH3wFY31tAKs7HufskYTte2aXo%2FlCg
......
......@@ -63,7 +63,8 @@ POST /projects/:id/approvals
### Get project-level rules
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11877) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.3.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11877) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.3.
> - `protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/issues/460) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.7.
You can request information about a project's approval rules using the following endpoint:
......@@ -130,6 +131,31 @@ GET /projects/:id/approval_rules
"ldap_access": null
}
],
"protected_branches": [
{
"id": 1,
"name": "master",
"push_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"merge_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"unprotect_access_levels": [
{
"access_level": 40,
"access_level_description": "Maintainers"
}
],
"code_owner_approval_required": "false"
}
],
"contains_hidden_groups": false
}
]
......@@ -147,13 +173,14 @@ POST /projects/:id/approval_rules
**Parameters:**
| Attribute | Type | Required | Description |
|----------------------|---------|----------|-----------------------------------------------------------|
| `id` | integer | yes | The ID of a project |
| `name` | string | yes | The name of the approval rule |
| `approvals_required` | integer | yes | The number of required approvals for this rule |
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
| Attribute | Type | Required | Description |
|------------------------|---------|----------|------------------------------------------------------------------|
| `id` | integer | yes | The ID of a project |
| `name` | string | yes | The name of the approval rule |
| `approvals_required` | integer | yes | The number of required approvals for this rule |
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by |
```json
{
......@@ -207,6 +234,31 @@ POST /projects/:id/approval_rules
"ldap_access": null
}
],
"protected_branches": [
{
"id": 1,
"name": "master",
"push_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"merge_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"unprotect_access_levels": [
{
"access_level": 40,
"access_level_description": "Maintainers"
}
],
"code_owner_approval_required": "false"
}
],
"contains_hidden_groups": false
}
```
......@@ -225,14 +277,15 @@ PUT /projects/:id/approval_rules/:approval_rule_id
**Parameters:**
| Attribute | Type | Required | Description |
|----------------------|---------|----------|-----------------------------------------------------------|
| `id` | integer | yes | The ID of a project |
| `approval_rule_id` | integer | yes | The ID of a approval rule |
| `name` | string | yes | The name of the approval rule |
| `approvals_required` | integer | yes | The number of required approvals for this rule |
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
| Attribute | Type | Required | Description |
|------------------------|---------|----------|------------------------------------------------------------------|
| `id` | integer | yes | The ID of a project |
| `approval_rule_id` | integer | yes | The ID of a approval rule |
| `name` | string | yes | The name of the approval rule |
| `approvals_required` | integer | yes | The number of required approvals for this rule |
| `user_ids` | Array | no | The ids of users as approvers |
| `group_ids` | Array | no | The ids of groups as approvers |
| `protected_branch_ids` | Array | no | **(PREMIUM)** The ids of protected branches to scope the rule by |
```json
{
......@@ -286,6 +339,31 @@ PUT /projects/:id/approval_rules/:approval_rule_id
"ldap_access": null
}
],
"protected_branches": [
{
"id": 1,
"name": "master",
"push_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"merge_access_levels": [
{
"access_level": 30,
"access_level_description": "Developers + Maintainers"
}
],
"unprotect_access_levels": [
{
"access_level": 40,
"access_level_description": "Maintainers"
}
],
"code_owner_approval_required": "false"
}
],
"contains_hidden_groups": false
}
```
......
......@@ -34,6 +34,7 @@ Example response:
```json
[
{
"id": 1,
"name": "master",
"push_access_levels": [
{
......@@ -61,6 +62,7 @@ Example response:
```json
[
{
"id": 1,
"name": "master",
"push_access_levels": [
{
......@@ -105,6 +107,7 @@ Example response:
```json
{
"id": 1,
"name": "master",
"push_access_levels": [
{
......@@ -129,6 +132,7 @@ Example response:
```json
{
"id": 1,
"name": "master",
"push_access_levels": [
{
......@@ -179,6 +183,7 @@ Example response:
```json
{
"id": 1,
"name": "*-stable",
"push_access_levels": [
{
......@@ -209,6 +214,7 @@ Example response:
```json
{
"id": 1,
"name": "*-stable",
"push_access_levels": [
{
......@@ -251,6 +257,7 @@ Example response:
```json
{
"id": 1,
"name": "*-stable",
"push_access_levels": [
{
......
......@@ -4,31 +4,55 @@ description: "Learn how long your open merge requests have spent in code review,
# Code Review Analytics **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/38062) in GitLab ([Starter](https://about.gitlab.com/pricing/)) 12.7.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/38062) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.7.
Want to learn how long your open merge requests have spent in code review? Or what distinguishes your longest-running code reviews? These are some of the questions Code Review Analytics is designed to answer.
Code Review Analytics makes it easy to view the longest-running reviews among open merge requests,
enabling you to take action on individual MRs and reduce overall cycle time.
NOTE: **Note:**
Initially no data will appear. Data will populate as users comment on open merge requests.
Initially, no data will appear. Data is populated as users comment on open merge requests.
## Overview
Code Review Analytics displays a collection of merge requests in a table. These are all the open merge requests that are considered to be in code review. This feature considers code review to begin when a merge request receives its first comment from someone other than the author. The rows of the table are sorted by review time so the longest reviews appear at the top. There are also columns to display the author, approvers, comment count, and line -/+ counts.
Code Review Analytics displays a table of open merge requests which are currently considered to be in code review.
The code review period for an MR is automatically identified as the time since the first non-author comment.
This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead) and others who want to understand broad code review dynamics, and identify patterns to help explain them. You can use Code Review Analytics to expose your team's unique challenges with code review, and identify improvements that might substantially accelerate your development cycle.
To access Code Review Analytics, from your project's menu, go to **Project Analytics > Code Review**.
- The table is sorted by review duration, helping you quickly find the longest-running reviews which may need intervention or to be broken down into smaller parts.
- You can filter the list of MRs by milestone and label.
- Columns to display the author, approvers, comment count, and line change (-/+) counts.
## Use cases
Perhaps your team agrees that code review is moving too slow, or the [Cycle Analytics feature](https://docs.gitlab.com/ee/user/analytics/cycle_analytics.html) shows that "Review" is your team's most time-consuming step. You can use Code Review Analytics to see what is currently moving slowest, and analyze the patterns and trends between them. Lots of comments or commits? Maybe the code is too complex. A particular author is involved? Maybe more training is advisable. Few comments and approvers? Maybe your team is understaffed.
This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
and others who want to understand broad code review dynamics, and identify patterns to help explain them.
You can use Code Review Analytics to expose your team's unique challenges with code review, and
identify improvements that might substantially accelerate your development cycle.
Code Review Analytics can be used when:
- Your team agrees that code review is moving too slow.
- The [Cycle Analytics feature](cycle_analytics.md) shows that reviews are your team's most time-consuming step.
You can use Code Review Analytics to see the types of work that are currently moving the slowest, and analyze the patterns
and trends between them. For example:
- Lots of comments or commits? Maybe the code is too complex.
- A particular author is involved? Maybe more training is required.
- Few comments and approvers? Maybe your team is understaffed.
## Permissions
- On [Starter or Bronze tier](https://about.gitlab.com/pricing/) and above.
- By users with [Reporter access] and above.
- By users with Reporter access and above.
## Feature flag
## Disable with feature flag
Code Review Analytics is [currently protected by a feature flag](https://gitlab.com/gitlab-org/gitlab/issues/194165) that defaults to "enabled" - meaning the feature is available. If you experience performance problems or otherwise wish to disable the feature, a GitLab administrator can execute a command in a Rails console:
Code Review Analytics is [currently enabled by a feature flag](https://gitlab.com/gitlab-org/gitlab/issues/194165)
that defaults to ON, meaning the feature is available. If you experience performance problems or
otherwise wish to disable the feature, a GitLab administrator can execute a command in a Rails console:
```ruby
Feature.disable(:code_review_analytics)
......
......@@ -163,6 +163,15 @@ This chart uses the global page filters for displaying data based on the selecte
group, projects, and timeframe. In addition, specific stages can be selected
from within the chart itself.
### Chart median line
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/36675) in GitLab 12.7.
The median line on the chart displays data that is offset by the number of days selected.
For example, if 30 days worth of data has been selected (for example, 2019-12-16 to 2020-01-15) the
median line will represent the previous 30 days worth of data (2019-11-16 to 2019-12-16)
as a metric to compare against.
### Enabling chart
By default, this chart is disabled for self-managed instances. To enable it, ask an
......
......@@ -16,11 +16,11 @@ Once enabled, click on **Analytics** from the top navigation bar.
From the centralized analytics workspace, the following analytics are available:
- [Code Review Analytics](code_review_analytics.md), enabled with the `code_review_analytics`
[feature flag](../../development/feature_flags/development.html#enabling-a-feature-flag-in-development). **(STARTER)**
[feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development). **(STARTER)**
- [Cycle Analytics](cycle_analytics.md), enabled with the `cycle_analytics`
[feature flag](../../development/feature_flags/development.html#enabling-a-feature-flag-in-development). **(PREMIUM)**
[feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development). **(PREMIUM)**
- [Productivity Analytics](productivity_analytics.md), enabled with the `productivity_analytics`
[feature flag](../../development/feature_flags/development.html#enabling-a-feature-flag-in-development). **(PREMIUM)**
[feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development). **(PREMIUM)**
NOTE: **Note:**
Project-level Cycle Analytics are still available at a project's **Project > Cycle Analytics**.
......
......@@ -135,7 +135,7 @@ using environment variables.
| `DS_ANALYZER_IMAGE_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). |
| `DS_ANALYZER_IMAGE_TAG` | Override the Docker tag of the official default images. Read more about [customizing analyzers](analyzers.md). |
| `DS_PYTHON_VERSION` | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)|
| `DS_PIP_VERSION` | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the docker image is used. |
| `DS_PIP_VERSION` | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12811) in GitLab 12.7) |
| `DS_PIP_DEPENDENCY_PATH` | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) |
| `GEMNASIUM_DB_LOCAL_PATH` | Path to local gemnasium database (default `/gemnasium-db`).
| `GEMNASIUM_DB_REMOTE_URL` | Repository URL for fetching the gemnasium database (default `https://gitlab.com/gitlab-org/security-products/gemnasium-db.git`).
......
......@@ -661,6 +661,65 @@ management project. Refer to the
[chart](https://gitlab.com/gitlab-org/charts/gitlab-runner) for the
available configuration options.
### Install Cilium using GitLab CI
> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/merge_requests/22) in GitLab 12.8.
[Cilium](https://cilium.io/) is a networking plugin for Kubernetes
that you can use to implement support for
[NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/)
resources.
Enable Cilium in the `.gitlab/managed-apps/config.yaml` file to install it:
```yaml
# possible values are gke, eks or you can leave it blank
clusterType: gke
cilium:
installed: true
```
The `clusterType` variable enables the recommended Helm variables for
a corresponding cluster type, the default value is blank. You can
check the recommended variables for each cluster type in the official
documentation:
- [Google GKE](https://cilium.readthedocs.io/en/stable/gettingstarted/k8s-install-gke/#prepare-deploy-cilium)
- [AWS EKS](https://cilium.readthedocs.io/en/stable/gettingstarted/k8s-install-eks/#prepare-deploy-cilium)
You can customize Cilium's Helm variables by defining the
`.gitlab/managed-apps/cilium/values.yaml` file in your cluster
management project. Refer to the
[Cilium chart](https://github.com/cilium/cilium/tree/master/install/kubernetes/cilium)
for the available configuration options.
CAUTION: **Caution:**
Installation and removal of the Cilium [requires restart](https://cilium.readthedocs.io/en/stable/gettingstarted/k8s-install-gke/#restart-remaining-pods)
of all affected pods in all namespaces to ensure that they are
[managed](https://cilium.readthedocs.io/en/stable/troubleshooting/#ensure-pod-is-managed-by-cilium)
by the correct networking plugin.
NOTE: **Note:**
Major upgrades might require additional setup steps, please consult
the official [upgrade guide](https://docs.cilium.io/en/stable/install/upgrade/) for more
information.
By default, the drop log for traffic is logged out by the
`cilium-monitor` sidecar container. You can check these logs via:
```bash
kubectl -n gitlab-managed-apps logs cilium-XXXX cilium-monitor
```
Drop logging can be disabled via `.gitlab/managed-apps/cilium/values.yaml`:
```yml
agent:
monitor:
enabled: false
```
## Upgrading applications
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24789) in GitLab 11.8.
......
---
redirect_to: '../instance_statistics/dev_ops_score.md'
---
This document was moved to [another location](../instance_statistics/dev_ops_score.md).
# DevOps Score
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/30469) in GitLab 9.3.
> [Renamed from Conversational Development Index](https://gitlab.com/gitlab-org/gitlab/issues/20976) in GitLab 12.6.
NOTE: **Note:**
......
......@@ -1375,6 +1375,22 @@ to the sides of the "dash" lines in the second row. This will affect every cell
| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
#### Copy from spreadsheet and paste in Markdown
[Introduced](https://gitlab.com/gitlab-org/gitlab/issues/27205) in GitLab 12.7.
If you're working in spreadsheet software (e.g. Microsoft Excel, Google
Sheets, Apple Numbers), you can copy from a spreadsheet, and GitLab will
paste it as a Markdown table. For example, suppose you have the
following spreadsheet:
![Copy from spreadsheet](img/markdown_copy_from_spreadsheet_v12_7.png)
Select the cells and copy them to your clipboard. Open a GitLab Markdown
entry and paste the spreadsheet:
![Paste to Markdown table](img/markdown_paste_table_v12_7.png)
## References
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
......
......@@ -266,6 +266,8 @@ The GitLab Conan repository supports the following Conan CLI commands:
## Using GitLab CI with Conan packages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11678) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.7.
To work with Conan commands within [GitLab CI](./../../../ci/README.md), you can use
`CI_JOB_TOKEN` in place of the personal access token in your commands.
......
......@@ -151,21 +151,24 @@ There are 2 methods to specify a variable in a query or dashboard:
By default, all projects include a GitLab-defined Prometheus dashboard, which
includes a few key metrics, but you can also define your own custom dashboards.
You may create a new file from scratch or duplicate a GitLab-defined Prometheus
dashboard.
NOTE: **Note:**
The custom metrics as defined below do not support alerts, unlike
[additional metrics](#adding-additional-metrics-premium).
#### Adding a new dashboard to your project
You can configure a custom dashboard by adding a new `.yml` file into a project's repository. Only `.yml` files present in the projects **default** branch are displayed on the project's **Operations > Metrics** section.
You may create a new file from scratch or duplicate a GitLab-defined dashboard.
You can configure a custom dashboard by adding a new YAML file into your project's
`.gitlab/dashboards/` directory. In order for the dashboards to be displayed on
the project's **Operations > Metrics** page, the files must have a `.yml`
extension and should be present in the project's **default** branch.
**Add a `.yml` file manually**
For example:
1. Create a YAML file with the `.yml` extension under your repository's root
directory inside `.gitlab/dashboards/`. For example, create
`.gitlab/dashboards/prom_alerts.yml` with the following contents:
1. Create `.gitlab/dashboards/prom_alerts.yml` under your repository's root
directory with the following contents:
```yaml
dashboard: 'Dashboard Title'
......@@ -194,7 +197,7 @@ NOTE: **Note:**
Configuration files nested under subdirectories of `.gitlab/dashboards` are not
supported and will not be available in the UI.
**Duplicate a GitLab-defined dashboard as a new `.yml` file**
#### Duplicating a GitLab-defined dashboard
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/37238) in GitLab 12.7.
......
......@@ -54,8 +54,8 @@ Design Management requires that projects are using
- Design Management data [won't be moved](https://gitlab.com/gitlab-org/gitlab/issues/13426)
when an issue is moved, nor [deleted](https://gitlab.com/gitlab-org/gitlab/issues/13427)
when an issue is deleted.
- Design Management
[isn't supported by Geo](https://gitlab.com/groups/gitlab-org/-/epics/1633) yet.
- From GitLab 12.7, Design Management data [can be replicated](../../../administration/geo/replication/datatypes.md#limitations-on-replicationverification)
by Geo but [not verified](https://gitlab.com/gitlab-org/gitlab/issues/32467).
- Only the latest version of the designs can be deleted.
- Deleted designs cannot be recovered but you can see them on previous designs versions.
......
......@@ -55,6 +55,7 @@ This page has:
- A link to the Sentry issue.
- A link to the GitLab commit if the Sentry [release id/version](https://docs.sentry.io/workflow/releases/?platform=javascript#configure-sdk) on the Sentry Issue's first release matches a commit SHA in your GitLab hosted project.
- Other details about the issue, including a full stack trace.
- In [GitLab 12.7 and newer](https://gitlab.com/gitlab-org/gitlab/issues/36246), language and urgency are displayed.
By default, a **Create issue** button is displayed. Once you have used it to create an issue, the button is hidden.
......@@ -78,6 +79,10 @@ Ignoring an error will prevent it from appearing in the [Error Tracking List](#e
### Resolving errors
From within the [Error Details](#error-details) page you can resolve a Sentry error by simply clicking the **Resolve** button near the top of the page.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/39825) in GitLab 12.7.
Marking an error as resolved indicates that the error has stopped firing events. If another event occurs, the error reverts to unresolved.
From within the [Error Details](#error-details) page you can resolve a Sentry error by
clicking the **Resolve** button near the top of the page.
Marking an error as resolved indicates that the error has stopped firing events. If another event
occurs, the error reverts to unresolved.
......@@ -23,10 +23,14 @@ noted information:
If you hover over a commit in the UI, you'll see a precise date and time
for that commit.
![Blame previous commit](img/file_blame_previous_commit_v12_7.png "Blame previous commit")
## Blame previous commit
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/19299) in GitLab 12.7.
To see earlier revisions of a specific line, click **View blame prior to this change**
until you've found the changes you're interested in viewing.
until you've found the changes you're interested in viewing:
![Blame previous commit](img/file_blame_previous_commit_v12_7.png "Blame previous commit")
## Associated `git` command
......
......@@ -69,7 +69,7 @@ Follow these steps to do so:
have access to your GitLab instance. We recommend **putting this behind an alias** so it can be
changed if needed, and **[enabling Akismet](../../integration/akismet.md)** on your GitLab
instance to add spam checking to this service. Unblocked email spam would result in many spam
issues being created, and may disrupt your GitLab service.
issues being created.
If you have [templates](description_templates.md) in your repository, you can optionally select
one from the selector menu to append it to all Service Desk issues.
......
......@@ -34,7 +34,7 @@ module Gitlab
end
def password
@password ||= Gitlab::Utils.force_utf8(Devise.friendly_token[0, 8].downcase)
@password ||= Gitlab::Utils.force_utf8(::User.random_password.downcase)
end
def location
......
......@@ -172,6 +172,7 @@ module Gitlab
stage: 'import_pull_requests', iid: pull_request.iid, error: e.message
)
backtrace = Gitlab::BacktraceCleaner.clean_backtrace(e.backtrace)
errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
end
end
......
......@@ -24,6 +24,10 @@ module Gitlab
'projects/compare' => %w{create}
}.freeze
WHITELISTED_LOGOUT_ROUTES = {
'sessions' => %w{destroy}
}.freeze
GRAPHQL_URL = '/api/graphql'
def initialize(app, env)
......@@ -85,7 +89,7 @@ module Gitlab
# Overridden in EE module
def whitelisted_routes
grack_route? || internal_route? || lfs_route? || compare_git_revisions_route? || sidekiq_route? || graphql_query?
grack_route? || internal_route? || lfs_route? || compare_git_revisions_route? || sidekiq_route? || logout_route? || graphql_query?
end
def grack_route?
......@@ -118,6 +122,13 @@ module Gitlab
WHITELISTED_GIT_LFS_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
end
def logout_route?
# Calling route_hash may be expensive. Only do it if we think there's a possible match
return false unless request.post? && request.path.end_with?('/users/sign_out')
WHITELISTED_LOGOUT_ROUTES[route_hash[:controller]]&.include?(route_hash[:action])
end
def sidekiq_route?
request.path.start_with?("#{relative_url}/admin/sidekiq")
end
......
......@@ -6295,15 +6295,9 @@ msgstr ""
msgid "Deselect all"
msgstr ""
msgid "Design Management"
msgstr ""
msgid "Design Management files and data"
msgstr ""
msgid "Design Sync Not Enabled"
msgstr ""
msgid "DesignManagement|%{current_design} of %{designs_count}"
msgstr ""
......@@ -8065,6 +8059,11 @@ msgstr ""
msgid "Fetching licenses failed. You are not permitted to perform this action."
msgstr ""
msgid "File"
msgid_plural "Files"
msgstr[0] ""
msgstr[1] ""
msgid "File Hooks"
msgstr ""
......@@ -8407,9 +8406,6 @@ msgstr ""
msgid "Geo Settings"
msgstr ""
msgid "Geo Troubleshooting"
msgstr ""
msgid "Geo allows you to replicate your GitLab instance to other geographical locations."
msgstr ""
......@@ -9879,9 +9875,6 @@ msgstr ""
msgid "If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
msgstr ""
msgid "If you believe this page to be an error, check out the links below for more information."
msgstr ""
msgid "If you lose your recovery codes you can generate new ones, invalidating all previous codes."
msgstr ""
......@@ -11639,10 +11632,10 @@ msgstr ""
msgid "MergeRequest|Error loading full diff. Please try again."
msgstr ""
msgid "MergeRequest|No files found"
msgid "MergeRequest|Filter files or search with %{modifier_key}+p"
msgstr ""
msgid "MergeRequest|Search files (%{modifier_key}P)"
msgid "MergeRequest|No files found"
msgstr ""
msgid "Merged"
......@@ -21719,9 +21712,6 @@ msgstr ""
msgid "among other things"
msgstr ""
msgid "and"
msgstr ""
msgid "archived"
msgstr ""
......@@ -22149,11 +22139,6 @@ msgstr ""
msgid "failed to dismiss associated finding(id=%{finding_id}): %{message}"
msgstr ""
msgid "file"
msgid_plural "files"
msgstr[0] ""
msgstr[1] ""
msgid "finding is not found or is already attached to a vulnerability"
msgstr ""
......@@ -22738,6 +22723,9 @@ msgstr[1] ""
msgid "reset it."
msgstr ""
msgid "resolved the corresponding error and closed the issue."
msgstr ""
msgid "score"
msgstr ""
......
......@@ -301,7 +301,7 @@ describe Projects::ErrorTrackingController do
context 'update result is successful' do
before do
expect(issue_update_service).to receive(:execute)
.and_return(status: :success, updated: true)
.and_return(status: :success, updated: true, closed_issue_iid: 1234)
update_issue
end
......
......@@ -50,7 +50,7 @@ describe 'Merge request > User sees versions', :js do
expect(page).to have_content 'latest version'
end
expect(page).to have_content '8 files'
expect(page).to have_content '8 Files'
end
it_behaves_like 'allows commenting',
......@@ -84,7 +84,7 @@ describe 'Merge request > User sees versions', :js do
end
it 'shows comments that were last relevant at that version' do
expect(page).to have_content '5 files'
expect(page).to have_content '5 Files'
position = Gitlab::Diff::Position.new(
old_path: ".gitmodules",
......@@ -128,10 +128,12 @@ describe 'Merge request > User sees versions', :js do
diff_id: merge_request_diff3.id,
start_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
)
expect(page).to have_content '4 files'
expect(page).to have_content '4 Files'
additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text
deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text
additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition')
.ancestor('.diff-stats-group').text
deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion')
.ancestor('.diff-stats-group').text
expect(additions_content).to eq '15'
expect(deletions_content).to eq '6'
......@@ -154,10 +156,12 @@ describe 'Merge request > User sees versions', :js do
end
it 'show diff between new and old version' do
additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-addition-line').text
deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group .js-file-deletion-line').text
additions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-addition')
.ancestor('.diff-stats-group').text
deletions_content = page.find('.diff-stats.is-compare-versions-header .diff-stats-group svg.ic-file-deletion')
.ancestor('.diff-stats-group').text
expect(page).to have_content '4 files'
expect(page).to have_content '4 Files'
expect(additions_content).to eq '15'
expect(deletions_content).to eq '6'
end
......@@ -167,7 +171,7 @@ describe 'Merge request > User sees versions', :js do
page.within '.mr-version-dropdown' do
expect(page).to have_content 'latest version'
end
expect(page).to have_content '8 files'
expect(page).to have_content '8 Files'
end
it_behaves_like 'allows commenting',
......@@ -193,7 +197,7 @@ describe 'Merge request > User sees versions', :js do
find('.btn-default').click
click_link 'version 1'
end
expect(page).to have_content '0 files'
expect(page).to have_content '0 Files'
end
end
......@@ -219,7 +223,7 @@ describe 'Merge request > User sees versions', :js do
expect(page).to have_content 'version 1'
end
expect(page).to have_content '0 files'
expect(page).to have_content '0 Files'
end
end
......
......@@ -21,4 +21,16 @@ describe 'Logout/Sign out', :js do
expect(page).not_to have_selector('.flash-notice')
end
context 'on a read-only instance' do
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
it 'sign out redirects to sign in page' do
gitlab_sign_out
expect(current_path).to eq new_user_session_path
end
end
end
......@@ -6,9 +6,15 @@
"properties" : {
"result": {
"type": "object",
"required" : [
"status",
"updated",
"closed_issue_iid"
],
"properties": {
"status": { "type": "string" },
"updated": { "type": "boolean" }
"updated": { "type": "boolean" },
"closed_issue_iid": { "type": ["integer", "null"] }
}
}
},
......
......@@ -49,7 +49,8 @@ describe('CompareVersions', () => {
expect(treeListBtn.exists()).toBe(true);
expect(treeListBtn.attributes('title')).toBe('Hide file browser');
expect(treeListBtn.find(Icon).props('name')).toBe('file-tree');
expect(treeListBtn.findAll(Icon).length).not.toBe(0);
expect(treeListBtn.find(Icon).props('name')).toBe('collapse-left');
});
it('should render comparison dropdowns with correct values', () => {
......
import { shallowMount } from '@vue/test-utils';
import Icon from '~/vue_shared/components/icon.vue';
import DiffStats from '~/diffs/components/diff_stats.vue';
describe('diff_stats', () => {
......@@ -23,11 +24,18 @@ describe('diff_stats', () => {
},
});
const findFileLine = name => wrapper.find(name);
const additions = findFileLine('.js-file-addition-line');
const deletions = findFileLine('.js-file-deletion-line');
const findIcon = name =>
wrapper
.findAll(Icon)
.filter(c => c.attributes('name') === name)
.at(0).element.parentNode;
expect(additions.text()).toBe('100');
expect(deletions.text()).toBe('200');
const additions = findIcon('file-addition');
const deletions = findIcon('file-deletion');
const filesChanged = findIcon('doc-code');
expect(additions.textContent).toContain('100');
expect(deletions.textContent).toContain('200');
expect(filesChanged.textContent).toContain('300');
});
});
......@@ -56,10 +56,10 @@ describe('Changed file icon', () => {
describe.each`
file | iconName | tooltipText | desc
${changedFile()} | ${'file-modified-solid'} | ${'Unstaged modification'} | ${'with file changed'}
${changedFile()} | ${'file-modified'} | ${'Unstaged modification'} | ${'with file changed'}
${stagedFile()} | ${'file-modified-solid'} | ${'Staged modification'} | ${'with file staged'}
${changedAndStagedFile()} | ${'file-modified-solid'} | ${'Unstaged and staged modification'} | ${'with file changed and staged'}
${newFile()} | ${'file-addition-solid'} | ${'Unstaged addition'} | ${'with file new'}
${changedAndStagedFile()} | ${'file-modified'} | ${'Unstaged and staged modification'} | ${'with file changed and staged'}
${newFile()} | ${'file-addition'} | ${'Unstaged addition'} | ${'with file new'}
`('$desc', ({ file, iconName, tooltipText }) => {
beforeEach(() => {
factory({ file });
......
......@@ -93,13 +93,13 @@ describe('RepoTab', () => {
Vue.nextTick()
.then(() => {
expect(vm.$el.querySelector('.file-modified-solid')).toBeNull();
expect(vm.$el.querySelector('.file-modified')).toBeNull();
vm.$el.dispatchEvent(new Event('mouseout'));
})
.then(Vue.nextTick)
.then(() => {
expect(vm.$el.querySelector('.file-modified-solid')).not.toBeNull();
expect(vm.$el.querySelector('.file-modified')).not.toBeNull();
done();
})
......
......@@ -139,6 +139,18 @@ describe Gitlab::Auth::LDAP::User do
expect(gl_user).to be_confirmed
end
end
context 'when the current minimum password length is different from the default minimum password length' do
before do
stub_application_setting minimum_password_length: 21
end
it 'creates the user' do
ldap_user.save
expect(gl_user).to be_persisted
end
end
end
describe 'updating email' do
......
......@@ -86,6 +86,20 @@ describe Gitlab::Auth::OAuth::User do
end
end
context 'when the current minimum password length is different from the default minimum password length' do
before do
stub_application_setting minimum_password_length: 21
end
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: [provider])
oauth_user.save
expect(gl_user).to be_persisted
end
end
it 'marks user as having password_automatically_set' do
stub_omniauth_config(allow_single_sign_on: [provider], external_providers: [provider])
......
......@@ -325,6 +325,18 @@ describe Gitlab::Auth::Saml::User do
expect(gl_user).to be_confirmed
end
end
context 'when the current minimum password length is different from the default minimum password length' do
before do
stub_application_setting minimum_password_length: 21
end
it 'creates the user' do
saml_user.save
expect(gl_user).to be_persisted
end
end
end
describe 'blocking' do
......
......@@ -67,6 +67,7 @@ describe Gitlab::BitbucketServerImport::Importer do
author_email: project.owner.email,
created_at: Time.now,
updated_at: Time.now,
raw: {},
merged?: true)
allow(subject.client).to receive(:pull_requests).and_return([pull_request])
......@@ -239,6 +240,13 @@ describe Gitlab::BitbucketServerImport::Importer do
expect(notes.first.note).to start_with('*Comment on .gitmodules')
expect(notes.second.note).to start_with('*Comment on .gitmodules')
end
it 'reports an error if an exception is raised' do
allow(subject).to receive(:import_bitbucket_pull_request).and_raise(RuntimeError)
expect(Gitlab::ErrorTracking).to receive(:log_exception)
subject.execute
end
end
describe 'inaccessible branches' do
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200123155929_remove_invalid_jira_data.rb')
describe RemoveInvalidJiraData, :migration do
let(:jira_tracker_data) { table(:jira_tracker_data) }
let(:services) { table(:services) }
let(:service) { services.create(id: 1) }
let(:data) do
{
service_id: service.id,
encrypted_api_url: 'http:url.com',
encrypted_api_url_iv: 'somevalue',
encrypted_url: 'http:url.com',
encrypted_url_iv: 'somevalue',
encrypted_username: 'username',
encrypted_username_iv: 'somevalue',
encrypted_password: 'username',
encrypted_password_iv: 'somevalue'
}
end
let!(:valid_data) { jira_tracker_data.create(data) }
let!(:empty_data) { jira_tracker_data.create(service_id: service.id) }
let!(:invalid_api_url) do
data[:encrypted_api_url_iv] = nil
jira_tracker_data.create(data)
end
let!(:missing_api_url) do
data[:encrypted_api_url] = ''
data[:encrypted_api_url_iv] = nil
jira_tracker_data.create(data)
end
let!(:invalid_url) do
data[:encrypted_url_iv] = nil
jira_tracker_data.create(data)
end
let!(:missing_url) do
data[:encrypted_url] = ''
jira_tracker_data.create(data)
end
let!(:invalid_username) do
data[:encrypted_username_iv] = nil
jira_tracker_data.create(data)
end
let!(:missing_username) do
data[:encrypted_username] = nil
data[:encrypted_username_iv] = nil
jira_tracker_data.create(data)
end
let!(:invalid_password) do
data[:encrypted_password_iv] = nil
jira_tracker_data.create(data)
end
let!(:missing_password) do
data[:encrypted_password] = nil
data[:encrypted_username_iv] = nil
jira_tracker_data.create(data)
end
it 'removes the invalid data' do
valid_data_records = [valid_data, empty_data, missing_api_url, missing_url, missing_username, missing_password]
expect { migrate! }.to change { jira_tracker_data.count }.from(10).to(6)
expect(jira_tracker_data.all).to match_array(valid_data_records)
end
end
......@@ -508,6 +508,20 @@ describe User, :do_not_mock_admin_mode do
end
end
describe '.random_password' do
let(:random_password) { described_class.random_password }
before do
expect(User).to receive(:password_length).and_return(88..128)
end
context 'length' do
it 'conforms to the current password length settings' do
expect(random_password.length).to eq(128)
end
end
end
describe '.password_length' do
let(:password_length) { described_class.password_length }
......
......@@ -3,24 +3,7 @@
require 'spec_helper'
describe ErrorTracking::IssueDetailsService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
let(:result) { subject.execute }
let(:error_tracking_setting) do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end
subject { described_class.new(project, user) }
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
project.add_reporter(user)
end
include_context 'sentry error tracking context'
describe '#execute' do
context 'with authorized user' do
......
......@@ -3,24 +3,7 @@
require 'spec_helper'
describe ErrorTracking::IssueLatestEventService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
let(:result) { subject.execute }
let(:error_tracking_setting) do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end
subject { described_class.new(project, user) }
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
project.add_reporter(user)
end
include_context 'sentry error tracking context'
describe '#execute' do
context 'with authorized user' do
......
# frozen_string_literal: true
require 'spec_helper'
describe ErrorTracking::IssueUpdateService do
include_context 'sentry error tracking context'
let(:arguments) { { issue_id: 1234, status: 'resolved' } }
subject { described_class.new(project, user, arguments) }
shared_examples 'does not perform close issue flow' do
it 'does not call the close issue service' do
expect(issue_close_service)
.not_to receive(:execute)
end
it 'does not create system note' do
expect(SystemNoteService).not_to receive(:close_after_error_tracking_resolve)
end
end
describe '#execute' do
context 'with authorized user' do
context 'when update_issue returns success' do
let(:update_issue_response) { { updated: true } }
before do
expect(error_tracking_setting)
.to receive(:update_issue).and_return(update_issue_response)
end
it 'returns the response' do
expect(result).to eq(update_issue_response.merge(status: :success, closed_issue_iid: nil))
end
it 'updates any related issue' do
expect(subject).to receive(:update_related_issue)
result
end
context 'related issue and resolving' do
let(:issue) { create(:issue, project: project) }
let(:sentry_issue) { create(:sentry_issue, issue: issue) }
let(:arguments) { { issue_id: sentry_issue.sentry_issue_identifier, status: 'resolved' } }
let(:issue_close_service) { spy(:issue_close_service) }
before do
allow_any_instance_of(SentryIssueFinder)
.to receive(:execute)
.and_return(sentry_issue)
allow(Issues::CloseService)
.to receive(:new)
.and_return(issue_close_service)
end
after do
result
end
it 'closes the issue' do
expect(issue_close_service)
.to receive(:execute)
.with(issue, system_note: false)
.and_return(issue)
end
it 'creates a system note' do
expect(SystemNoteService).to receive(:close_after_error_tracking_resolve)
end
it 'returns a response with closed issue' do
closed_issue = create(:issue, :closed, project: project)
expect(issue_close_service)
.to receive(:execute)
.with(issue, system_note: false)
.and_return(closed_issue)
expect(result).to eq(status: :success, updated: true, closed_issue_iid: closed_issue.iid)
end
context 'issue is already closed' do
let(:issue) { create(:issue, :closed, project: project) }
include_examples 'does not perform close issue flow'
end
context 'status is not resolving' do
let(:arguments) { { issue_id: sentry_issue.sentry_issue_identifier, status: 'ignored' } }
include_examples 'does not perform close issue flow'
end
end
end
include_examples 'error tracking service sentry error handling', :update_issue
end
include_examples 'error tracking service unauthorized user'
include_examples 'error tracking service disabled'
end
end
......@@ -3,8 +3,8 @@
require 'spec_helper'
describe ErrorTracking::ListIssuesService do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
include_context 'sentry error tracking context'
let(:params) { { search_term: 'something', sort: 'last_seen', cursor: 'some-cursor' } }
let(:list_sentry_issues_args) do
{
......@@ -16,22 +16,8 @@ describe ErrorTracking::ListIssuesService do
}
end
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
let(:result) { subject.execute }
let(:error_tracking_setting) do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end
subject { described_class.new(project, user, params) }
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
project.add_reporter(user)
end
describe '#execute' do
context 'with authorized user' do
context 'when list_sentry_issues returns issues' do
......
......@@ -45,34 +45,12 @@ describe PagesDomains::CreateAcmeOrderService do
expect { OpenSSL::PKey::RSA.new(saved_order.private_key) }.not_to raise_error
end
it 'properly saves order url' do
it 'properly saves order attributes' do
service.execute
saved_order = PagesDomainAcmeOrder.last
expect(saved_order.url).to eq(order_double.url)
end
context 'when order expires in 2 days' do
it 'sets expiration time in 2 hours' do
Timecop.freeze do
service.execute
saved_order = PagesDomainAcmeOrder.last
expect(saved_order.expires_at).to be_like_time(2.hours.from_now)
end
end
end
context 'when order expires in an hour' do
it 'sets expiration time accordingly to order' do
Timecop.freeze do
allow(order_double).to receive(:expires).and_return(1.hour.from_now)
service.execute
saved_order = PagesDomainAcmeOrder.last
expect(saved_order.expires_at).to be_like_time(1.hour.from_now)
end
end
expect(saved_order.expires_at).to be_like_time(order_double.expires)
end
it 'properly saves challenge attributes' do
......
# frozen_string_literal: true
shared_context 'sentry error tracking context' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
let(:token) { 'test-token' }
let(:result) { subject.execute }
subject { described_class.new(project, user) }
let(:error_tracking_setting) do
create(:project_error_tracking_setting, api_url: sentry_url, token: token, project: project)
end
before do
expect(project).to receive(:error_tracking_setting).at_least(:once).and_return(error_tracking_setting)
project.add_reporter(user)
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册