Commit 87af6f2e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent c43ba267
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{js,json,vue,scss,rb,haml,yml}]
indent_size = 2
[*.{js,json,vue,scss,rb,haml,yml,md}]
indent_style = space
charset = utf-8
......@@ -56,6 +56,7 @@ Dangerfile @gl-quality/eng-prod
/lib/gitlab/danger/ @gl-quality/eng-prod
/scripts/ @gl-quality/eng-prod
/scripts/frontend/ @gl-quality/eng-prod @gitlab-org/maintainers/frontend
.editorconfig @gl-quality/eng-prod
# Telemetry owner files
/ee/lib/gitlab/usage_data_counters/ @gitlab-org/growth/telemetry
......
......@@ -4,6 +4,10 @@
Set the title to: `Security Release: 12.2.X, 12.1.X, and 12.0.X`
-->
:warning: **Only Release Managers and members of the AppSec team can edit the description of this issue**
-------
## Releases tasks
- https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/release-manager.md
......@@ -24,22 +28,6 @@ in the "Linked issues" section below this issue description.
tracking issue, their merge requests may not be included in the security
release.
## Issues in Omnibus-GitLab
Omnibus security fixes need to be added manually to this issue description
using and below the following template:
```markdown
* {https://gitlab.com/gitlab-org/security/gitlab/issues/ link}
| Version | MR |
|---------|----|
| 12.2 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| 12.1 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| 12.0 | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
| master | {https://dev.gitlab.org/gitlab/omnibus-gitlab/merge_requests/ link} |
```
## QA
{QA issue link}
......
Please view this file on the master branch, on stable branches it's out of date.
## 12.8.7 (2020-03-16)
### Fixed (1 change)
- Allow multipart uploads for packages. !26387
## 12.8.6 (2020-03-11)
- No changes.
......
......@@ -4,7 +4,10 @@ entry.
## 12.8.7 (2020-03-16)
- No changes.
### Fixed (1 change, 1 of them is from the community)
- Fix crl_url parsing and certificate visualization. !25876 (Roger Meier)
## 12.8.6 (2020-03-11)
......
<script>
import { createNamespacedHelpers, mapState, mapActions } from 'vuex';
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale';
import ClusterFormDropdown from '~/create_cluster/components/cluster_form_dropdown.vue';
......@@ -133,7 +133,7 @@ export default {
: s__('ClusterIntegration|Create Kubernetes cluster');
},
kubernetesIntegrationHelpText() {
const escapedUrl = _.escape(this.kubernetesIntegrationHelpPath);
const escapedUrl = esc(this.kubernetesIntegrationHelpPath);
return sprintf(
s__(
......@@ -245,7 +245,7 @@ export default {
);
},
gitlabManagedHelpText() {
const escapedUrl = _.escape(this.gitlabManagedClusterHelpPath);
const escapedUrl = esc(this.gitlabManagedClusterHelpPath);
return sprintf(
s__(
......
<script>
import { GlFormInput } from '@gitlab/ui';
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { mapState, mapActions } from 'vuex';
import { sprintf, s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
......@@ -42,7 +42,7 @@ export default {
: s__('ClusterIntegration|Authenticate with AWS');
},
accountAndExternalIdsHelpText() {
const escapedUrl = _.escape(this.accountAndExternalIdsHelpPath);
const escapedUrl = esc(this.accountAndExternalIdsHelpPath);
return sprintf(
s__(
......@@ -59,7 +59,7 @@ export default {
);
},
provisionRoleArnHelpText() {
const escapedUrl = _.escape(this.createRoleArnHelpPath);
const escapedUrl = esc(this.createRoleArnHelpPath);
return sprintf(
s__(
......
import _ from 'underscore';
import { GlLoadingIcon } from '@gitlab/ui';
import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
......@@ -49,7 +48,7 @@ export default {
methods: {
fetchSuccessHandler() {
if (this.defaultValue) {
const itemToSelect = _.find(this.items, item => item.name === this.defaultValue);
const itemToSelect = this.items.find(item => item.name === this.defaultValue);
if (itemToSelect) {
this.setItem(itemToSelect.name);
......
<script>
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { mapState, mapGetters, mapActions } from 'vuex';
import { s__, sprintf } from '~/locale';
......@@ -65,7 +65,7 @@ export default {
s__(message),
{
docsLinkEnd: '&nbsp;<i class="fa fa-external-link" aria-hidden="true"></i></a>',
docsLinkStart: `<a href="${_.escape(
docsLinkStart: `<a href="${esc(
this.docsUrl,
)}" target="_blank" rel="noopener noreferrer">`,
},
......@@ -119,7 +119,7 @@ export default {
...mapActions({ setItem: 'setProject' }),
fetchSuccessHandler() {
if (this.defaultValue) {
const projectToSelect = _.find(this.items, item => item.projectId === this.defaultValue);
const projectToSelect = this.items.find(item => item.projectId === this.defaultValue);
if (projectToSelect) {
this.setItem(projectToSelect);
......
......@@ -38,6 +38,8 @@ module SubmoduleHelper
url_helpers.namespace_project_tree_path(namespace, project, submodule_item_id)]
elsif relative_self_url?(url)
relative_self_links(url, submodule_item_id, repository.project)
elsif gist_github_dot_com_url?(url)
gist_github_com_tree_links(namespace, project, submodule_item_id)
elsif github_dot_com_url?(url)
github_com_tree_links(namespace, project, submodule_item_id)
elsif gitlab_dot_com_url?(url)
......@@ -52,6 +54,10 @@ module SubmoduleHelper
protected
def gist_github_dot_com_url?(url)
url =~ %r{gist\.github\.com[/:][^/]+/[^/]+\Z}
end
def github_dot_com_url?(url)
url =~ %r{github\.com[/:][^/]+/[^/]+\Z}
end
......@@ -78,6 +84,11 @@ module SubmoduleHelper
[base, [base, '/-/tree/', commit].join('')]
end
def gist_github_com_tree_links(namespace, project, commit)
base = ['https://gist.github.com/', namespace, '/', project].join('')
[base, [base, commit].join('/')]
end
def github_com_tree_links(namespace, project, commit)
base = ['https://github.com/', namespace, '/', project].join('')
[base, [base, '/tree/', commit].join('')]
......
......@@ -44,7 +44,7 @@ class Ability
# Returns an Array of MergeRequests that can be read by the given user.
#
# merge_requests - MRs out of which to collect mr's readable by the user.
# merge_requests - MRs out of which to collect MRs readable by the user.
# user - The User for which to check the merge_requests
# filters - A hash of abilities and filters to apply if the user lacks this
# ability
......
......@@ -11,8 +11,6 @@ module Commits
private
def track_mr_picking(pick_sha)
return unless Feature.enabled?(:track_mr_picking, project)
merge_request = project.merge_requests.by_merge_commit_sha(@commit.sha).first
return unless merge_request
......
......@@ -38,8 +38,6 @@ module Deployments
.commits_between(from, to)
.map(&:id)
track_mr_picking = Feature.enabled?(:track_mr_picking, project)
# For some projects the list of commits to deploy may be very large. To
# ensure we do not end up running SQL queries with thousands of WHERE IN
# values, we run one query per a certain number of commits.
......@@ -53,8 +51,6 @@ module Deployments
deployment.link_merge_requests(merge_requests)
next unless track_mr_picking
picked_merge_requests =
project.merge_requests.by_cherry_pick_sha(slice)
......
......@@ -143,7 +143,7 @@ module SystemNotes
def picked_into_branch(branch_name, pick_commit)
link = url_helpers.project_tree_path(project, branch_name)
body = "picked this merge request into branch [`#{branch_name}`](#{link}) with commit #{pick_commit}"
body = "picked the changes into the branch [`#{branch_name}`](#{link}) with commit #{pick_commit}"
summary = NoteSummary.new(noteable, project, author, body, action: 'cherry_pick')
summary.note[:commit_id] = pick_commit
......
- if show_no_ssh_key_message?
.no-ssh-key-message.alert.alert-warning
- add_ssh_key_link = link_to s_('MissingSSHKeyWarningLink|add an SSH key'), profile_keys_path, class: 'alert-link'
- ssh_message = _("You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile") % { add_ssh_key_link: add_ssh_key_link }
= ssh_message.html_safe
.alert-link-group
= link_to _("Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'alert-link'
|
= link_to _('Remind later'), '#', class: 'hide-no-ssh-message alert-link'
%div{ class: 'no-ssh-key-message gl-alert gl-alert-warning', role: 'alert' }
= sprite_icon('warning', size: 16, css_class: 'gl-icon s16 gl-alert-icon gl-alert-icon-no-title')
%button{ class: 'gl-alert-dismiss hide-no-ssh-message', type: 'button', 'aria-label': 'Dismiss' }
= sprite_icon('close', size: 16, css_class: 'gl-icon s16')
.gl-alert-body
= s_("MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile").html_safe
.gl-alert-actions
= link_to s_('MissingSSHKeyWarningLink|Add SSH key'), profile_keys_path, class: "btn gl-alert-action btn-warning btn-md new-gl-button"
= link_to s_("MissingSSHKeyWarningLink|Don't show again"), profile_path(user: {hide_no_ssh_key: true}), method: :put, role: 'button', class: 'btn gl-alert-action btn-md btn-warning btn-secondary new-gl-button'
---
title: Fix Conan package download_urls and snapshot to return files based on requested
conan_package_reference
merge_request: 27250
author:
type: fixed
---
title: Optimize clusters counters query performance in usage data
merge_request: 26887
author:
type: performance
---
title: Fix submodule links to gist.github.com
merge_request: 27346
author:
type: fixed
---
title: Track merge request cherry-picks
merge_request: 26907
author:
type: added
---
title: Fixed SSH warning style
merge_request: 26992
author:
type: other
---
title: Fix crl_url parsing and certificate visualization
merge_request: 25876
author: Roger Meier
type: fixed
---
title: Swap to UNLINK for Redis set cache
merge_request: 27116
author:
type: performance
# frozen_string_literal: true
class AddIndexOnEnabledAndProviderTypeAndIdToClusters < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :clusters, [:enabled, :provider_type, :id]
remove_concurrent_index :clusters, :enabled
end
def down
add_concurrent_index :clusters, :enabled
remove_concurrent_index :clusters, [:enabled, :provider_type, :id]
end
end
......@@ -1125,7 +1125,7 @@ ActiveRecord::Schema.define(version: 2020_03_12_163407) do
t.integer "management_project_id"
t.integer "cleanup_status", limit: 2, default: 1, null: false
t.text "cleanup_status_reason"
t.index ["enabled"], name: "index_clusters_on_enabled"
t.index ["enabled", "provider_type", "id"], name: "index_clusters_on_enabled_and_provider_type_and_id"
t.index ["management_project_id"], name: "index_clusters_on_management_project_id", where: "(management_project_id IS NOT NULL)"
t.index ["user_id"], name: "index_clusters_on_user_id"
end
......
......@@ -252,7 +252,7 @@ on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Too
GitLab checks files to detect LFS pointers on push. If LFS pointers are detected, GitLab tries to verify that those files already exist in LFS on GitLab.
Verify that LFS in installed locally and consider a manual push with `git lfs push --all`.
Verify that LFS is installed locally and consider a manual push with `git lfs push --all`.
If you are storing LFS files outside of GitLab you can disable LFS on the project by setting `lfs_enabled: false` with the [projects API](../../api/projects.md#edit-project).
......
......@@ -3934,6 +3934,11 @@ enum IssueState {
opened
}
"""
Represents untyped JSON
"""
scalar JSON
type Label {
"""
Background color of the label
......@@ -6060,6 +6065,31 @@ type Project {
"""
visibility: String
"""
Vulnerabilities reported on the project. Available only when feature flag `first_class_vulnerabilities` is enabled.
"""
vulnerabilities(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): VulnerabilityConnection
"""
Web URL of the project
"""
......@@ -8421,4 +8451,117 @@ enum VisibilityScopesEnum {
internal
private
public
}
"""
Represents a vulnerability.
"""
type Vulnerability {
"""
Description of the vulnerability
"""
description: String
"""
GraphQL ID of the vulnerability
"""
id: ID!
"""
The JSON location metadata for the vulnerability. Its format depends on the
type of the security scan that found the vulnerability
"""
location: JSON
"""
Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST)
"""
reportType: VulnerabilityReportType
"""
Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL)
"""
severity: VulnerabilitySeverity
"""
State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED)
"""
state: VulnerabilityState
"""
Title of the vulnerability
"""
title: String
"""
URL to the vulnerability's details page
"""
vulnerabilityPath: String
}
"""
The connection type for Vulnerability.
"""
type VulnerabilityConnection {
"""
A list of edges.
"""
edges: [VulnerabilityEdge]
"""
A list of nodes.
"""
nodes: [Vulnerability]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type VulnerabilityEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: Vulnerability
}
"""
The type of the security scan that found the vulnerability.
"""
enum VulnerabilityReportType {
CONTAINER_SCANNING
DAST
DEPENDENCY_SCANNING
SAST
}
"""
The severity of the vulnerability.
"""
enum VulnerabilitySeverity {
CRITICAL
HIGH
INFO
LOW
MEDIUM
UNKNOWN
}
"""
The state of the vulnerability.
"""
enum VulnerabilityState {
CONFIRMED
DETECTED
DISMISSED
RESOLVED
}
\ No newline at end of file
......@@ -11224,6 +11224,16 @@
],
"possibleTypes": null
},
{
"kind": "SCALAR",
"name": "JSON",
"description": "Represents untyped JSON",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Label",
......@@ -18163,6 +18173,59 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "vulnerabilities",
"description": "Vulnerabilities reported on the project. Available only when feature flag `first_class_vulnerabilities` is enabled.",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "VulnerabilityConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "webUrl",
"description": "Web URL of the project",
......@@ -25498,6 +25561,364 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Vulnerability",
"description": "Represents a vulnerability.",
"fields": [
{
"name": "description",
"description": "Description of the vulnerability",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "GraphQL ID of the vulnerability",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "location",
"description": "The JSON location metadata for the vulnerability. Its format depends on the type of the security scan that found the vulnerability",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "JSON",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "reportType",
"description": "Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST)",
"args": [
],
"type": {
"kind": "ENUM",
"name": "VulnerabilityReportType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "severity",
"description": "Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL)",
"args": [
],
"type": {
"kind": "ENUM",
"name": "VulnerabilitySeverity",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "state",
"description": "State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED)",
"args": [
],
"type": {
"kind": "ENUM",
"name": "VulnerabilityState",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "title",
"description": "Title of the vulnerability",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "vulnerabilityPath",
"description": "URL to the vulnerability's details page",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityConnection",
"description": "The connection type for Vulnerability.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "VulnerabilityEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "Vulnerability",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "VulnerabilityEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "Vulnerability",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "VulnerabilityReportType",
"description": "The type of the security scan that found the vulnerability.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "SAST",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DEPENDENCY_SCANNING",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CONTAINER_SCANNING",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DAST",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "VulnerabilitySeverity",
"description": "The severity of the vulnerability.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "INFO",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "UNKNOWN",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "LOW",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "MEDIUM",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "HIGH",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CRITICAL",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "VulnerabilityState",
"description": "The state of the vulnerability.",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "DETECTED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "DISMISSED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "RESOLVED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "CONFIRMED",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "__Directive",
......
......@@ -1354,3 +1354,18 @@ Autogenerated return type of UpdateSnippet
| Name | Type | Description |
| --- | ---- | ---------- |
| `createSnippet` | Boolean! | Indicates the user can perform `create_snippet` on this resource |
## Vulnerability
Represents a vulnerability.
| Name | Type | Description |
| --- | ---- | ---------- |
| `description` | String | Description of the vulnerability |
| `id` | ID! | GraphQL ID of the vulnerability |
| `location` | JSON | The JSON location metadata for the vulnerability. Its format depends on the type of the security scan that found the vulnerability |
| `reportType` | VulnerabilityReportType | Type of the security report that found the vulnerability (SAST, DEPENDENCY_SCANNING, CONTAINER_SCANNING, DAST) |
| `severity` | VulnerabilitySeverity | Severity of the vulnerability (INFO, UNKNOWN, LOW, MEDIUM, HIGH, CRITICAL) |
| `state` | VulnerabilityState | State of the vulnerability (DETECTED, DISMISSED, RESOLVED, CONFIRMED) |
| `title` | String | Title of the vulnerability |
| `vulnerabilityPath` | String | URL to the vulnerability's details page |
# Style guides
## Editor/IDE styling standardization
We use [EditorConfig](https://editorconfig.org/) to automatically apply certain styling
standards before files are saved locally. Most editors/IDEs will honor the `.editorconfig`
settings automatically by default. If your editor/IDE does not automatically support `.editorconfig`,
we suggest investigating to see if a plugin exists. For instance here is the
[plugin for vim](https://github.com/editorconfig/editorconfig-vim).
## Pre-commit static analysis
You're strongly advised to install
......
......@@ -124,6 +124,8 @@ the following preparations into account.
- Follow the [guidelines on dropping columns](what_requires_downtime.md#dropping-columns).
- Generally it's best practice (but not a hard rule) to remove indexes and foreign keys in a post-deployment migration.
- Exceptions include removing indexes and foreign keys for small tables.
- If you're adding a composite index, another index might become redundant, so remove that in the same migration.
For example adding `index(column_A, column_B, column_C)` makes the indexes `index(column_A, column_B)` and `index(column_A)` redundant.
### How to review for database
......
......@@ -166,6 +166,10 @@ environment, you can use the [GitLab Development Kit (GDK)](https://gitlab.com/g
Please refer to the instructions in the [QA README](https://gitlab.com/gitlab-org/gitlab/tree/master/qa/README.md#how-can-i-use-it)
and the section below.
### Running tests that require special setup
Learn how to perform [tests that require special setup or consideration to run on your local environment](running_tests_that_require_special_setup.md).
## How do I write tests?
In order to write new tests, you first need to learn more about GitLab QA
......
# Running tests that require special setup
## Jenkins spec
The [`jenkins_build_status_spec`](https://gitlab.com/gitlab-org/gitlab/blob/163c8a8c814db26d11e104d1cb2dcf02eb567dbe/qa/qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb) spins up a Jenkins instance in a docker container based on an image stored in the [GitLab-QA container registry](https://gitlab.com/gitlab-org/gitlab-qa/container_registry).
The docker image it uses is preconfigured with some base data and plugins.
The test then configures the GitLab plugin in Jenkins with a URL of the GitLab instance that will be used
to run the tests. Unfortunately, the GitLab Jenkins plugin does not accept ports so `http://localhost:3000` would
not be accepted. Therefore, this requires us to run GitLab on port 80 or inside a docker container.
To start a docker container for GitLab based on the nightly image:
```shell
docker run \
--publish 80:80 \
--name gitlab \
--hostname localhost \
gitlab/gitlab-ee:nightly
```
To run the tests from the `/qa` directory:
```shell
CHROME_HEADLESS=false bin/qa Test::Instance::All http://localhost -- qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb
```
The test will automatically spinup a docker container for Jenkins and tear down once the test completes.
However, if you need to run Jenkins manually outside of the tests, use this command:
```shell
docker run \
--hostname localhost \
--name jenkins-server \
--env JENKINS_HOME=jenkins_home \
--publish 8080:8080 \
registry.gitlab.com/gitlab-org/gitlab-qa/jenkins-gitlab:version1
```
Jenkins will be available on `http://localhost:8080`.
Admin username is `admin` and password is `password`.
It is worth noting that this is not an orchestrated test. It is [tagged with the `:orchestrated` meta](https://gitlab.com/gitlab-org/gitlab/blob/163c8a8c814db26d11e104d1cb2dcf02eb567dbe/qa/qa/specs/features/ee/browser_ui/3_create/jenkins/jenkins_build_status_spec.rb#L5)
only to prevent it from running in the pipelines for live environments such as Staging.
### Troubleshooting
If Jenkins docker container exits without providing any information in the logs, try increasing the memory used by
the Docker Engine.
......@@ -62,7 +62,7 @@ The scanning tools and vulnerabilities database are updated regularly.
| Secure scanning tool | Vulnerabilities database updates |
|:-------------------------------------------------------------|-------------------------------------------|
| [Container Scanning](container_scanning/index.md) | Uses `clair`. The latest `clair-db` version is used for each job by running the [`latest` docker image tag](https://gitlab.com/gitlab-org/gitlab/blob/438a0a56dc0882f22bdd82e700554525f552d91b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L37). The `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). |
| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Rubygems), `retire.js` (for NPM packages), and `gemnasium` (GitLab's own tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. |
| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Rubygems), `retire.js` (for NPM packages), and `gemnasium` (GitLab's own tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. See our [current measurement of time from CVE being issued to our product being updated](https://about.gitlab.com/handbook/engineering/development/performance-indicators/#cve-issue-to-update). |
| [Dynamic Application Security Testing (DAST)](dast/index.md) | The scanning engine is updated on a periodic basis. See the [version of the underlying tool `zaproxy`](https://gitlab.com/gitlab-org/security-products/dast/blob/master/Dockerfile#L1). The scanning rules are downloaded at scan runtime. |
| [Static Application Security Testing (SAST)](sast/index.md) | Relies exclusively on [the tools GitLab wraps](sast/index.md#supported-languages-and-frameworks). The underlying analyzers are updated at least once per month if a relevant update is available. The vulnerabilities database is updated by the upstream tools. |
......@@ -240,6 +240,15 @@ An approval is optional when a license report:
- Contains no software license violations.
- Contains only new licenses that are `approved` or unknown.
## Working in an offline environment
It is possible to run most of the GitLab security scanners when not
connected to the internet, in what is sometimes known as an offline,
limited connectivity, Local Area Network (LAN), Intranet, or "air-gap"
environment.
Read how to [operate the Secure scanners in an offline environment](offline_deployments/index.md).
## Outdated security reports
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4913) in GitLab 12.7.
......
---
type: reference, howto
---
# Offline deployments
This document describes how to operate Secure scanners offline.
## Overview
It is possible to run most of the GitLab security scanners when not
connected to the internet, in what is sometimes known as an offline,
limited connectivity, Local Area Network (LAN), Intranet, or "air-gap"
environment.
In this situation, the GitLab instance can be one, or more, servers and services running in a network that can talk to one another, but have zero, or perhaps very restricted access to the internet. Assume anything within the GitLab instance and supporting infrastrusture (private maven repository for example) can be accessed via local network connection. Assume any files from the internet must come in via physical media (USB drive, hard drive).
GitLab scanners generally will connect to the internet to download the
latest sets of signatures, rules, and patches. A few extra steps are necessary
to configure the tools to not do this and to still function properly.
### Container registries and package repositories
At a high-level, each of the security analyzers are delivered as Docker
containers and reference various package repositories. When you run a job on
an internet-connected GitLab installation, GitLab checks the GitLab.com-hosted
container registry and package repositories to ensure that you have
the latest versions.
In an air-gapped environment, this must be disabled so that GitLab.com is not
queried. Because the GitLab.com registry and repositories are not available,
you must update each of the scanners to either reference a different,
internally-hosted registry or provide access to the individual scanner images.
You must also ensure that your app has access to common package repos
that are not hosted on GitLab.com, such as npm, yarn, or rubygems. Packages
from these repos can be obtained by temporarily connecting to a network or by
mirroring the packages inside your own offline network.
### Scanner signature and rule updates
When connected to the internet, some scanners will reference public databases
for the latest sets of signatures and rules to check against. Without connectivity,
this is not possible. Depending on the scanner, you must therefore disable
these automatic update checks and either use the databases that they came
with or manually update those databases.
## Specific scanner instructions
Each individual scanner may be slightly different than the steps described
above. You can find more info at each of the pages below:
- [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-air-gapped-installation)
- [SAST offline directions](../sast/index.md#gitlab-sast-in-an-offline-air-gapped-installation)
- [DAST offline directions](../dast/index.md#running-dast-in-an-offline-air-gapped-installation)
......@@ -297,6 +297,25 @@ Ingress with the recent changes.
![Disabling WAF](../../topics/web_application_firewall/img/guide_waf_ingress_save_changes_v12_9.png)
##### Viewing Web Application Firewall traffic
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/14707) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.9.
You can view Web Application Firewall traffic by navigating to your project's
**Security & Compliance > Threat Monitoring** page.
From there, you can see tracked over time:
- The total amount of traffic to your application.
- The proportion of traffic that is considered anomalous by the Web Application
Firewall's default [OWASP ruleset](https://www.modsecurity.org/CRS/Documentation/).
If a significant percentage of traffic is anomalous, it should be investigated
for potential threats, which can be done by
[examining the application logs](#web-application-firewall-modsecurity).
![Threat Monitoring](img/threat_monitoring_v12_9.png)
### JupyterHub
> - Introduced in GitLab 11.0 for project-level clusters.
......
......@@ -88,11 +88,14 @@ or over the size limit, you can [reduce your repository size with Git](../projec
## IP range
GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any
IP based firewall can be configured by looking up all
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
GitLab.com is using the IP range `34.74.90.64/28` for traffic from its Web/API
fleet. You can expect connections from webhooks or repository mirroring to come
from those IPs and whitelist them.
[Static endpoints](https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/97) are being considered.
For connections from CI/CD runners we are not providing static IP addresses.
All our runners are deployed into Google Cloud Platform (GCP) - any IP based
firewall can be configured by looking up all
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
## Maximum number of webhooks
......
......@@ -159,6 +159,9 @@ In most of the below cases, the notification will be sent to:
- Subscribers: anyone who manually subscribed to the issue, merge request, or epic **(ULTIMATE)**
- Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below
NOTE: **Note:**
To minimize the number of notifications that do not require any action, from [GitLab 12.9 onwards](https://gitlab.com/gitlab-org/gitlab/issues/616), eligible approvers are no longer notified for all the activities in their projects. To receive them they have to change their user notification settings to **Watch** instead.
| Event | Sent to |
|------------------------|---------|
| New issue | |
......
......@@ -21,6 +21,20 @@ where you can choose to either:
- Cherry-pick the changes directly into the selected branch.
- Create a new merge request with the cherry-picked changes.
### Cherry-pick tracking
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2675) in GitLab 12.9.
When you cherry-pick a merge commit, GitLab will output a system note to the related merge
request thread crosslinking the new commit and the existing merge request.
![Cherry-pick tracking in Merge Request timeline](img/cherry_pick_mr_timeline_v12_9.png)
Each deployment's [list of associated merge requests](../../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment) will include cherry-picked merge commits.
NOTE: **Note:**
We only track cherry-pick executed from GitLab (both UI and API). Support for [tracking cherry-picked commits through the command line](https://gitlab.com/gitlab-org/gitlab/issues/202215) is planned for a future release.
## Cherry-picking a commit
You can cherry-pick a commit from the commit details page:
......
......@@ -297,7 +297,7 @@ For a list of known issues, visit GitLab's [public issue tracker].
[staticgen]: https://www.staticgen.com/
[pages-jekyll]: https://gitlab.com/pages/jekyll
[metarefresh]: https://en.wikipedia.org/wiki/Meta_refresh
[public issue tracker]: https://gitlab.com/gitlab-org/gitlab-foss/issues?label_name=pages
[public issue tracker]: https://gitlab.com/gitlab-org/gitlab/-/issues?label_name[]=Category%3APages
[quick start guide]: ../../../ci/quick_start/README.md
[pages-index-guide]: index.md
[pages-quick]: getting_started_part_one.md
......
......@@ -133,6 +133,7 @@ module Gitlab
%r{\A(\.gitlab-ci\.yml\z|\.gitlab\/ci)} => :engineering_productivity,
%r{\A\.overcommit\.yml\.example\z} => :engineering_productivity,
%r{\Atooling/overcommit/} => :engineering_productivity,
%r{\A.editorconfig\z} => :engineering_productivity,
%r{Dangerfile\z} => :engineering_productivity,
%r{\A(ee/)?(danger/|lib/gitlab/danger/)} => :engineering_productivity,
%r{\A(ee/)?scripts/} => :engineering_productivity,
......
......@@ -101,6 +101,8 @@ module Gitlab
#
redis.expire(key, EXPIRATION)
end
record_memory_usage(fetch_memory_usage(redis, key))
end
# Subsequent read_file calls would need the latest cache.
......@@ -109,6 +111,23 @@ module Gitlab
clear_memoization(:cacheable_files)
end
def record_memory_usage(memory_usage)
if memory_usage
self.class.gitlab_redis_diff_caching_memory_usage_bytes.observe({}, memory_usage)
end
end
def fetch_memory_usage(redis, key)
# Redis versions prior to 4.0.0 do not support memory usage reporting
# for a specific key. As of 11-March-2020 we support Redis 3.x, so
# need to account for this. We can remove this check once we
# officially cease supporting versions <4.0.0.
#
return if Gem::Version.new(redis.info["redis_version"]) < Gem::Version.new("4")
redis.memory("USAGE", key)
end
def file_paths
strong_memoize(:file_paths) do
diff_files.collect(&:file_path)
......
......@@ -237,7 +237,7 @@ module Gitlab
end
def expire_redis_set_method_caches(methods)
methods.each { |name| redis_set_cache.expire(name) }
redis_set_cache.expire(*methods)
end
def expire_redis_hash_method_caches(methods)
......
......@@ -14,8 +14,11 @@ module Gitlab
"#{key}:set"
end
def expire(key)
with { |redis| redis.del(cache_key(key)) }
def expire(*keys)
with do |redis|
keys = keys.map { |key| cache_key(key) }
unlink_or_delete(redis, keys)
end
end
def exist?(key)
......@@ -51,5 +54,15 @@ module Gitlab
def with(&blk)
Gitlab::Redis::Cache.with(&blk) # rubocop:disable CodeReuse/ActiveRecord
end
def unlink_or_delete(redis, keys)
if Feature.enabled?(:repository_set_cache_unlink, default_enabled: true)
redis.unlink(*keys)
else
redis.del(*keys)
end
rescue ::Redis::CommandError
redis.del(*keys)
end
end
end
......@@ -66,8 +66,8 @@ module Gitlab
clusters_disabled: count(::Clusters::Cluster.disabled),
project_clusters_disabled: count(::Clusters::Cluster.disabled.project_type),
group_clusters_disabled: count(::Clusters::Cluster.disabled.group_type),
clusters_platforms_eks: count(::Clusters::Cluster.aws_installed.enabled, batch: false),
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled, batch: false),
clusters_platforms_eks: count(::Clusters::Cluster.aws_installed.enabled),
clusters_platforms_gke: count(::Clusters::Cluster.gcp_installed.enabled),
clusters_platforms_user: count(::Clusters::Cluster.user_provided.enabled),
clusters_applications_helm: count(::Clusters::Applications::Helm.available),
clusters_applications_ingress: count(::Clusters::Applications::Ingress.available),
......
......@@ -363,6 +363,9 @@ msgstr ""
msgid "%{name} found %{resultsString}"
msgstr ""
msgid "%{name} is scheduled for %{action}"
msgstr ""
msgid "%{name}'s avatar"
msgstr ""
......@@ -1585,6 +1588,9 @@ msgstr ""
msgid "All"
msgstr ""
msgid "All %{replicableType} are being scheduled for %{action}"
msgstr ""
msgid "All Members"
msgstr ""
......@@ -12795,7 +12801,13 @@ msgstr ""
msgid "Missing commit signatures endpoint!"
msgstr ""
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgid "MissingSSHKeyWarningLink|Add SSH key"
msgstr ""
msgid "MissingSSHKeyWarningLink|Don't show again"
msgstr ""
msgid "MissingSSHKeyWarningLink|You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
msgid "Modal|Cancel"
......@@ -13139,7 +13151,7 @@ msgstr ""
msgid "No %{providerTitle} repositories found"
msgstr ""
msgid "No Design Repositories match this filter"
msgid "No %{replicableType} match this filter"
msgstr ""
msgid "No Epic"
......@@ -16896,7 +16908,7 @@ msgstr ""
msgid "Resync"
msgstr ""
msgid "Resync all designs"
msgid "Resync all %{replicableType}"
msgstr ""
msgid "Retry"
......@@ -20013,10 +20025,10 @@ msgstr ""
msgid "There was an error fetching median data for stages"
msgstr ""
msgid "There was an error fetching the Node's Groups"
msgid "There was an error fetching the %{replicableType}"
msgstr ""
msgid "There was an error fetching the designs"
msgid "There was an error fetching the Node's Groups"
msgstr ""
msgid "There was an error fetching the environments information."
......@@ -20061,7 +20073,10 @@ msgstr ""
msgid "There was an error subscribing to this label."
msgstr ""
msgid "There was an error syncing the designs."
msgid "There was an error syncing project %{name}"
msgstr ""
msgid "There was an error syncing the %{replicableType}"
msgstr ""
msgid "There was an error trying to validate your query"
......@@ -23102,9 +23117,6 @@ msgstr ""
msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
msgstr ""
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
msgstr ""
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
......
FROM ruby:2.6-stretch
LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive
##
......
......@@ -44,6 +44,14 @@ Note: GitLab QA uses [Selenium WebDriver](https://www.seleniumhq.org/) via
the browser to use. You will need to have Chrome (or Chromium) and
[chromedriver](https://chromedriver.chromium.org/) installed / in your `$PATH`.
### Writing tests
- [Writing tests from scratch tutorial](../doc/development/testing_guide/end_to_end/quick_start_guide.md)
- [Best practices](../doc/development/testing_guide/best_practices.md)
- [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md)
- [Guidelines](../doc/development/testing_guide/index.md)
- [Tests with special setup for local environemnts](../doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md)
### Run the end-to-end tests in a local development environment
Follow the GDK instructions to [prepare](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/prepare.md)
......@@ -77,13 +85,6 @@ Once you have the license file you can export it as an environment variable and
export EE_LICENSE=$(cat /path/to/gitlab_license)
```
### Writing tests
- [Writing tests from scratch tutorial](../doc/development/testing_guide/end_to_end/quick_start_guide.md)
- [Best practices](../doc/development/testing_guide/best_practices.md)
- [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md)
- [Guidelines](../doc/development/testing_guide/index.md)
### Running specific tests
You can also supply specific tests to run as another parameter. For example, to
......
......@@ -81,6 +81,33 @@ describe SubmoduleHelper do
end
end
context 'submodule on gist.github.com' do
it 'detects ssh' do
stub_url('git@gist.github.com:gitlab-org/gitlab-foss.git')
is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash'])
end
it 'detects http' do
stub_url('http://gist.github.com/gitlab-org/gitlab-foss.git')
is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash'])
end
it 'detects https' do
stub_url('https://gist.github.com/gitlab-org/gitlab-foss.git')
is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash'])
end
it 'handles urls with no .git on the end' do
stub_url('http://gist.github.com/gitlab-org/gitlab-foss')
is_expected.to eq(['https://gist.github.com/gitlab-org/gitlab-foss', 'https://gist.github.com/gitlab-org/gitlab-foss/hash'])
end
it 'returns original with non-standard url' do
stub_url('http://gist.github.com/another/gitlab-org/gitlab-foss.git')
is_expected.to eq([repo.submodule_url_for, nil])
end
end
context 'submodule on github.com' do
it 'detects ssh' do
stub_url('git@github.com:gitlab-org/gitlab-foss.git')
......
......@@ -222,6 +222,7 @@ describe Gitlab::Danger::Helper do
'lib/gitlab/danger/foo' | :engineering_productivity
'ee/lib/gitlab/danger/foo' | :engineering_productivity
'.overcommit.yml.example' | :engineering_productivity
'.editorconfig' | :engineering_productivity
'tooling/overcommit/foo' | :engineering_productivity
'lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml' | :backend
......
......@@ -97,6 +97,28 @@ describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
let(:paths) { merge_request.diffs.raw_diff_files.select(&:text?).map(&:file_path) }
end
it 'updates memory usage metrics if Redis version >= 4' do
allow_next_instance_of(Redis) do |redis|
allow(redis).to receive(:info).and_return({ "redis_version" => "4.0.0" })
expect(described_class.gitlab_redis_diff_caching_memory_usage_bytes)
.to receive(:observe).and_call_original
cache.send(:write_to_redis_hash, diff_hash)
end
end
it 'does not update memory usage metrics if Redis version < 4' do
allow_next_instance_of(Redis) do |redis|
allow(redis).to receive(:info).and_return({ "redis_version" => "3.0.0" })
expect(described_class.gitlab_redis_diff_caching_memory_usage_bytes)
.not_to receive(:observe).and_call_original
cache.send(:write_to_redis_hash, diff_hash)
end
end
context 'different diff_collections for the same diffable' do
before do
cache.write_if_empty
......
......@@ -211,8 +211,7 @@ describe Gitlab::RepositoryCacheAdapter do
it 'expires the caches of the given methods' do
expect(cache).to receive(:expire).with(:rendered_readme)
expect(cache).to receive(:expire).with(:branch_names)
expect(redis_set_cache).to receive(:expire).with(:rendered_readme)
expect(redis_set_cache).to receive(:expire).with(:branch_names)
expect(redis_set_cache).to receive(:expire).with(:rendered_readme, :branch_names)
expect(redis_hash_cache).to receive(:delete).with(:rendered_readme)
expect(redis_hash_cache).to receive(:delete).with(:branch_names)
......
......@@ -51,12 +51,52 @@ describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do
end
describe '#expire' do
it 'expires the given key from the cache' do
subject { cache.expire(*keys) }
before do
cache.write(:foo, ['value'])
cache.write(:bar, ['value2'])
end
it 'actually wrote the values' do
expect(cache.read(:foo)).to contain_exactly('value')
expect(cache.expire(:foo)).to eq(1)
expect(cache.read(:foo)).to be_empty
expect(cache.read(:bar)).to contain_exactly('value2')
end
context 'single key' do
let(:keys) { %w(foo) }
it { is_expected.to eq(1) }
it 'deletes the given key from the cache' do
subject
expect(cache.read(:foo)).to be_empty
end
end
context 'multiple keys' do
let(:keys) { %w(foo bar) }
it { is_expected.to eq(2) }
it 'deletes the given keys from the cache' do
subject
expect(cache.read(:foo)).to be_empty
expect(cache.read(:bar)).to be_empty
end
end
context "unlink isn't supported" do
before do
allow_any_instance_of(Redis).to receive(:unlink) { raise ::Redis::CommandError }
end
it 'still deletes the given key' do
expect(cache.expire(:foo)).to eq(1)
expect(cache.read(:foo)).to be_empty
end
end
end
......
......@@ -61,18 +61,6 @@ describe Commits::CherryPickService do
expect(mr_notes.length).to eq(1)
expect(mr_notes[0].commit_id).to eq(result[:result])
end
context 'when :track_mr_picking feature flag is disabled' do
before do
stub_feature_flags(track_mr_picking: false)
end
it 'does not add system notes' do
expect do
cherry_pick(merge_commit_sha, branch_name)
end.not_to change { Note.count }
end
end
end
def find_cherry_pick_notes(noteable)
......
......@@ -160,53 +160,6 @@ describe Deployments::LinkMergeRequestsService do
expect(deploy.merge_requests).to be_empty
end
context 'when :track_mr_picking feature flag is disabled' do
before do
stub_feature_flags(track_mr_picking: false)
end
it 'does not link picked merge requests' do
environment = create(:environment, project: project)
deploy =
create(:deployment, :success, project: project, environment: environment)
picked_mr = create(
:merge_request,
:merged,
merge_commit_sha: '123abc',
source_project: project,
target_project: project
)
mr1 = create(
:merge_request,
:merged,
merge_commit_sha: mr1_merge_commit_sha,
source_project: project,
target_project: project
)
# mr1 includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
mr2 = create(
:merge_request,
:merged,
merge_commit_sha: mr2_merge_commit_sha,
source_project: project,
target_project: project
)
described_class.new(deploy).link_merge_requests_for_range(
first_deployment_sha,
mr2_merge_commit_sha
)
expect(deploy.merge_requests).to include(mr1, mr2)
expect(deploy.merge_requests).not_to include(picked_mr)
end
end
end
describe '#link_all_merged_merge_requests' do
......
......@@ -253,7 +253,7 @@ describe ::SystemNotes::MergeRequestsService do
end
it "posts the 'picked merge request' system note" do
expect(subject.note).to eq("picked this merge request into branch [`#{branch_name}`](/#{project.full_path}/-/tree/#{branch_name}) with commit #{commit_sha}")
expect(subject.note).to eq("picked the changes into the branch [`#{branch_name}`](/#{project.full_path}/-/tree/#{branch_name}) with commit #{commit_sha}")
end
it 'links the merge request and the cherry-pick commit' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment