Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
61cf5b32c5
commit
3de2ce7c6b
|
@ -1 +1 @@
|
|||
448304d500ab91dca193a21094319d38e22f60a7
|
||||
6bb5f6969910ce5010f1c894ee671a86e656e6da
|
||||
|
|
|
@ -292,7 +292,7 @@
|
|||
"project": {
|
||||
"description": "Path to the project, e.g. `group/project`, or `group/sub-group/project`.",
|
||||
"type": "string",
|
||||
"pattern": "\\S/\\S"
|
||||
"pattern": "\\S/\\S|\\$(\\S+)"
|
||||
},
|
||||
"ref": {
|
||||
"description": "Branch/Tag/Commit-hash for the target project.",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { __ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
export default (IssuableTokenKeys, disableTargetBranchFilter = false) => {
|
||||
const reviewerToken = {
|
||||
formattedKey: __('Reviewer'),
|
||||
formattedKey: s__('SearchToken|Reviewer'),
|
||||
key: 'reviewer',
|
||||
type: 'string',
|
||||
param: 'username',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { flattenDeep } from 'lodash';
|
||||
import { __ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import FilteredSearchTokenKeys from './filtered_search_token_keys';
|
||||
|
||||
export const tokenKeys = [
|
||||
|
@ -13,7 +13,7 @@ export const tokenKeys = [
|
|||
tag: '@author',
|
||||
},
|
||||
{
|
||||
formattedKey: __('Assignee'),
|
||||
formattedKey: s__('SearchToken|Assignee'),
|
||||
key: 'assignee',
|
||||
type: 'string',
|
||||
param: 'username',
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import { initCiSecureFiles } from '~/ci_secure_files';
|
||||
|
||||
initCiSecureFiles();
|
|
@ -0,0 +1,27 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
mr: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
widgets() {
|
||||
return [].filter((w) => w);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section role="region" :aria-label="__('Merge request reports')" data-testid="mr-widget-app">
|
||||
<component
|
||||
:is="widget"
|
||||
v-for="(widget, index) in widgets"
|
||||
:key="widget.name || index"
|
||||
:mr="mr"
|
||||
:class="{ 'mr-widget-border-top': index === 0 }"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
|
@ -0,0 +1,148 @@
|
|||
<script>
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import { __ } from '~/locale';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
|
||||
const FETCH_TYPE_COLLAPSED = 'collapsed';
|
||||
// const FETCH_TYPE_EXPANDED = 'expanded';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* @param {value.collapsed} Object
|
||||
* @param {value.extended} Object
|
||||
*/
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
loadingText: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
errorText: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: __('Failed to load'),
|
||||
},
|
||||
fetchCollapsedData: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
fetchExtendedData: {
|
||||
type: Function,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
// If the summary slot is not used, this value will be used as a fallback.
|
||||
summary: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
// If the content slot is not used, this value will be used as a fallback.
|
||||
content: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
multiPolling: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
statusIcon: null,
|
||||
error: null,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.loading = true;
|
||||
|
||||
try {
|
||||
await this.fetch(this.fetchCollapsedData, FETCH_TYPE_COLLAPSED);
|
||||
} catch {
|
||||
this.error = this.errorText;
|
||||
}
|
||||
|
||||
this.loading = false;
|
||||
},
|
||||
methods: {
|
||||
fetch(handler, dataType) {
|
||||
const requests = this.multiPolling ? handler() : [handler];
|
||||
const allData = [];
|
||||
|
||||
const promises = requests.map((request) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const setData = (data) => {
|
||||
this.$emit('input', { ...this.value, [dataType]: data });
|
||||
resolve(data);
|
||||
};
|
||||
|
||||
const poll = new Poll({
|
||||
resource: {
|
||||
fetchData: () => request(),
|
||||
},
|
||||
method: 'fetchData',
|
||||
successCallback: (response) => {
|
||||
const headers = normalizeHeaders(response.headers);
|
||||
|
||||
if (headers['POLL-INTERVAL']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.multiPolling) {
|
||||
allData.push(response.data);
|
||||
|
||||
if (allData.length === requests.length) {
|
||||
setData(allData);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setData(response.data);
|
||||
},
|
||||
errorCallback: (e) => {
|
||||
Sentry.captureException(e);
|
||||
reject(e);
|
||||
},
|
||||
});
|
||||
|
||||
poll.makeRequest();
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="media-section" data-testid="widget-extension">
|
||||
<div class="media gl-p-5">
|
||||
<!-- status icon will go here -->
|
||||
<div
|
||||
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
|
||||
data-testid="widget-extension-top-level"
|
||||
>
|
||||
<div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
|
||||
<slot name="summary">{{ summary }}</slot>
|
||||
</div>
|
||||
<!-- actions will go here -->
|
||||
<!-- toggle button will go here -->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mr-widget-grouped-section gl-relative"
|
||||
data-testid="widget-extension-collapsed-section"
|
||||
>
|
||||
<slot name="content">{{ content }}</slot>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { GlSafeHtmlDirective } from '@gitlab/ui';
|
||||
import { isEmpty } from 'lodash';
|
||||
import securityReportExtension from 'ee_else_ce/vue_merge_request_widget/extensions/security_reports';
|
||||
import { registerExtension } from '~/vue_merge_request_widget/components/extensions';
|
||||
import MrWidgetApprovals from 'ee_else_ce/vue_merge_request_widget/components/approvals/approvals.vue';
|
||||
import MRWidgetService from 'ee_else_ce/vue_merge_request_widget/services/mr_widget_service';
|
||||
|
@ -39,6 +38,7 @@ import ShaMismatch from './components/states/sha_mismatch.vue';
|
|||
import UnresolvedDiscussionsState from './components/states/unresolved_discussions.vue';
|
||||
import WorkInProgressState from './components/states/work_in_progress.vue';
|
||||
import ExtensionsContainer from './components/extensions/container';
|
||||
import WidgetContainer from './components/widget/app.vue';
|
||||
import { STATE_MACHINE, stateToComponentMap } from './constants';
|
||||
import eventHub from './event_hub';
|
||||
import mergeRequestQueryVariablesMixin from './mixins/merge_request_query_variables';
|
||||
|
@ -58,6 +58,7 @@ export default {
|
|||
components: {
|
||||
Loading,
|
||||
ExtensionsContainer,
|
||||
WidgetContainer,
|
||||
'mr-widget-suggest-pipeline': WidgetSuggestPipeline,
|
||||
MrWidgetPipelineContainer,
|
||||
MrWidgetAlertMessage,
|
||||
|
@ -262,11 +263,6 @@ export default {
|
|||
this.registerTestReportExtension();
|
||||
}
|
||||
},
|
||||
shouldRenderSecurityReport(newVal) {
|
||||
if (newVal) {
|
||||
this.registerSecurityReportExtension();
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
MRWidgetService.fetchInitialData()
|
||||
|
@ -522,11 +518,6 @@ export default {
|
|||
registerExtension(testReportExtension);
|
||||
}
|
||||
},
|
||||
registerSecurityReportExtension() {
|
||||
if (this.shouldRenderSecurityReport && this.shouldShowSecurityExtension) {
|
||||
registerExtension(securityReportExtension);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -587,7 +578,11 @@ export default {
|
|||
</template>
|
||||
</mr-widget-alert-message>
|
||||
</div>
|
||||
|
||||
<extensions-container :mr="mr" />
|
||||
|
||||
<widget-container v-if="mr" :mr="mr" />
|
||||
|
||||
<grouped-codequality-reports-app
|
||||
v-if="shouldRenderCodeQuality && !shouldShowCodeQualityExtension"
|
||||
:head-blob-path="mr.headBlobPath"
|
||||
|
|
|
@ -33,7 +33,6 @@ export default class MergeRequestStore {
|
|||
|
||||
this.setData(data);
|
||||
this.initCodeQualityReport(data);
|
||||
this.initSecurityReport(data);
|
||||
this.setGitpodData(data);
|
||||
}
|
||||
|
||||
|
@ -42,19 +41,6 @@ export default class MergeRequestStore {
|
|||
this.codeQuality = data.codequality_reports_path;
|
||||
}
|
||||
|
||||
initSecurityReport(data) {
|
||||
// TODO: check if gl.mrWidgetData can be safely removed after we migrate to the
|
||||
// widget extension.
|
||||
this.securityReportPaths = {
|
||||
apiFuzzingReportPath: data.api_fuzzing_comparison_path,
|
||||
coverageFuzzingReportPath: data.coverage_fuzzing_comparison_path,
|
||||
sastReportPath: data.sast_comparison_path,
|
||||
dastReportPath: data.dast_comparison_path,
|
||||
secretDetectionReportPath: data.secret_detection_comparison_path,
|
||||
dependencyScanningReportPath: data.dependency_scanning_comparison_path,
|
||||
};
|
||||
}
|
||||
|
||||
setData(data, isRebased) {
|
||||
this.initApprovals();
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::Ci::SecureFilesController < Projects::ApplicationController
|
||||
before_action :authorize_read_secure_files!
|
||||
|
||||
feature_category :pipeline_authoring
|
||||
|
||||
def show
|
||||
render_404 unless Feature.enabled?(:ci_secure_files, project)
|
||||
end
|
||||
end
|
|
@ -31,7 +31,7 @@ module Mutations
|
|||
runners = find_all_runners_by_ids(model_ids_of(ids))
|
||||
|
||||
result = ::Ci::Runners::BulkDeleteRunnersService.new(runners: runners).execute
|
||||
result.slice(:deleted_count, :deleted_ids).merge(errors: [])
|
||||
result.payload.slice(:deleted_count, :deleted_ids).merge(errors: [])
|
||||
else
|
||||
{ errors: [] }
|
||||
end
|
||||
|
|
|
@ -15,10 +15,10 @@ module Ci
|
|||
def execute
|
||||
if @runners
|
||||
# Delete a few runners immediately
|
||||
return delete_runners
|
||||
return ServiceResponse.success(payload: delete_runners)
|
||||
end
|
||||
|
||||
{ deleted_count: 0, deleted_ids: [] }
|
||||
ServiceResponse.success(payload: { deleted_count: 0, deleted_ids: [] })
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
- page_title s_('Secure Files')
|
||||
|
||||
#js-ci-secure-files{ data: { project_id: @project.id, admin: can?(current_user, :admin_secure_files, @project).to_s, file_size_limit: Ci::SecureFile::FILE_SIZE_LIMIT.to_mb } }
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: highlight_diffs_optimize_memory_usage
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92456
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367890
|
||||
milestone: '15.3'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
|
@ -96,7 +96,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
namespace :ci do
|
||||
resource :lint, only: [:show, :create]
|
||||
resource :pipeline_editor, only: [:show], controller: :pipeline_editor, path: 'editor'
|
||||
resource :secure_files, only: [:show], controller: :secure_files, path: 'secure_files'
|
||||
resources :daily_build_group_report_results, only: [:index], constraints: { format: /(csv|json)/ }
|
||||
namespace :prometheus_metrics do
|
||||
resources :histograms, only: [:create], constraints: { format: 'json' }
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
# Error: gitlab.HeadingDepth
|
||||
#
|
||||
# Checks that there are no headings greater than 3 levels
|
||||
#
|
||||
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
||||
extends: existence
|
||||
message: 'The subheading "%s" is nested too deeply. Headings deeper than H5 suggest the section or page should be refactored.'
|
||||
link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#headings-in-markdown
|
||||
level: warning
|
||||
scope: raw
|
||||
raw:
|
||||
- '(?<=\n)#{5,}\s.*'
|
|
@ -1202,8 +1202,8 @@ Before diving in to the following sections, here's some basic troubleshooting:
|
|||
been synchronized (for example, via NTP).
|
||||
|
||||
1. If you are using an S3-backed Registry, double check that the IAM
|
||||
permissions and the S3 credentials (including region) are correct. See [the
|
||||
sample IAM policy](https://docs.docker.com/registry/storage-drivers/s3/)
|
||||
permissions and the S3 credentials (including region) are correct. See
|
||||
[the sample IAM policy](https://docs.docker.com/registry/storage-drivers/s3/)
|
||||
for more details.
|
||||
|
||||
1. Check the Registry logs (for example `/var/log/gitlab/registry/current`) and the GitLab production logs
|
||||
|
@ -1631,8 +1631,8 @@ wrong. However, since all communications between Docker clients and servers
|
|||
are done over HTTPS, it's a bit difficult to decrypt the traffic quickly even
|
||||
if you know the private key. What can we do instead?
|
||||
|
||||
One way would be to disable HTTPS by setting up an [insecure
|
||||
Registry](https://docs.docker.com/registry/insecure/). This could introduce a
|
||||
One way would be to disable HTTPS by setting up an
|
||||
[insecure Registry](https://docs.docker.com/registry/insecure/). This could introduce a
|
||||
security hole and is only recommended for local testing. If you have a
|
||||
production system and can't or don't want to do this, there is another way:
|
||||
use mitmproxy, which stands for Man-in-the-Middle Proxy.
|
||||
|
|
|
@ -933,8 +933,8 @@ The following settings are:
|
|||
| `connection` | Various connection options described below. | |
|
||||
|
||||
NOTE:
|
||||
If you want to stop using and disconnect the NFS server, you need to [explicitly disable
|
||||
local storage](#disable-pages-local-storage), and it's only possible after upgrading to GitLab 13.11.
|
||||
If you want to stop using and disconnect the NFS server, you need to
|
||||
[explicitly disable local storage](#disable-pages-local-storage), and it's only possible after upgrading to GitLab 13.11.
|
||||
|
||||
#### S3-compatible connection settings
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ GET /projects/:id/jobs
|
|||
| Attribute | Type | Required | Description |
|
||||
|-----------|--------------------------------|------------------------|-------------|
|
||||
| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, `waiting_for_resource`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
|
||||
```shell
|
||||
curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs?scope[]=pending&scope[]=running"
|
||||
|
@ -167,7 +167,7 @@ GET /projects/:id/pipelines/:pipeline_id/jobs
|
|||
|-------------------|--------------------------------|------------------------|-------------|
|
||||
| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `pipeline_id` | integer | **{check-circle}** Yes | ID of a pipeline. Can also be obtained in CI jobs via the [predefined CI variable](../ci/variables/predefined_variables.md) `CI_PIPELINE_ID`. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, `waiting_for_resource`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
| `include_retried` | boolean | **{dotted-circle}** No | Include retried jobs in the response. Defaults to `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/272627) in GitLab 13.9. |
|
||||
|
||||
```shell
|
||||
|
@ -324,7 +324,7 @@ GET /projects/:id/pipelines/:pipeline_id/bridges
|
|||
|---------------|--------------------------------|------------------------|-------------|
|
||||
| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `pipeline_id` | integer | **{check-circle}** Yes | ID of a pipeline. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
| `scope` | string **or** array of strings | **{dotted-circle}** No | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, `waiting_for_resource`, or `manual`. All jobs are returned if `scope` is not provided. |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/pipelines/6/bridges?scope[]=pending&scope[]=running"
|
||||
|
|
|
@ -11,7 +11,7 @@ Notes are comments on:
|
|||
- Snippets
|
||||
- Issues
|
||||
- Merge requests
|
||||
- Epics **(PREMIUM)**
|
||||
- [Epics](../user/group/epics/index.md)
|
||||
|
||||
This includes system notes, which are notes about changes to the object (for example, when an
|
||||
assignee changes, GitLab posts a system note).
|
||||
|
@ -447,7 +447,7 @@ Parameters:
|
|||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/7/notes/1602"
|
||||
```
|
||||
|
||||
## Epics **(ULTIMATE)**
|
||||
## Epics **(PREMIUM)**
|
||||
|
||||
### List all epic notes
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ The examples in this document all use the instance-level prefix.
|
|||
/packages/conan/v1
|
||||
```
|
||||
|
||||
When using the instance-level routes, be aware that there is a [naming
|
||||
restriction](../../user/packages/conan_repository/index.md#package-recipe-naming-convention-for-instance-remotes)
|
||||
When using the instance-level routes, be aware that there is a
|
||||
[naming restriction](../../user/packages/conan_repository/index.md#package-recipe-naming-convention-for-instance-remotes)
|
||||
for Conan recipes.
|
||||
|
||||
### Project-level
|
||||
|
|
|
@ -342,9 +342,8 @@ tags using these formats:
|
|||
- `vX.Y.Z`
|
||||
- `X.Y.Z`
|
||||
|
||||
Where `X.Y.Z` is a version that follows [semantic
|
||||
versioning](https://semver.org/). For example, consider a project with the
|
||||
following tags:
|
||||
Where `X.Y.Z` is a version that follows [semantic versioning](https://semver.org/).
|
||||
For example, consider a project with the following tags:
|
||||
|
||||
- v1.0.0-pre1
|
||||
- v1.0.0
|
||||
|
|
|
@ -32,6 +32,8 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
|
||||
Example response:
|
||||
|
||||
<!-- vale gitlab.MultiLineLinks = NO -->
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
|
@ -57,6 +59,8 @@ Example response:
|
|||
]
|
||||
```
|
||||
|
||||
<!-- vale gitlab.MultiLineLinks = YES -->
|
||||
|
||||
## Get a wiki page
|
||||
|
||||
Get a wiki page for a given project.
|
||||
|
|
|
@ -35,8 +35,8 @@ sequenceDiagram
|
|||
Workhorse-->>-Runner: request results
|
||||
```
|
||||
|
||||
1. The CI/CD job generates a document in an LSIF format (usually `dump.lsif`) using [an
|
||||
indexer](https://lsif.dev) for the language of a project. The format
|
||||
1. The CI/CD job generates a document in an LSIF format (usually `dump.lsif`) using
|
||||
[an indexer](https://lsif.dev) for the language of a project. The format
|
||||
[describes](https://github.com/sourcegraph/sourcegraph/blob/main/doc/code_intelligence/explanations/writing_an_indexer.md)
|
||||
interactions between a method or function and its definitions or references. The
|
||||
document is marked to be stored as an LSIF report artifact.
|
||||
|
|
|
@ -10,8 +10,8 @@ Ruby processes accessing the database through
|
|||
ActiveRecord, automatically calculate the connection-pool size for the
|
||||
process based on the concurrency.
|
||||
|
||||
Because of the way [Ruby on Rails manages database
|
||||
connections](#connection-lifecycle), it is important that we have at
|
||||
Because of the way [Ruby on Rails manages database connections](#connection-lifecycle),
|
||||
it is important that we have at
|
||||
least as many connections as we have threads. While there is a 'pool'
|
||||
setting in [`database.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/database.yml.postgresql), it is not very practical because you need to
|
||||
maintain it in tandem with the number of application threads. For this
|
||||
|
@ -28,9 +28,8 @@ because connections are instantiated lazily.
|
|||
|
||||
## Troubleshooting connection-pool issues
|
||||
|
||||
The connection-pool usage can be seen per environment in the [connection-pool
|
||||
saturation
|
||||
dashboard](https://dashboards.gitlab.net/d/alerts-sat_rails_db_connection_pool/alerts-rails_db_connection_pool-saturation-detail?orgId=1).
|
||||
The connection-pool usage can be seen per environment in the
|
||||
[connection-pool saturation dashboard](https://dashboards.gitlab.net/d/alerts-sat_rails_db_connection_pool/alerts-rails_db_connection_pool-saturation-detail?orgId=1).
|
||||
|
||||
If the connection-pool is too small, this would manifest in
|
||||
`ActiveRecord::ConnectionTimeoutError`s from the application. Because we alert
|
||||
|
@ -41,8 +40,8 @@ hardcoded value (10).
|
|||
|
||||
At this point, we need to investigate what is using more connections
|
||||
than we anticipated. To do that, we can use the
|
||||
`gitlab_ruby_threads_running_threads` metric. For example, [this
|
||||
graph](https://thanos.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum%20by%20(thread_name)%20(%20gitlab_ruby_threads_running_threads%7Buses_db_connection%3D%22yes%22%7D%20)&g0.tab=0)
|
||||
`gitlab_ruby_threads_running_threads` metric. For example,
|
||||
[this graph](https://thanos.gitlab.net/graph?g0.range_input=1h&g0.max_source_resolution=0s&g0.expr=sum%20by%20(thread_name)%20(%20gitlab_ruby_threads_running_threads%7Buses_db_connection%3D%22yes%22%7D%20)&g0.tab=0)
|
||||
shows all running threads that connect to the database by their
|
||||
name. Threads labeled `puma worker` or `sidekiq_worker_thread` are
|
||||
the threads that define `Gitlab::Runtime.max_threads` so those are
|
||||
|
|
|
@ -221,8 +221,8 @@ ON DELETE CASCADE;
|
|||
```
|
||||
|
||||
The migration must run after the `DELETE` trigger is installed and the loose
|
||||
foreign key definition is deployed. As such, it must be a [post-deployment
|
||||
migration](post_deployment_migrations.md) dated after the migration for the
|
||||
foreign key definition is deployed. As such, it must be a
|
||||
[post-deployment migration](post_deployment_migrations.md) dated after the migration for the
|
||||
trigger. If the foreign key is deleted earlier, there is a good chance of
|
||||
introducing data inconsistency which needs manual cleanup:
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@ Execute a standard migration (not a post-migration):
|
|||
when naming indexes, so there is a possibility that not all indexes are properly renamed. After running
|
||||
the migration locally, check if there are inconsistently named indexes (`db/structure.sql`). Those can be
|
||||
renamed manually in a separate migration, which can be also part of the release M.N+1.
|
||||
- Foreign key columns might still contain the old table name. For smaller tables, follow our [standard column
|
||||
rename process](avoiding_downtime_in_migrations.md#renaming-columns)
|
||||
- Foreign key columns might still contain the old table name. For smaller tables, follow our
|
||||
[standard column rename process](avoiding_downtime_in_migrations.md#renaming-columns)
|
||||
- Avoid renaming database tables which are using with triggers.
|
||||
- Table modifications (add or remove columns) are not allowed during the rename process, please make sure that all changes to the table happen before the rename migration is started (or in the next release).
|
||||
- As the index names might change, verify that the model does not use bulk insert
|
||||
|
|
|
@ -148,8 +148,9 @@ to update the `title_html` with a title that has more than 1024 characters, the
|
|||
a database error.
|
||||
|
||||
Adding or removing a constraint to an existing attribute requires that any application changes are
|
||||
deployed _first_, [otherwise servers still in the old version of the application may try to update the
|
||||
attribute with invalid values](../multi_version_compatibility.md#ci-artifact-uploads-were-failing).
|
||||
deployed _first_,
|
||||
otherwise servers still in the old version of the application
|
||||
[may try to update the attribute with invalid values](../multi_version_compatibility.md#ci-artifact-uploads-were-failing).
|
||||
For these reasons, `add_text_limit` should run in a post-deployment migration.
|
||||
|
||||
Still in our example, for the 13.0 milestone (current), consider that the following validation
|
||||
|
|
|
@ -252,8 +252,8 @@ A scan on an index that required retrieving some data from the table.
|
|||
|
||||
Bitmap scans fall between sequential scans and index scans. These are typically
|
||||
used when we would read too much data from an index scan, but too little to
|
||||
perform a sequential scan. A bitmap scan uses what is known as a [bitmap
|
||||
index](https://en.wikipedia.org/wiki/Bitmap_index) to perform its work.
|
||||
perform a sequential scan. A bitmap scan uses what is known as a
|
||||
[bitmap index](https://en.wikipedia.org/wiki/Bitmap_index) to perform its work.
|
||||
|
||||
The [source code of PostgreSQL](https://gitlab.com/postgres/postgres/blob/REL_11_STABLE/src/include/nodes/plannodes.h#L441)
|
||||
states the following on bitmap scans:
|
||||
|
@ -794,8 +794,8 @@ Execution time: 0.113 ms
|
|||
|
||||
### ChatOps
|
||||
|
||||
[GitLab team members can also use our ChatOps solution, available in Slack using the
|
||||
`/chatops` slash command](../chatops_on_gitlabcom.md).
|
||||
GitLab team members can also use our ChatOps solution, available in Slack
|
||||
using the [`/chatops` slash command](../chatops_on_gitlabcom.md).
|
||||
|
||||
NOTE:
|
||||
While ChatOps is still available, the recommended way to generate execution plans is to use [Database Lab Engine](#database-lab-engine).
|
||||
|
|
|
@ -148,8 +148,8 @@ curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" \
|
|||
## Authorized Keys Check
|
||||
|
||||
This endpoint is called by the GitLab Shell authorized keys
|
||||
check. Which is called by OpenSSH for [fast SSH key
|
||||
lookup](../../administration/operations/fast_ssh_key_lookup.md).
|
||||
check. Which is called by OpenSSH for
|
||||
[fast SSH key lookup](../../administration/operations/fast_ssh_key_lookup.md).
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|:----------|:-------|:---------|:------------|
|
||||
|
|
|
@ -13,10 +13,8 @@ Sometimes when a new resource type is added it's not clear if it should be only
|
|||
"extension" of Issue (Issue Type) or if it should be a new first-class resource type
|
||||
(similar to issue, epic, merge request, snippet).
|
||||
|
||||
The idea of Issue Types was first proposed in [this
|
||||
issue](https://gitlab.com/gitlab-org/gitlab/-/issues/8767) and its usage was
|
||||
discussed few times since then, for example in [incident
|
||||
management](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55532).
|
||||
The idea of Issue Types was first proposed in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/8767) and its usage was
|
||||
discussed few times since then, for example in [incident management](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55532).
|
||||
|
||||
## What is an Issue Type
|
||||
|
||||
|
|
|
@ -76,14 +76,13 @@ process, which writes the contents to the standard output.
|
|||
1. The archive data is sent back to the client.
|
||||
|
||||
In step 7, the `gitaly-lfs-smudge` filter must talk to Workhorse, not to
|
||||
Rails, or an invalid LFS blob is saved. To support this, GitLab
|
||||
13.5 [changed the default Omnibus configuration to have Gitaly talk to
|
||||
the Workhorse](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4592)
|
||||
Rails, or an invalid LFS blob is saved. To support this, GitLab 13.5
|
||||
[changed the default Omnibus configuration to have Gitaly talk to the Workhorse](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4592)
|
||||
instead of Rails.
|
||||
|
||||
One side effect of this change: the correlation ID of the original
|
||||
request is not preserved for the internal API requests made by Gitaly
|
||||
(or `gitaly-lfs-smudge`), such as the one made in step 8. The
|
||||
correlation IDs for those API requests are random values until [this
|
||||
Workhorse issue](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/309) is
|
||||
correlation IDs for those API requests are random values until
|
||||
[this Workhorse issue](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/309) is
|
||||
resolved.
|
||||
|
|
|
@ -60,8 +60,8 @@ downstream services.
|
|||
|
||||
To mitigate this, ensure that the code establishing the new WebSocket connection
|
||||
is feature flagged and defaulted to `off`. A careful, percentage-based roll-out
|
||||
of the feature flag ensures that effects can be observed on the [WebSocket
|
||||
dashboard](https://dashboards.gitlab.net/d/websockets-main/websockets-overview?orgId=1)
|
||||
of the feature flag ensures that effects can be observed on the
|
||||
[WebSocket dashboard](https://dashboards.gitlab.net/d/websockets-main/websockets-overview?orgId=1)
|
||||
|
||||
1. Create a
|
||||
[feature flag roll-out](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/issue_templates/Feature%20Flag%20Roll%20Out.md)
|
||||
|
|
|
@ -111,7 +111,7 @@ sequenceDiagram
|
|||
|
||||
1. Finally, the timing metadata information that is used for diagnostic purposes is submitted to the Versions application. It consists of a list of metric identifiers and the time it took to calculate the metrics:
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911) in GitLab 15.0 [with a flag(../../user/feature_flags.md), enabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911) in GitLab 15.0 [with a flag](../../user/feature_flags.md), enabled by default.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/295289) in GitLab 15.2. [Feature flag `measure_service_ping_metric_collection`](https://gitlab.com/gitlab-org/gitlab/-/issues/358128) removed.
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -79,8 +79,9 @@ ON table_name
|
|||
USING GIN(column_name gin_trgm_ops);
|
||||
```
|
||||
|
||||
The key here is the `GIN(column_name gin_trgm_ops)` part. This creates a [GIN
|
||||
index](https://www.postgresql.org/docs/current/gin.html) with the operator class set to `gin_trgm_ops`. These indexes
|
||||
The key here is the `GIN(column_name gin_trgm_ops)` part. This creates a
|
||||
[GIN index](https://www.postgresql.org/docs/current/gin.html)
|
||||
with the operator class set to `gin_trgm_ops`. These indexes
|
||||
_can_ be used by `ILIKE` / `LIKE` and can lead to greatly improved performance.
|
||||
One downside of these indexes is that they can easily get quite large (depending
|
||||
on the amount of data indexed).
|
||||
|
|
|
@ -10,8 +10,8 @@ GitLab Workhorse is a smart reverse proxy for GitLab. It handles
|
|||
"large" HTTP requests such as file downloads, file uploads, Git
|
||||
push/pull and Git archive downloads.
|
||||
|
||||
Workhorse itself is not a feature, but there are [several features in
|
||||
GitLab](gitlab_features.md) that would not work efficiently without Workhorse.
|
||||
Workhorse itself is not a feature, but there are
|
||||
[several features in GitLab](gitlab_features.md) that would not work efficiently without Workhorse.
|
||||
|
||||
The canonical source for Workhorse is
|
||||
[`gitlab-org/gitlab/workhorse`](https://gitlab.com/gitlab-org/gitlab/tree/master/workhorse).
|
||||
|
|
|
@ -115,8 +115,7 @@ See the documentation on [File Locking](../../../user/project/file_lock.md).
|
|||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46572) in GitLab 13.6.
|
||||
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62539) in GitLab 14.0. Feature flag `include_lfs_blobs_in_archive` removed.
|
||||
|
||||
Prior to GitLab 13.5, [project source
|
||||
downloads](../../../user/project/repository/index.md) would include Git
|
||||
Prior to GitLab 13.5, [project source downloads](../../../user/project/repository/index.md) would include Git
|
||||
LFS pointers instead of the actual objects. For example, LFS pointers
|
||||
look like the following:
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
## Instance-level analytics
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12077) in GitLab 12.2.
|
||||
|
||||
Instance-level analytics make it possible to aggregate analytics across
|
||||
GitLab, so that users can view information across multiple projects and groups
|
||||
in one place.
|
||||
|
@ -18,8 +16,7 @@ in one place.
|
|||
|
||||
## Group-level analytics
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195979) in GitLab 12.8.
|
||||
> - Moved to GitLab Premium in 13.9.
|
||||
> Moved to GitLab Premium in 13.9.
|
||||
|
||||
GitLab provides several analytics features at the group level. Some of these features require you to use a higher tier than GitLab Free.
|
||||
|
||||
|
|
|
@ -31,12 +31,12 @@ To view merge request analytics:
|
|||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Analytics > Merge request**.
|
||||
|
||||
## View merge requests merged per month
|
||||
## View the number of merge requests in a date range
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232651) in GitLab 13.3.
|
||||
> - Filtering [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229266) in GitLab 13.4
|
||||
|
||||
To view the number of merge requests merged per month:
|
||||
To view the number of merge requests merged during a specific date range:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Analytics > Merge request**.
|
||||
|
|
|
@ -169,7 +169,7 @@ You can also add, remove, or replace issue contacts using the
|
|||
[GraphQL](../../api/graphql/reference/index.md#mutationissuesetcrmcontacts)
|
||||
API.
|
||||
|
||||
## Autocomplete contacts **(FREE SELF)**
|
||||
## Autocomplete contacts
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/2256) in GitLab 14.8 [with a flag](../../administration/feature_flags.md) named `contacts_autocomplete`. Disabled by default.
|
||||
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/352123) in GitLab 15.0.
|
||||
|
|
|
@ -23,5 +23,5 @@ ignored.
|
|||
## Syntax Highlighting
|
||||
|
||||
The `.gitattributes` file can be used to define which language to use when
|
||||
syntax highlighting files and diffs. See ["Syntax
|
||||
Highlighting"](highlighting.md) for more information.
|
||||
syntax highlighting files and diffs. See
|
||||
["Syntax Highlighting"](highlighting.md) for more information.
|
||||
|
|
|
@ -420,10 +420,10 @@ Now GitLab CI/CD not only builds the website, but also:
|
|||
|
||||
For more information, see the following blog posts.
|
||||
|
||||
- [Use GitLab CI/CD `environments` to deploy your
|
||||
web app to staging and production](https://about.gitlab.com/blog/2021/02/05/ci-deployment-and-environments/).
|
||||
- Learn [how to run jobs sequentially,
|
||||
in parallel, or build a custom pipeline](https://about.gitlab.com/blog/2016/07/29/the-basics-of-gitlab-ci/).
|
||||
- Use GitLab CI/CD `environments` to
|
||||
[deploy your web app to staging and production](https://about.gitlab.com/blog/2021/02/05/ci-deployment-and-environments/).
|
||||
- Learn how to run jobs
|
||||
[sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/blog/2016/07/29/the-basics-of-gitlab-ci/).
|
||||
- Learn [how to pull specific directories from different projects](https://about.gitlab.com/blog/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
|
||||
to deploy this website, <https://docs.gitlab.com>.
|
||||
- Learn [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/blog/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).
|
||||
|
|
|
@ -95,15 +95,15 @@ git push -o merge_request.create -o merge_request.target=my-target-branch -o mer
|
|||
## Useful Git aliases
|
||||
|
||||
As shown above, Git push options can cause Git commands to grow very long. If
|
||||
you use the same push options frequently, it's useful to create [Git
|
||||
aliases](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases). Git aliases
|
||||
you use the same push options frequently, it's useful to create
|
||||
[Git aliases](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases). Git aliases
|
||||
are command line shortcuts for Git which can significantly simplify the use of
|
||||
long Git commands.
|
||||
|
||||
### Merge when pipeline succeeds alias
|
||||
|
||||
To set up a Git alias for the [merge when pipeline succeeds Git push
|
||||
option](#push-options-for-merge-requests):
|
||||
To set up a Git alias for the
|
||||
[merge when pipeline succeeds Git push option](#push-options-for-merge-requests):
|
||||
|
||||
```shell
|
||||
git config --global alias.mwps "push -o merge_request.create -o merge_request.target=master -o merge_request.merge_when_pipeline_succeeds"
|
||||
|
|
|
@ -7,7 +7,6 @@ module Gitlab
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
EXPIRATION = 1.day
|
||||
PREVIOUS_EXPIRATION_PERIOD = 7.days
|
||||
VERSION = 2
|
||||
|
||||
delegate :diffable, to: :@diff_collection
|
||||
|
@ -75,28 +74,14 @@ module Gitlab
|
|||
Feature.enabled?(:use_marker_ranges, diffable.project),
|
||||
Feature.enabled?(:diff_line_syntax_highlighting, diffable.project)
|
||||
]
|
||||
options_for_key = OpenSSL::Digest::SHA256.hexdigest(options.join)
|
||||
|
||||
options_for_key =
|
||||
if Feature.enabled?(:highlight_diffs_optimize_memory_usage, diffable.project)
|
||||
[OpenSSL::Digest::SHA256.hexdigest(options.join)]
|
||||
else
|
||||
options
|
||||
end
|
||||
|
||||
['highlighted-diff-files', diffable.cache_key, VERSION, *options_for_key].join(":")
|
||||
['highlighted-diff-files', diffable.cache_key, VERSION, options_for_key].join(":")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expiration_period
|
||||
if Feature.enabled?(:highlight_diffs_optimize_memory_usage, diffable.project)
|
||||
EXPIRATION
|
||||
else
|
||||
PREVIOUS_EXPIRATION_PERIOD
|
||||
end
|
||||
end
|
||||
|
||||
def set_highlighted_diff_lines(diff_file, content)
|
||||
diff_file.highlighted_diff_lines = content.map do |line|
|
||||
Gitlab::Diff::Line.safe_init_from_hash(line)
|
||||
|
@ -153,7 +138,7 @@ module Gitlab
|
|||
|
||||
# HSETs have to have their expiration date manually updated
|
||||
#
|
||||
redis.expire(key, expiration_period)
|
||||
redis.expire(key, EXPIRATION)
|
||||
end
|
||||
|
||||
record_memory_usage(fetch_memory_usage(redis, key))
|
||||
|
|
|
@ -34571,6 +34571,12 @@ msgid_plural "SearchResults|wiki results"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "SearchToken|Assignee"
|
||||
msgstr ""
|
||||
|
||||
msgid "SearchToken|Reviewer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Searching by both author and message is currently not supported."
|
||||
msgstr ""
|
||||
|
||||
|
@ -45727,9 +45733,15 @@ msgstr ""
|
|||
msgid "ciReport|%{sameNum} same"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|%{scanner} detected no %{boldStart}new%{boldEnd} potential vulnerabilities"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|%{scanner} detected no %{strong_start}new%{strong_end} %{vulnStr}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::Ci::SecureFilesController do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
subject(:show_request) { get :show, params: { namespace_id: project.namespace, project_id: project } }
|
||||
|
||||
describe 'GET #show' do
|
||||
context 'when the :ci_secure_files feature flag is enabled' do
|
||||
context 'with enough privileges' do
|
||||
before do
|
||||
stub_feature_flags(ci_secure_files: true)
|
||||
sign_in(user)
|
||||
project.add_developer(user)
|
||||
show_request
|
||||
end
|
||||
|
||||
it { expect(response).to have_gitlab_http_status(:ok) }
|
||||
|
||||
it 'renders show page' do
|
||||
expect(response).to render_template :show
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the :ci_secure_files feature flag is disabled' do
|
||||
context 'with enough privileges' do
|
||||
before do
|
||||
stub_feature_flags(ci_secure_files: false)
|
||||
sign_in(user)
|
||||
project.add_developer(user)
|
||||
show_request
|
||||
end
|
||||
|
||||
it 'responds with 404' do
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without enough privileges' do
|
||||
before do
|
||||
sign_in(user)
|
||||
project.add_reporter(user)
|
||||
show_request
|
||||
end
|
||||
|
||||
it 'responds with 404' do
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
context 'an unauthenticated user' do
|
||||
before do
|
||||
show_request
|
||||
end
|
||||
|
||||
it 'redirects to sign in' do
|
||||
expect(response).to have_gitlab_http_status(:found)
|
||||
expect(response).to redirect_to('/users/sign_in')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Secure Files', :js do
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(ci_secure_files_read_only: false)
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'user sees the Secure Files list component' do
|
||||
visit project_ci_secure_files_path(project)
|
||||
expect(page).to have_content('There are no secure files yet.')
|
||||
end
|
||||
|
||||
it 'prompts the user to confirm before deleting a file' do
|
||||
file = create(:ci_secure_file, project: project)
|
||||
|
||||
visit project_ci_secure_files_path(project)
|
||||
|
||||
expect(page).to have_content(file.name)
|
||||
|
||||
find('button.btn-danger').click
|
||||
|
||||
expect(page).to have_content("Delete #{file.name}?")
|
||||
|
||||
click_on('Delete secure file')
|
||||
|
||||
visit project_ci_secure_files_path(project)
|
||||
|
||||
expect(page).not_to have_content(file.name)
|
||||
end
|
||||
|
||||
it 'displays an uploaded file in the file list' do
|
||||
visit project_ci_secure_files_path(project)
|
||||
expect(page).to have_content('There are no secure files yet.')
|
||||
|
||||
page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do
|
||||
click_button 'Upload File'
|
||||
end
|
||||
|
||||
expect(page).to have_content('upload-keystore.jks')
|
||||
end
|
||||
|
||||
it 'displays an error when a duplicate file upload is attempted' do
|
||||
create(:ci_secure_file, project: project, name: 'upload-keystore.jks')
|
||||
visit project_ci_secure_files_path(project)
|
||||
|
||||
expect(page).to have_content('upload-keystore.jks')
|
||||
|
||||
page.attach_file('spec/fixtures/ci_secure_files/upload-keystore.jks') do
|
||||
click_button 'Upload File'
|
||||
end
|
||||
|
||||
expect(page).to have_content('A file with this name already exists.')
|
||||
end
|
||||
end
|
|
@ -121,7 +121,7 @@ describe('gl_emoji', () => {
|
|||
window.gon.emoji_sprites_css_path = testPath;
|
||||
|
||||
expect(document.head.querySelector(`link[href="${testPath}"]`)).toBe(null);
|
||||
expect(window.gon.emoji_sprites_css_added).toBeFalsy();
|
||||
expect(window.gon.emoji_sprites_css_added).toBe(undefined);
|
||||
|
||||
markupToDomElement(
|
||||
'<gl-emoji data-fallback-sprite-class="emoji-bomb" data-name="bomb"></gl-emoji>',
|
||||
|
|
|
@ -402,7 +402,7 @@ describe('TrainingProviderList component', () => {
|
|||
|
||||
it('has disabled state for radio', () => {
|
||||
findPrimaryProviderRadios().wrappers.forEach((radio) => {
|
||||
expect(radio.attributes('disabled')).toBeTruthy();
|
||||
expect(radio.attributes('disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import App from '~/vue_merge_request_widget/components/widget/app.vue';
|
||||
|
||||
describe('MR Widget App', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMountExtended(App, {
|
||||
propsData: {
|
||||
mr: {},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
it('mounts the component', () => {
|
||||
createComponent();
|
||||
expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,134 @@
|
|||
import * as Sentry from '@sentry/browser';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import Widget from '~/vue_merge_request_widget/components/widget/widget.vue';
|
||||
|
||||
describe('MR Widget', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = ({ propsData, slots } = {}) => {
|
||||
wrapper = shallowMountExtended(Widget, {
|
||||
propsData: {
|
||||
loadingText: 'Loading widget',
|
||||
value: {
|
||||
collapsed: null,
|
||||
expanded: null,
|
||||
},
|
||||
...propsData,
|
||||
},
|
||||
slots,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe('on mount', () => {
|
||||
it('fetches collapsed', async () => {
|
||||
const fetchCollapsedData = jest
|
||||
.fn()
|
||||
.mockReturnValue(Promise.resolve({ headers: {}, status: 200, data: {} }));
|
||||
|
||||
createComponent({ propsData: { fetchCollapsedData } });
|
||||
await waitForPromises();
|
||||
expect(fetchCollapsedData).toHaveBeenCalled();
|
||||
expect(wrapper.vm.error).toBe(null);
|
||||
});
|
||||
|
||||
it('sets the error text when fetch method fails', async () => {
|
||||
const fetchCollapsedData = jest.fn().mockReturnValue(() => Promise.reject());
|
||||
createComponent({ propsData: { fetchCollapsedData } });
|
||||
await waitForPromises();
|
||||
expect(wrapper.vm.error).toBe('Failed to load');
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetch', () => {
|
||||
it('sets the data.collapsed property after a successfull call - multiPolling: false', async () => {
|
||||
const mockData = { headers: {}, status: 200, data: { vulnerabilities: [] } };
|
||||
createComponent({ propsData: { fetchCollapsedData: async () => mockData } });
|
||||
await waitForPromises();
|
||||
expect(wrapper.emitted('input')[0][0]).toEqual({ collapsed: mockData.data, expanded: null });
|
||||
});
|
||||
|
||||
it('sets the data.collapsed property after a successfull call - multiPolling: true', async () => {
|
||||
const mockData1 = { headers: {}, status: 200, data: { vulnerabilities: [{ vuln: 1 }] } };
|
||||
const mockData2 = { headers: {}, status: 200, data: { vulnerabilities: [{ vuln: 2 }] } };
|
||||
|
||||
createComponent({
|
||||
propsData: {
|
||||
multiPolling: true,
|
||||
fetchCollapsedData: () => [
|
||||
() => Promise.resolve(mockData1),
|
||||
() => Promise.resolve(mockData2),
|
||||
],
|
||||
},
|
||||
});
|
||||
await waitForPromises();
|
||||
await waitForPromises();
|
||||
expect(wrapper.emitted('input')[0][0]).toEqual({
|
||||
collapsed: [mockData1.data, mockData2.data],
|
||||
expanded: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('calls sentry when failed', async () => {
|
||||
const error = new Error('Something went wrong');
|
||||
jest.spyOn(Sentry, 'captureException').mockImplementation();
|
||||
createComponent({
|
||||
propsData: {
|
||||
fetchCollapsedData: async () => Promise.reject(error),
|
||||
},
|
||||
});
|
||||
await waitForPromises();
|
||||
expect(wrapper.emitted('input')).toBeUndefined();
|
||||
expect(Sentry.captureException).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('content', () => {
|
||||
it('displays summary property when summary slot is not provided', () => {
|
||||
createComponent({
|
||||
propsData: {
|
||||
summary: 'Hello world',
|
||||
fetchCollapsedData: async () => Promise.resolve(),
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.findByTestId('widget-extension-top-level-summary').text()).toBe('Hello world');
|
||||
});
|
||||
|
||||
it.todo('displays content property when content slot is not provided');
|
||||
|
||||
it('displays the summary slot when provided', () => {
|
||||
createComponent({
|
||||
propsData: {
|
||||
fetchCollapsedData: async () => Promise.resolve(),
|
||||
},
|
||||
slots: {
|
||||
summary: '<b>More complex summary</b>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.findByTestId('widget-extension-top-level-summary').text()).toBe(
|
||||
'More complex summary',
|
||||
);
|
||||
});
|
||||
|
||||
it('displays the content slot when provided', () => {
|
||||
createComponent({
|
||||
propsData: {
|
||||
fetchCollapsedData: async () => Promise.resolve(),
|
||||
},
|
||||
slots: {
|
||||
content: '<b>More complex content</b>',
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.findByTestId('widget-extension-collapsed-section').text()).toBe(
|
||||
'More complex content',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -45,7 +45,7 @@ describe('vue_shared/components/dismissible_alert', () => {
|
|||
});
|
||||
|
||||
it('emmits alertDismissed', () => {
|
||||
expect(wrapper.emitted('alertDismissed')).toBeTruthy();
|
||||
expect(wrapper.emitted()).toHaveProperty('alertDismissed');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -127,20 +127,6 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
|
|||
|
||||
cache.write_if_empty
|
||||
end
|
||||
|
||||
context 'when highlight_diffs_optimize_memory_usage is disabled' do
|
||||
before do
|
||||
stub_feature_flags(highlight_diffs_optimize_memory_usage: false)
|
||||
end
|
||||
|
||||
it 'sets the previous expiration period' do
|
||||
Gitlab::Redis::Cache.with do |redis|
|
||||
expect(redis).to receive(:expire).with(cache.key, described_class::PREVIOUS_EXPIRATION_PERIOD)
|
||||
end
|
||||
|
||||
cache.write_if_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#write_if_empty' do
|
||||
|
@ -304,15 +290,5 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
|
|||
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{options_hash([cache.diff_options, true, false])}")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when highlight_diffs_optimize_memory_usage is disabled' do
|
||||
before do
|
||||
stub_feature_flags(highlight_diffs_optimize_memory_usage: false)
|
||||
end
|
||||
|
||||
it 'uses the options hash as a part of the cache key' do
|
||||
is_expected.to eq("highlighted-diff-files:#{cache.diffable.cache_key}:2:#{cache.diff_options}:true:true")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -953,12 +953,6 @@ RSpec.describe 'project routing' do
|
|||
end
|
||||
end
|
||||
|
||||
describe Projects::Ci::SecureFilesController, 'routing' do
|
||||
it 'to #show' do
|
||||
expect(get('/gitlab/gitlabhq/-/ci/secure_files')).to route_to('projects/ci/secure_files#show', namespace_id: 'gitlab', project_id: 'gitlabhq')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a non-existent project' do
|
||||
it 'routes to 404 with get request' do
|
||||
expect(get: "/gitlab/not_exist").to route_to(
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
|
||||
subject { described_class.new(**service_args).execute }
|
||||
subject(:execute) { described_class.new(**service_args).execute }
|
||||
|
||||
let(:service_args) { { runners: runners_arg } }
|
||||
let(:runners_arg) { }
|
||||
|
@ -17,7 +17,8 @@ RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
|
|||
it 'destroys runners', :aggregate_failures do
|
||||
expect { subject }.to change { Ci::Runner.count }.by(-2)
|
||||
|
||||
is_expected.to eq({ deleted_count: 2, deleted_ids: [instance_runner.id, project_runner.id] })
|
||||
is_expected.to be_success
|
||||
expect(execute.payload).to eq({ deleted_count: 2, deleted_ids: [instance_runner.id, project_runner.id] })
|
||||
expect(instance_runner[:errors]).to be_nil
|
||||
expect(project_runner[:errors]).to be_nil
|
||||
expect { project_runner.runner_projects.first.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
|
@ -36,7 +37,8 @@ RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
|
|||
it 'destroys runners and returns only deleted runners', :aggregate_failures do
|
||||
expect { subject }.to change { Ci::Runner.count }.by(-1)
|
||||
|
||||
is_expected.to eq({ deleted_count: 1, deleted_ids: [project_runner.id] })
|
||||
is_expected.to be_success
|
||||
expect(execute.payload).to eq({ deleted_count: 1, deleted_ids: [project_runner.id] })
|
||||
expect(instance_runner[:errors]).to be_nil
|
||||
expect(project_runner[:errors]).to be_nil
|
||||
expect { project_runner.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
|
@ -51,7 +53,8 @@ RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
|
|||
it 'deletes only first RUNNER_LIMIT runners' do
|
||||
expect { subject }.to change { Ci::Runner.count }.by(-1)
|
||||
|
||||
is_expected.to eq({ deleted_count: 1, deleted_ids: [instance_runner.id] })
|
||||
is_expected.to be_success
|
||||
expect(execute.payload).to eq({ deleted_count: 1, deleted_ids: [instance_runner.id] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -72,7 +75,8 @@ RSpec.describe ::Ci::Runners::BulkDeleteRunnersService, '#execute' do
|
|||
let(:runners_arg) { nil }
|
||||
|
||||
it 'returns 0 deleted runners' do
|
||||
is_expected.to eq({ deleted_count: 0, deleted_ids: [] })
|
||||
is_expected.to be_success
|
||||
expect(execute.payload).to eq({ deleted_count: 0, deleted_ids: [] })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue