Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-02-11 00:08:50 +00:00
parent 02c3b2af44
commit ced6c9ae9a
54 changed files with 480 additions and 229 deletions

View File

@ -3,7 +3,6 @@
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlButton } from '@gitlab/ui';
import NoteableNote from '~/notes/components/noteable_note.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import PublishButton from './publish_button.vue';
export default {
@ -12,7 +11,6 @@ export default {
PublishButton,
GlButton,
},
mixins: [glFeatureFlagsMixin()],
props: {
draft: {
type: Object,
@ -63,14 +61,14 @@ export default {
this.isEditingDraft = false;
},
handleMouseEnter(draft) {
if (this.glFeatures.multilineComments && draft.position) {
if (draft.position) {
this.setSelectedCommentPositionHover(draft.position.line_range);
}
},
handleMouseLeave(draft) {
// Even though position isn't used here we still don't want to unecessarily call a mutation
// Even though position isn't used here we still don't want to unnecessarily call a mutation
// The lack of position tells us that highlighting is irrelevant in this context
if (this.glFeatures.multilineComments && draft.position) {
if (draft.position) {
this.setSelectedCommentPositionHover();
}
},

View File

@ -3,7 +3,6 @@ import { mapGetters } from 'vuex';
import { GlSprintf, GlIcon } from '@gitlab/ui';
import { IMAGE_DIFF_POSITION_TYPE } from '~/diffs/constants';
import { sprintf, __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
getStartLineNumber,
getEndLineNumber,
@ -16,7 +15,7 @@ export default {
GlIcon,
GlSprintf,
},
mixins: [resolvedStatusMixin, glFeatureFlagsMixin()],
mixins: [resolvedStatusMixin],
props: {
draft: {
type: Object,
@ -71,6 +70,10 @@ export default {
return this.draft.position || this.discussion.position;
},
startLineNumber() {
if (this.position?.position_type === IMAGE_DIFF_POSITION_TYPE) {
// eslint-disable-next-line @gitlab/require-i18n-strings
return `${this.position.x}x ${this.position.y}y`;
}
return getStartLineNumber(this.position?.line_range);
},
endLineNumber() {
@ -90,16 +93,12 @@ export default {
<span>
<span class="review-preview-item-header">
<gl-icon class="flex-shrink-0" :name="iconName" />
<span
class="bold text-nowrap"
:class="{ 'gl-align-items-center': glFeatures.multilineComments }"
>
<span class="bold text-nowrap gl-align-items-center">
<span class="review-preview-item-header-text block-truncated">
{{ titleText }}
</span>
<template v-if="showLinePosition">
<template v-if="!glFeatures.multilineComments">:{{ linePosition }}</template>
<template v-else-if="startLineNumber === endLineNumber">
<template v-if="startLineNumber === endLineNumber">
:<span :class="getLineClasses(startLineNumber)">{{ startLineNumber }}</span>
</template>
<gl-sprintf v-else :message="__(':%{startLine} to %{endLine}')">

View File

@ -165,7 +165,7 @@ export default {
<template>
<div class="content discussion-form discussion-form-container discussion-notes">
<div v-if="glFeatures.multilineComments" class="gl-mb-3 gl-text-gray-500 gl-pb-3">
<div class="gl-mb-3 gl-text-gray-500 gl-pb-3">
<multiline-comment-form
v-model="commentLineStart"
:line="line"

View File

@ -4,7 +4,6 @@ import { __ } from '~/locale';
import PlaceholderNote from '~/vue_shared/components/notes/placeholder_note.vue';
import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { SYSTEM_NOTE } from '../constants';
import NoteableNote from './noteable_note.vue';
import ToggleRepliesWidget from './toggle_replies_widget.vue';
@ -18,7 +17,6 @@ export default {
NoteEditedText,
DiscussionNotesRepliesWrapper,
},
mixins: [glFeatureFlagsMixin()],
props: {
discussion: {
type: Object,
@ -96,14 +94,14 @@ export default {
return note.isPlaceholderNote ? note.notes[0] : note;
},
handleMouseEnter(discussion) {
if (this.glFeatures.multilineComments && discussion.position) {
if (discussion.position) {
this.setSelectedCommentPositionHover(discussion.position.line_range);
}
},
handleMouseLeave(discussion) {
// Even though position isn't used here we still don't want to unecessarily call a mutation
// Even though position isn't used here we still don't want to unnecessarily call a mutation
// The lack of position tells us that highlighting is irrelevant in this context
if (this.glFeatures.multilineComments && discussion.position) {
if (discussion.position) {
this.setSelectedCommentPositionHover();
}
},

View File

@ -3,7 +3,6 @@ import $ from 'jquery';
import { mapGetters, mapActions } from 'vuex';
import { escape } from 'lodash';
import { GlSprintf, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { truncateSha } from '~/lib/utils/text_utility';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import httpStatusCodes from '~/lib/utils/http_status';
@ -38,7 +37,7 @@ export default {
directives: {
SafeHtml,
},
mixins: [noteable, resolvable, glFeatureFlagsMixin()],
mixins: [noteable, resolvable],
props: {
note: {
type: Object,
@ -160,7 +159,6 @@ export default {
},
showMultiLineComment() {
if (
!this.glFeatures.multilineComments ||
!this.discussionRoot ||
this.startLineNumber.length === 0 ||
this.endLineNumber.length === 0

View File

@ -1,4 +1,4 @@
import { __ } from '~/locale';
import { s__ } from '~/locale';
import { deprecatedCreateFlash as flash } from '../flash';
import axios from '../lib/utils/axios_utils';
import ProtectedTagAccessDropdown from './protected_tag_access_dropdown';
@ -48,11 +48,8 @@ export default class ProtectedTagEdit {
.catch(() => {
this.$allowedToCreateDropdownButton.enable();
flash(
__('Failed to update tag!'),
'alert',
document.querySelector('.js-protected-tags-list'),
);
window.scrollTo({ top: 0, behavior: 'smooth' });
flash(s__('ProjectSettings|Failed to update tag!'));
});
}
}

View File

@ -69,9 +69,6 @@ table {
}
}
td {
border-color: $white-normal;
}
}
.thead-white {

View File

@ -30,7 +30,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do
push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true)
push_frontend_feature_flag(:file_identifier_hash)
push_frontend_feature_flag(:batch_suggestions, @project, default_enabled: true)
push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)

View File

@ -7,6 +7,7 @@ module Ci
ArtifactsExistError = Class.new(StandardError)
LSIF_ARTIFACT_TYPE = 'lsif'
METRICS_REPORT_UPLOAD_EVENT_NAME = 'i_testing_metrics_report_artifact_uploaders'
OBJECT_STORAGE_ERRORS = [
Errno::EIO,
@ -42,6 +43,8 @@ module Ci
artifact, artifact_metadata = build_artifact(artifacts_file, params, metadata_file)
result = parse_artifact(artifact)
track_artifact_uploader(artifact)
return result unless result[:status] == :success
persist_artifact(artifact, artifact_metadata, params)
@ -152,6 +155,12 @@ module Ci
)
end
def track_artifact_uploader(artifact)
return unless artifact.file_type == 'metrics'
track_usage_event(METRICS_REPORT_UPLOAD_EVENT_NAME, @job.user_id)
end
def parse_dotenv_artifact(artifact)
Ci::ParseDotenvArtifactService.new(project, current_user).execute(artifact)
end

View File

@ -23,7 +23,6 @@
%th
%tbody
%tr
%td.flash-container{ colspan: 4 }
= yield
= paginate @protected_tags, theme: 'gitlab'

View File

@ -0,0 +1,5 @@
---
title: Fix protected branches/tags border
merge_request: 52816
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: 'Improve Vulnerability Tracking: Add fingerprints table'
merge_request: 52720
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Update Ruby detection rules for SAST
merge_request: 53414
author:
type: changed

View File

@ -1,8 +0,0 @@
---
name: multiline_comments
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37114
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/211255
milestone: '13.2'
type: development
group: group::code review
default_enabled: true

View File

@ -0,0 +1,8 @@
---
name: usage_data_i_testing_metrics_report_artifact_uploaders
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51670
rollout_issue_url:
milestone: '13.9'
type: development
group: group::testing
default_enabled: true

View File

@ -2,3 +2,4 @@
filenames:
- ee/app/assets/javascripts/on_demand_scans/graphql/dast_scan_create.mutation.graphql
- ee/app/assets/javascripts/oncall_schedules/graphql/mutations/update_oncall_schedule_rotation.mutation.graphql
- ee/app/assets/javascripts/security_configuration/api_fuzzing/graphql/api_fuzzing_ci_configuration.query.graphql

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
class AddFindingFingerprintTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
FINGERPRINT_IDX = :idx_vuln_fingerprints_on_occurrences_id_and_fingerprint
UNIQ_IDX = :idx_vuln_fingerprints_uniqueness
def up
with_lock_retries do
create_table :vulnerability_finding_fingerprints do |t|
t.references :finding,
index: true,
null: false,
foreign_key: { to_table: :vulnerability_occurrences, column: :finding_id, on_delete: :cascade }
t.timestamps_with_timezone null: false
t.integer :algorithm_type, null: false
t.binary :fingerprint_sha256, null: false
t.index %i[finding_id fingerprint_sha256],
name: FINGERPRINT_IDX,
unique: true # only one link should exist between occurrence and the fingerprint
t.index %i[finding_id algorithm_type fingerprint_sha256],
name: UNIQ_IDX,
unique: true # these should be unique
end
end
end
def down
with_lock_retries do
drop_table :vulnerability_finding_fingerprints
end
end
end

View File

@ -3,6 +3,7 @@
class CreateVulnerabilityFindingsRemediationsJoinTable < ActiveRecord::Migration[6.0]
DOWNTIME = false
# rubocop:disable Migration/CreateTableWithForeignKeys
def change
create_table :vulnerability_findings_remediations do |t|
t.references :vulnerability_occurrence, index: false, foreign_key: { on_delete: :cascade }
@ -13,4 +14,5 @@ class CreateVulnerabilityFindingsRemediationsJoinTable < ActiveRecord::Migration
t.index [:vulnerability_occurrence_id, :vulnerability_remediation_id], unique: true, name: 'index_vulnerability_findings_remediations_on_unique_keys'
end
end
# rubocop:enable Migration/CreateTableWithForeignKeys
end

View File

@ -0,0 +1 @@
6643e5b4c5597d92c94115f392bfbd5cfce9884eb0bcb18f9629855f3711eed0

View File

@ -18141,6 +18141,24 @@ CREATE SEQUENCE vulnerability_feedback_id_seq
ALTER SEQUENCE vulnerability_feedback_id_seq OWNED BY vulnerability_feedback.id;
CREATE TABLE vulnerability_finding_fingerprints (
id bigint NOT NULL,
finding_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
algorithm_type integer NOT NULL,
fingerprint_sha256 bytea NOT NULL
);
CREATE SEQUENCE vulnerability_finding_fingerprints_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE vulnerability_finding_fingerprints_id_seq OWNED BY vulnerability_finding_fingerprints.id;
CREATE TABLE vulnerability_finding_links (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@ -19382,6 +19400,8 @@ ALTER TABLE ONLY vulnerability_external_issue_links ALTER COLUMN id SET DEFAULT
ALTER TABLE ONLY vulnerability_feedback ALTER COLUMN id SET DEFAULT nextval('vulnerability_feedback_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_finding_fingerprints ALTER COLUMN id SET DEFAULT nextval('vulnerability_finding_fingerprints_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_finding_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_finding_links_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_findings_remediations ALTER COLUMN id SET DEFAULT nextval('vulnerability_findings_remediations_id_seq'::regclass);
@ -20957,6 +20977,9 @@ ALTER TABLE ONLY vulnerability_external_issue_links
ALTER TABLE ONLY vulnerability_feedback
ADD CONSTRAINT vulnerability_feedback_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_finding_fingerprints
ADD CONSTRAINT vulnerability_finding_fingerprints_pkey PRIMARY KEY (id);
ALTER TABLE ONLY vulnerability_finding_links
ADD CONSTRAINT vulnerability_finding_links_pkey PRIMARY KEY (id);
@ -21294,6 +21317,10 @@ CREATE INDEX idx_security_scans_on_scan_type ON security_scans USING btree (scan
CREATE UNIQUE INDEX idx_serverless_domain_cluster_on_clusters_applications_knative ON serverless_domain_cluster USING btree (clusters_applications_knative_id);
CREATE UNIQUE INDEX idx_vuln_fingerprints_on_occurrences_id_and_fingerprint ON vulnerability_finding_fingerprints USING btree (finding_id, fingerprint_sha256);
CREATE UNIQUE INDEX idx_vuln_fingerprints_uniqueness ON vulnerability_finding_fingerprints USING btree (finding_id, algorithm_type, fingerprint_sha256);
CREATE UNIQUE INDEX idx_vulnerability_ext_issue_links_on_vulne_id_and_ext_issue ON vulnerability_external_issue_links USING btree (vulnerability_id, external_type, external_project_key, external_issue_key);
CREATE UNIQUE INDEX idx_vulnerability_ext_issue_links_on_vulne_id_and_link_type ON vulnerability_external_issue_links USING btree (vulnerability_id, link_type) WHERE (link_type = 1);
@ -23596,6 +23623,8 @@ CREATE INDEX index_vulnerability_feedback_on_merge_request_id ON vulnerability_f
CREATE INDEX index_vulnerability_feedback_on_pipeline_id ON vulnerability_feedback USING btree (pipeline_id);
CREATE INDEX index_vulnerability_finding_fingerprints_on_finding_id ON vulnerability_finding_fingerprints USING btree (finding_id);
CREATE INDEX index_vulnerability_findings_remediations_on_remediation_id ON vulnerability_findings_remediations USING btree (vulnerability_remediation_id);
CREATE UNIQUE INDEX index_vulnerability_findings_remediations_on_unique_keys ON vulnerability_findings_remediations USING btree (vulnerability_occurrence_id, vulnerability_remediation_id);
@ -26119,6 +26148,9 @@ ALTER TABLE ONLY merge_trains
ALTER TABLE ONLY ci_runner_namespaces
ADD CONSTRAINT fk_rails_f9d9ed3308 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_finding_fingerprints
ADD CONSTRAINT fk_rails_fa411253b2 FOREIGN KEY (finding_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
ALTER TABLE ONLY requirements_management_test_reports
ADD CONSTRAINT fk_rails_fb3308ad55 FOREIGN KEY (requirement_id) REFERENCES requirements(id) ON DELETE CASCADE;

View File

@ -814,6 +814,39 @@ Though the name of the Prometheus metric contains `rate_limiting`, it is a concu
a rate limiter. If a Gitaly client makes 1000 requests in a row very quickly, concurrency does not
exceed 1 and the concurrency limiter has no effect.
## Background Repository Optimization
Empty directories and unneeded config settings may accumulate in a repository and
slow down Git operations. Gitaly can schedule a daily background task with a maximum duration
to clean up these items and improve performance.
WARNING:
This is an experimental feature and may place significant load on the host while running.
Make sure to schedule this during off-peak hours and keep the duration short (for example, 30-60 minutes).
**For Omnibus GitLab**
Edit `/etc/gitlab/gitlab.rb` and add:
```ruby
gitaly['daily_maintenance_start_hour'] = 4
gitaly['daily_maintenance_start_minute'] = 30
gitaly['daily_maintenance_duration'] = '30m'
gitaly['daily_maintenance_storages'] = ["default"]
```
**For installations from source**
Edit `/home/git/gitaly/config.toml` and add:
```toml
[daily_maintenance]
start_hour = 4
start_minute = 30
duration = '30m'
storages = ["default"]
```
## Rotate Gitaly authentication token
Rotating credentials in a production environment often requires downtime, causes outages, or both.

View File

@ -1111,7 +1111,7 @@ replication factor offers better redundancy and distribution of read workload, b
in a higher storage cost. By default, Praefect replicates repositories to every storage in a
virtual storage.
### Variable replication factor
### Configure replication factors
WARNING:
The feature is not production ready yet. After you set a replication factor, you can't unset it
@ -1122,36 +1122,50 @@ strategy is not production ready yet.
Praefect supports configuring a replication factor on a per-repository basis, by assigning
specific storage nodes to host a repository.
[In an upcoming release](https://gitlab.com/gitlab-org/gitaly/-/issues/3362), we intend to
support configuring a default replication factor for a virtual storage. The default replication factor
is applied to every newly-created repository.
Praefect does not store the actual replication factor, but assigns enough storages to host the repository
so the desired replication factor is met. If a storage node is later removed from the virtual storage,
the replication factor of repositories assigned to the storage is decreased accordingly.
The only way to configure a repository's replication factor is the `set-replication-factor`
sub-command. `set-replication-factor` automatically assigns or unassigns random storage nodes as necessary to
reach the desired replication factor. The repository's primary node is always assigned
first and is never unassigned.
You can configure:
```shell
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage <virtual-storage> -repository <relative-path> -replication-factor <replication-factor>
```
- A default replication factor for each virtual storage that is applied to newly-created repositories.
The configuration is added to the `/etc/gitlab/gitlab.rb` file:
- `-virtual-storage` is the virtual storage the repository is located in.
- `-repository` is the repository's relative path in the storage.
- `-replication-factor` is the desired replication factor of the repository. The minimum value is
`1`, as the primary needs a copy of the repository. The maximum replication factor is the number of
storages in the virtual storage.
```ruby
praefect['virtual_storages'] = {
'default' => {
'default_replication_factor' => 1,
# nodes...
'gitaly-1' => {
'address' => 'tcp://GITALY_HOST:8075',
'token' => 'PRAEFECT_INTERNAL_TOKEN',
},
}
}
```
On success, the assigned host storages are printed. For example:
- A replication factor for an existing repository using the `set-replication-factor` sub-command.
`set-replication-factor` automatically assigns or unassigns random storage nodes as
necessary to reach the desired replication factor. The repository's primary node is
always assigned first and is never unassigned.
```shell
$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage default -repository @hashed/3f/db/3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278.git -replication-factor 2
```shell
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage <virtual-storage> -repository <relative-path> -replication-factor <replication-factor>
```
current assignments: gitaly-1, gitaly-2
```
- `-virtual-storage` is the virtual storage the repository is located in.
- `-repository` is the repository's relative path in the storage.
- `-replication-factor` is the desired replication factor of the repository. The minimum value is
`1`, as the primary needs a copy of the repository. The maximum replication factor is the number of
storages in the virtual storage.
On success, the assigned host storages are printed. For example:
```shell
$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml set-replication-factor -virtual-storage default -repository @hashed/3f/db/3fdba35f04dc8c462986c992bcf875546257113072a909c162f7e470e581e278.git -replication-factor 2
current assignments: gitaly-1, gitaly-2
```
## Automatic failover and leader election

View File

@ -413,11 +413,14 @@ For example:
## Sidekiq Logs
NOTE:
In Omnibus GitLab `12.10` or earlier, the Sidekiq log lives in `/var/log/gitlab/gitlab-rails/sidekiq.log`.
For Omnibus installations, some Sidekiq logs reside in `/var/log/gitlab/sidekiq/current` and as follows.
### `sidekiq.log`
This file lives in `/var/log/gitlab/gitlab-rails/sidekiq.log` for
This file lives in `/var/log/gitlab/sidekiq/current` for
Omnibus GitLab packages or in `/home/git/gitlab/log/sidekiq.log` for
installations from source.

View File

@ -35,14 +35,12 @@ To request access to ChatOps on GitLab.com:
in the `#chat-ops-test` Slack channel, replacing `<username>` with your username:
`/chatops run member add <username> gitlab-com/chatops --ops`
<!-- vale gitlab.FirstPerson = NO -->
> Hi `__BUDDY_HANDLE__` and `__MANAGER_HANDLE__`, could you please add me to
> the ChatOps project in Ops by running this command:
> `/chatops run member add <username> gitlab-com/chatops --ops` in the
> `#chat-ops-test` Slack channel? Thanks in advance.
<!-- vale gitlab.FirstPerson = YES -->
```plaintext
Hi <__BUDDY_HANDLE__> and <__MANAGER_HANDLE__>, could you please add me to
the ChatOps project in Ops by running this command:
`/chatops run member add <username> gitlab-com/chatops --ops` in the
`#chat-ops-test` Slack channel? Thanks in advance.
```
1. Ensure you've set up two-factor authentication.
1. After you're added to the ChatOps project, run this command to check your user

View File

@ -283,10 +283,7 @@ first time.
you forget to remove any debugging code?
- Consider providing instructions on how to test the merge request. This can be
helpful for reviewers not familiar with the product feature or area of the codebase.
<!-- vale gitlab.FutureTense = NO -->
- Be grateful for the reviewer's suggestions. ("Good call. I'll make that
change.")
<!-- vale gitlab.FutureTense = YES -->
- Be grateful for the reviewer's suggestions. (`Good call. I'll make that change.`)
- Don't take it personally. The review is of the code, not of you.
- Explain why the code exists. ("It's like that because of these reasons. Would
it be more clear if I rename this class/file/method/variable?")

View File

@ -742,8 +742,6 @@ Items nested in lists should always align with the first character of the list
item. In unordered lists (using `-`), this means two spaces for each level of
indentation:
<!-- vale off -->
````markdown
- Unordered list item 1
@ -765,12 +763,8 @@ indentation:
![an image that will nest inside list item 4](image.png)
````
<!-- vale on -->
For ordered lists, use three spaces for each level of indentation:
<!-- vale off -->
````markdown
1. Ordered list item 1
@ -792,8 +786,6 @@ For ordered lists, use three spaces for each level of indentation:
![an image that will nest inside list item 4](image.png)
````
<!-- vale on -->
You can nest full lists inside other lists using the same rules as above. If you
want to mix types, that's also possible, if you don't mix items at the same
level:
@ -1324,8 +1316,6 @@ hidden on the documentation site, but is displayed by `/help`.
- For regular fenced code blocks, always use a highlighting class corresponding to
the language for better readability. Examples:
<!-- vale off -->
````markdown
```ruby
Ruby code
@ -1344,8 +1334,6 @@ hidden on the documentation site, but is displayed by `/help`.
```
````
<!-- vale on -->
Syntax highlighting is required for fenced code blocks added to the GitLab
documentation. Refer to this table for the most common language classes,
or check the [complete list](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers)
@ -1834,8 +1822,6 @@ Configuration procedures can require users to edit configuration files, reconfig
GitLab, or restart GitLab. Use these styles to document these steps, replacing
`PATH/TO` with the appropriate path:
<!-- vale off -->
````markdown
**For Omnibus installations**
@ -1863,8 +1849,6 @@ GitLab, or restart GitLab. Use these styles to document these steps, replacing
GitLab for the changes to take effect.
````
<!-- vale on -->
In this case:
- Bold the installation method's name.

View File

@ -26,15 +26,11 @@ To use a sprite Icon in HAML or Rails we use a specific helper function:
sprite_icon(icon_name, size: nil, css_class: '')
```
<!-- vale gitlab.Spelling = NO -->
- **icon_name**: Use the icon_name for the SVG sprite in the list of
- **`icon_name`**: Use the `icon_name` for the SVG sprite in the list of
([GitLab SVGs](https://gitlab-org.gitlab.io/gitlab-svgs)).
- **size (optional)**: Use one of the following sizes : 16, 24, 32, 48, 72 (this
- **`size` (optional)**: Use one of the following sizes : 16, 24, 32, 48, 72 (this
is translated into a `s16` class)
- **css_class (optional)**: If you want to add additional CSS classes.
<!-- vale gitlab.Spelling = YES -->
- **`css_class` (optional)**: If you want to add additional CSS classes.
**Example**

View File

@ -161,6 +161,7 @@ to be consider, but may be preferable depending on your use case.
--locked="false" \
--access-level="not_protected" \
--docker-volumes "/cache"\
--docker-volumes "/builds:/builds"\
--docker-volumes "/var/run/docker.sock:/var/run/docker.sock" \
--registration-token="<project_token>" \
--non-interactive
@ -173,8 +174,8 @@ to be consider, but may be preferable depending on your use case.
in the previous step.
```shell
--builds-dir /tmp/builds
--docker-volumes /tmp/builds:/tmp/builds
--builds-dir "/tmp/builds"
--docker-volumes "/tmp/builds:/tmp/builds" # Use this instead of --docker-volumes "/builds:/builds"
```
The resulting configuration:
@ -456,3 +457,32 @@ This can be due to multiple reasons:
GitLab only uses the Code Quality artifact from the latest created job (with the largest job ID).
If multiple jobs in a pipeline generate a code quality artifact, those of earlier jobs are ignored.
To avoid confusion, configure only one job to generate a `gl-code-quality-report.json`.
### Rubocop errors
When using Code Quality jobs on a **Ruby** project, you can encounter problems running Rubocop.
For example, the following error can appear when using either a very recent or very old version
of Ruby:
```plaintext
/usr/local/bundle/gems/rubocop-0.52.1/lib/rubocop/config.rb:510:in `check_target_ruby':
Unknown Ruby version 2.7 found in `.ruby-version`. (RuboCop::ValidationError)
Supported versions: 2.1, 2.2, 2.3, 2.4, 2.5
```
This is caused by the default version of **rubocop** used by the check engine not covering
support for the Ruby version in use.
To use a custom version of **rubocop** that
[supports the version of Ruby used by the project](https://docs.rubocop.org/rubocop/compatibility.html#support-matrix),
you can [override the configuration through a `.codeclimate.yml` file](https://docs.codeclimate.com/docs/rubocop#using-rubocops-newer-versions)
created in the project repository.
For example, to specify using **rubocop** release **0.67**:
```yaml
version: "2"
plugins:
rubocop:
enabled: true
channel: rubocop-0-67
```

View File

@ -195,12 +195,7 @@ to expand the diff lines and leave a comment, just as you would for a changed li
### Commenting on multiple lines
> - [Introduced](https://gitlab.com/gitlab-org/ux-research/-/issues/870) in GitLab 13.2.
> - It's deployed behind a feature flag, enabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/221268) on GitLab 13.3.
> - It's enabled on GitLab.com.
> - It can be disabled or enabled per-project.
> - It's recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-multiline-comments). **(FREE SELF)**
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/299121) in GitLab 13.9.
GitLab provides a way to select which lines of code a comment refers to. After starting a comment
a dropdown selector is shown to select the first line that this comment refers to.
@ -216,25 +211,6 @@ above it.
![Multiline comment selection displayed above comment](img/multiline-comment-saved.png)
### Enable or disable multiline comments **(FREE SELF)**
The multiline comments feature is under development but ready for production use.
It is deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can opt to enable it for your instance.
To disable it:
```ruby
Feature.disable(:multiline_comments)
```
To enable it:
```ruby
Feature.enable(:multiline_comments)
```
## Pipeline status in merge requests widgets
If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,

View File

@ -66,7 +66,8 @@ brakeman-sast:
- if: $CI_COMMIT_BRANCH &&
$SAST_DEFAULT_ANALYZERS =~ /brakeman/
exists:
- 'config/routes.rb'
- '**/*.rb'
- '**/Gemfile'
eslint-sast:
extends: .sast-analyzer

View File

@ -278,6 +278,11 @@
redis_slot: testing
aggregation: weekly
feature_flag: usage_data_i_testing_load_performance_widget_total
- name: i_testing_metrics_report_artifact_uploaders
category: testing
redis_slot: testing
aggregation: weekly
feature_flag: usage_data_i_testing_metrics_report_artifact_uploaders
# Project Management group
- name: g_project_management_issue_title_changed
category: issues_edit

View File

@ -1393,6 +1393,90 @@ msgstr ""
msgid "API Token"
msgstr ""
msgid "APIFuzzing|API Fuzzing Configuration"
msgstr ""
msgid "APIFuzzing|Authentication is handled by providing HTTP basic authentication token as a header or cookie. %{linkStart}More information%{linkEnd}."
msgstr ""
msgid "APIFuzzing|Base URL of API fuzzing target."
msgstr ""
msgid "APIFuzzing|Choose a method"
msgstr ""
msgid "APIFuzzing|Choose a profile"
msgstr ""
msgid "APIFuzzing|Customize common API fuzzing settings to suit your requirements. For details of more advanced configuration options, see the %{docsLinkStart}GitLab API Fuzzing documentation%{docsLinkEnd}."
msgstr ""
msgid "APIFuzzing|Enable authentication"
msgstr ""
msgid "APIFuzzing|Ex: $TestPassword"
msgstr ""
msgid "APIFuzzing|Ex: $TestUsername"
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.har"
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.json"
msgstr ""
msgid "APIFuzzing|Generate code snippet"
msgstr ""
msgid "APIFuzzing|HAR files may contain sensitive information such as authentication tokens, API keys, and session cookies. We recommend that you review the HAR files' contents before adding them to a repository."
msgstr ""
msgid "APIFuzzing|Instead of entering the password directly, enter the key of the CI variable set to the password."
msgstr ""
msgid "APIFuzzing|Instead of entering the username directly, enter the key of the CI variable set to the username."
msgstr ""
msgid "APIFuzzing|Make sure your credentials are secured"
msgstr ""
msgid "APIFuzzing|Password for basic authentication"
msgstr ""
msgid "APIFuzzing|Scan mode"
msgstr ""
msgid "APIFuzzing|Scan profile"
msgstr ""
msgid "APIFuzzing|Show code snippet for the profile"
msgstr ""
msgid "APIFuzzing|Target URL"
msgstr ""
msgid "APIFuzzing|There are two ways to perform scans."
msgstr ""
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. A user with maintainer access rights can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area. We detected that you are not a maintainer. Commit your changes and assign them to a maintainer to update the credentials before merging."
msgstr ""
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. As a user with maintainer access rights, you can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area."
msgstr ""
msgid "APIFuzzing|Use this tool to generate API fuzzing configuration YAML to copy into your .gitlab-ci.yml file. This tool does not reflect or update your .gitlab-ci.yml file automatically."
msgstr ""
msgid "APIFuzzing|Username for basic authentication"
msgstr ""
msgid "APIFuzzing|We recommend that you review the JSON specifications file before adding it to a repository."
msgstr ""
msgid "APIFuzzing|You may need a maintainer's help to secure your credentials."
msgstr ""
msgid "AWS Access Key"
msgstr ""
@ -4138,6 +4222,9 @@ msgstr ""
msgid "Authenticating"
msgstr ""
msgid "Authentication"
msgstr ""
msgid "Authentication Failure"
msgstr ""
@ -11850,6 +11937,9 @@ msgstr ""
msgid "Evidence collection"
msgstr ""
msgid "Ex: Example.com"
msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
@ -12297,9 +12387,6 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
msgid "Failed to update tag!"
msgstr ""
msgid "Failed to update the Canary Ingress."
msgstr ""
@ -14510,6 +14597,12 @@ msgstr ""
msgid "Guideline"
msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
msgid "HAR file path"
msgstr ""
msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
msgstr ""
@ -20667,6 +20760,12 @@ msgstr ""
msgid "Open"
msgstr ""
msgid "Open API"
msgstr ""
msgid "Open API specification file path"
msgstr ""
msgid "Open Selection"
msgstr ""

View File

@ -38,6 +38,7 @@ Migration/UpdateLargeTable:
- :users
- :user_preferences
- :user_details
- :vulnerability_occurrences
- :web_hook_logs
DeniedMethods:
- :change_column_type_concurrently

View File

@ -7,6 +7,8 @@ RSpec.describe Projects::Security::ConfigurationController do
let(:user) { create(:user) }
before do
allow(controller).to receive(:ensure_security_and_compliance_enabled!)
sign_in(user)
end

View File

@ -21,14 +21,11 @@ describe('Batch comments draft note component', () => {
const getList = () => getByRole(wrapper.element, 'list');
const createComponent = (propsData = { draft }, features = {}) => {
const createComponent = (propsData = { draft }) => {
wrapper = shallowMount(localVue.extend(DraftNote), {
store,
propsData,
localVue,
provide: {
glFeatures: { multilineComments: true, ...features },
},
});
jest.spyOn(wrapper.vm.$store, 'dispatch').mockImplementation();
@ -145,16 +142,14 @@ describe('Batch comments draft note component', () => {
describe('multiline comments', () => {
describe.each`
desc | props | features | event | expectedCalls
${'with `draft.position`'} | ${draftWithLineRange} | ${{}} | ${'mouseenter'} | ${[['setSelectedCommentPositionHover', LINE_RANGE]]}
${'with `draft.position`'} | ${draftWithLineRange} | ${{}} | ${'mouseleave'} | ${[['setSelectedCommentPositionHover']]}
${'with `draft.position`'} | ${draftWithLineRange} | ${{ multilineComments: false }} | ${'mouseenter'} | ${[]}
${'with `draft.position`'} | ${draftWithLineRange} | ${{ multilineComments: false }} | ${'mouseleave'} | ${[]}
${'without `draft.position`'} | ${{}} | ${{}} | ${'mouseenter'} | ${[]}
${'without `draft.position`'} | ${{}} | ${{}} | ${'mouseleave'} | ${[]}
`('$desc and features $features', ({ props, event, features, expectedCalls }) => {
desc | props | event | expectedCalls
${'with `draft.position`'} | ${draftWithLineRange} | ${'mouseenter'} | ${[['setSelectedCommentPositionHover', LINE_RANGE]]}
${'with `draft.position`'} | ${draftWithLineRange} | ${'mouseleave'} | ${[['setSelectedCommentPositionHover']]}
${'without `draft.position`'} | ${{}} | ${'mouseenter'} | ${[]}
${'without `draft.position`'} | ${{}} | ${'mouseleave'} | ${[]}
`('$desc', ({ props, event, expectedCalls }) => {
beforeEach(() => {
createComponent({ draft: { ...draft, ...props } }, features);
createComponent({ draft: { ...draft, ...props } });
jest.spyOn(store, 'dispatch');
});

View File

@ -56,17 +56,30 @@ describe('Batch comments draft preview item component', () => {
createComponent(false, {
file_path: 'index.js',
file_hash: 'abc',
position: { new_line: 1 },
position: {
line_range: {
start: {
new_line: 1,
type: 'new',
},
},
},
});
expect(vm.$el.querySelector('.bold').textContent).toContain(':1');
expect(vm.$el.querySelector('.bold').textContent).toContain(':+1');
});
it('renders old line position', () => {
createComponent(false, {
file_path: 'index.js',
file_hash: 'abc',
position: { old_line: 2 },
position: {
line_range: {
start: {
old_line: 2,
},
},
},
});
expect(vm.$el.querySelector('.bold').textContent).toContain(':2');

View File

@ -17,6 +17,7 @@ describe('DiffLineNoteForm', () => {
const store = createStore();
store.state.notes.userData.id = 1;
store.state.notes.noteableData = noteableDataMock;
store.state.diffs.diffFiles = [diffFile];
store.replaceState({ ...store.state, ...args.state });

View File

@ -23,7 +23,7 @@ describe('DiscussionNotes', () => {
let wrapper;
const getList = () => getByRole(wrapper.element, 'list');
const createComponent = (props, features = {}) => {
const createComponent = (props) => {
wrapper = shallowMount(DiscussionNotes, {
store,
propsData: {
@ -38,9 +38,6 @@ describe('DiscussionNotes', () => {
slots: {
'avatar-badge': '<span class="avatar-badge-slot-content" />',
},
provide: {
glFeatures: { multilineComments: true, ...features },
},
});
};
@ -177,16 +174,14 @@ describe('DiscussionNotes', () => {
});
describe.each`
desc | props | features | event | expectedCalls
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${{}} | ${'mouseenter'} | ${[['setSelectedCommentPositionHover', LINE_RANGE]]}
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${{}} | ${'mouseleave'} | ${[['setSelectedCommentPositionHover']]}
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${{ multilineComments: false }} | ${'mouseenter'} | ${[]}
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${{ multilineComments: false }} | ${'mouseleave'} | ${[]}
${'without `discussion.position`'} | ${{}} | ${{}} | ${'mouseenter'} | ${[]}
${'without `discussion.position`'} | ${{}} | ${{}} | ${'mouseleave'} | ${[]}
`('$desc and features $features', ({ props, event, features, expectedCalls }) => {
desc | props | event | expectedCalls
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${'mouseenter'} | ${[['setSelectedCommentPositionHover', LINE_RANGE]]}
${'with `discussion.position`'} | ${{ discussion: DISCUSSION_WITH_LINE_RANGE }} | ${'mouseleave'} | ${[['setSelectedCommentPositionHover']]}
${'without `discussion.position`'} | ${{}} | ${'mouseenter'} | ${[]}
${'without `discussion.position`'} | ${{}} | ${'mouseleave'} | ${[]}
`('$desc', ({ props, event, expectedCalls }) => {
beforeEach(() => {
createComponent(props, features);
createComponent(props);
jest.spyOn(store, 'dispatch');
});

View File

@ -8,15 +8,6 @@ import NoteActions from '~/notes/components/note_actions.vue';
import NoteBody from '~/notes/components/note_body.vue';
import { noteableDataMock, notesDataMock, note } from '../mock_data';
jest.mock('~/vue_shared/mixins/gl_feature_flags_mixin', () => () => ({
inject: {
glFeatures: {
from: 'glFeatures',
default: () => ({ multilineComments: true }),
},
},
}));
describe('issue_note', () => {
let store;
let wrapper;

View File

@ -6,6 +6,8 @@ RSpec.describe API::GenericPackages do
include HttpBasicAuthHelpers
using RSpec::Parameterized::TableSyntax
include_context 'workhorse headers'
let_it_be(:personal_access_token) { create(:personal_access_token) }
let_it_be(:project, reload: true) { create(:project) }
let_it_be(:deploy_token_rw) { create(:deploy_token, read_package_registry: true, write_package_registry: true) }
@ -14,8 +16,6 @@ RSpec.describe API::GenericPackages do
let_it_be(:project_deploy_token_ro) { create(:project_deploy_token, deploy_token: deploy_token_ro, project: project) }
let_it_be(:deploy_token_wo) { create(:deploy_token, read_package_registry: false, write_package_registry: true) }
let_it_be(:project_deploy_token_wo) { create(:project_deploy_token, deploy_token: deploy_token_wo, project: project) }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let(:user) { personal_access_token.user }
let(:ci_build) { create(:ci_build, :running, user: user) }
@ -129,7 +129,7 @@ RSpec.describe API::GenericPackages do
end
it "responds with #{params[:expected_status]}" do
authorize_upload_file(workhorse_header.merge(auth_header))
authorize_upload_file(workhorse_headers.merge(auth_header))
expect(response).to have_gitlab_http_status(expected_status)
end
@ -144,7 +144,7 @@ RSpec.describe API::GenericPackages do
with_them do
it "responds with #{params[:expected_status]}" do
authorize_upload_file(workhorse_header.merge(deploy_token_auth_header))
authorize_upload_file(workhorse_headers.merge(deploy_token_auth_header))
expect(response).to have_gitlab_http_status(expected_status)
end
@ -162,7 +162,7 @@ RSpec.describe API::GenericPackages do
end
with_them do
subject { authorize_upload_file(workhorse_header.merge(personal_access_token_header), param_name => param_value) }
subject { authorize_upload_file(workhorse_headers.merge(personal_access_token_header), param_name => param_value) }
it_behaves_like 'secure endpoint'
end
@ -173,7 +173,7 @@ RSpec.describe API::GenericPackages do
stub_feature_flags(generic_packages: false)
project.add_developer(user)
authorize_upload_file(workhorse_header.merge(personal_access_token_header))
authorize_upload_file(workhorse_headers.merge(personal_access_token_header))
expect(response).to have_gitlab_http_status(:not_found)
end
@ -239,7 +239,7 @@ RSpec.describe API::GenericPackages do
end
it "responds with #{params[:expected_status]}" do
headers = workhorse_header.merge(auth_header)
headers = workhorse_headers.merge(auth_header)
upload_file(params, headers)
@ -254,7 +254,7 @@ RSpec.describe API::GenericPackages do
with_them do
it "responds with #{params[:expected_status]}" do
headers = workhorse_header.merge(deploy_token_auth_header)
headers = workhorse_headers.merge(deploy_token_auth_header)
upload_file(params, headers)
@ -270,7 +270,7 @@ RSpec.describe API::GenericPackages do
shared_examples 'creates a package and package file' do
it 'creates a package and package file' do
headers = workhorse_header.merge(auth_header)
headers = workhorse_headers.merge(auth_header)
expect { upload_file(params, headers) }
.to change { project.packages.generic.count }.by(1)
@ -324,26 +324,26 @@ RSpec.describe API::GenericPackages do
end
context 'event tracking' do
subject { upload_file(params, workhorse_header.merge(personal_access_token_header)) }
subject { upload_file(params, workhorse_headers.merge(personal_access_token_header)) }
it_behaves_like 'a gitlab tracking event', described_class.name, 'push_package'
end
it 'rejects request without a file from workhorse' do
headers = workhorse_header.merge(personal_access_token_header)
headers = workhorse_headers.merge(personal_access_token_header)
upload_file({}, headers)
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'rejects request without an auth token' do
upload_file(params, workhorse_header)
upload_file(params, workhorse_headers)
expect(response).to have_gitlab_http_status(:unauthorized)
end
it 'rejects request without workhorse rewritten fields' do
headers = workhorse_header.merge(personal_access_token_header)
headers = workhorse_headers.merge(personal_access_token_header)
upload_file(params, headers, send_rewritten_field: false)
expect(response).to have_gitlab_http_status(:bad_request)
@ -354,7 +354,7 @@ RSpec.describe API::GenericPackages do
allow(uploaded_file).to receive(:size).and_return(project.actual_limits.generic_packages_max_file_size + 1)
end
headers = workhorse_header.merge(personal_access_token_header)
headers = workhorse_headers.merge(personal_access_token_header)
upload_file(params, headers)
expect(response).to have_gitlab_http_status(:bad_request)
@ -378,7 +378,7 @@ RSpec.describe API::GenericPackages do
end
with_them do
subject { upload_file(params, workhorse_header.merge(personal_access_token_header), param_name => param_value) }
subject { upload_file(params, workhorse_headers.merge(personal_access_token_header), param_name => param_value) }
it_behaves_like 'secure endpoint'
end

View File

@ -5,13 +5,13 @@ require 'spec_helper'
RSpec.describe API::GroupImport do
include WorkhorseHelpers
include_context 'workhorse headers'
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let(:path) { '/groups/import' }
let(:file) { File.join('spec', 'fixtures', 'group_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/group_export_spec" }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
before do
allow_next_instance_of(Gitlab::ImportExport) do |import_export|

View File

@ -4,6 +4,8 @@ require 'spec_helper'
RSpec.describe API::MavenPackages do
include WorkhorseHelpers
include_context 'workhorse headers'
let_it_be_with_refind(:package_settings) { create(:namespace_package_setting, :group) }
let_it_be(:group) { package_settings.namespace }
let_it_be(:user) { create(:user) }
@ -20,8 +22,7 @@ RSpec.describe API::MavenPackages do
let_it_be(:group_deploy_token) { create(:group_deploy_token, deploy_token: deploy_token_for_group, group: group) }
let(:package_name) { 'com/example/my-app' }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let(:headers) { workhorse_headers }
let(:headers_with_token) { headers.merge('Private-Token' => personal_access_token.token) }
let(:group_deploy_token_headers) { { Gitlab::Auth::AuthFinders::DEPLOY_TOKEN_HEADER => deploy_token_for_group.token } }
@ -548,8 +549,8 @@ RSpec.describe API::MavenPackages do
end
describe 'PUT /api/v4/projects/:id/packages/maven/*path/:file_name' do
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let(:send_rewritten_field) { true }
let(:file_upload) { fixture_file_upload('spec/fixtures/packages/maven/my-app-1.0-20180724.124855-1.jar') }
@ -602,7 +603,7 @@ RSpec.describe API::MavenPackages do
end
context 'without workhorse header' do
let(:workhorse_header) { {} }
let(:workhorse_headers) { {} }
subject { upload_file_with_token(params: params) }

View File

@ -144,8 +144,8 @@ RSpec.describe API::NugetProjectPackages do
end
describe 'PUT /api/v4/projects/:id/packages/nuget/authorize' do
let_it_be(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let_it_be(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let(:url) { "/projects/#{target.id}/packages/nuget/authorize" }
let(:headers) { {} }
@ -176,7 +176,7 @@ RSpec.describe API::NugetProjectPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
@ -194,8 +194,8 @@ RSpec.describe API::NugetProjectPackages do
end
describe 'PUT /api/v4/projects/:id/packages/nuget' do
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let_it_be(:file_name) { 'package.nupkg' }
let(:url) { "/projects/#{target.id}/packages/nuget" }
let(:headers) { {} }
@ -239,7 +239,7 @@ RSpec.describe API::NugetProjectPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
@ -256,7 +256,7 @@ RSpec.describe API::NugetProjectPackages do
it_behaves_like 'rejects nuget access with invalid target id'
context 'file size above maximum limit' do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
before do
allow_next_instance_of(UploadedFile) do |uploaded_file|

View File

@ -5,13 +5,12 @@ require 'spec_helper'
RSpec.describe API::ProjectImport do
include WorkhorseHelpers
include_context 'workhorse headers'
let(:user) { create(:user) }
let(:file) { File.join('spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:namespace) { create(:group) }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
before do
namespace.add_owner(user)
end

View File

@ -74,8 +74,8 @@ RSpec.describe API::PypiPackages do
end
describe 'POST /api/v4/projects/:id/packages/pypi/authorize' do
let_it_be(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let_it_be(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let(:url) { "/projects/#{project.id}/packages/pypi/authorize" }
let(:headers) { {} }
@ -106,7 +106,7 @@ RSpec.describe API::PypiPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
@ -124,8 +124,8 @@ RSpec.describe API::PypiPackages do
end
describe 'POST /api/v4/projects/:id/packages/pypi' do
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let_it_be(:file_name) { 'package.whl' }
let(:url) { "/projects/#{project.id}/packages/pypi" }
let(:headers) { {} }
@ -170,7 +170,7 @@ RSpec.describe API::PypiPackages do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:user_headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel.const_get(project_visibility_level, false))
@ -184,7 +184,7 @@ RSpec.describe API::PypiPackages do
let(:requires_python) { 'x' * 256 }
let(:token) { personal_access_token.token }
let(:user_headers) { basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@ -196,7 +196,7 @@ RSpec.describe API::PypiPackages do
context 'with an invalid package' do
let(:token) { personal_access_token.token }
let(:user_headers) { basic_auth_header(user.username, token) }
let(:headers) { user_headers.merge(workhorse_header) }
let(:headers) { user_headers.merge(workhorse_headers) }
before do
params[:name] = '.$/@!^*'
@ -213,7 +213,7 @@ RSpec.describe API::PypiPackages do
it_behaves_like 'rejects PyPI access with unknown project id'
context 'file size above maximum limit' do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
before do
allow_next_instance_of(UploadedFile) do |uploaded_file|

View File

@ -5,12 +5,10 @@ require 'spec_helper'
RSpec.describe Import::GitlabGroupsController do
include WorkhorseHelpers
include_context 'workhorse headers'
let_it_be(:user) { create(:user) }
let(:import_path) { "#{Dir.tmpdir}/gitlab_groups_controller_spec" }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) do
{ 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token }
end
before do
allow_next_instance_of(Gitlab::ImportExport) do |import_export|

View File

@ -5,8 +5,7 @@ require 'spec_helper'
RSpec.describe Import::GitlabProjectsController do
include WorkhorseHelpers
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
include_context 'workhorse headers'
let_it_be(:namespace) { create(:namespace) }
let_it_be(:user) { namespace.owner }

View File

@ -27,6 +27,14 @@ RSpec.describe Ci::CreateJobArtifactsService do
UploadedFile.new(upload.path, **params)
end
def unique_metrics_report_uploaders
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(
event_names: described_class::METRICS_REPORT_UPLOAD_EVENT_NAME,
start_date: 2.weeks.ago,
end_date: 2.weeks.from_now
)
end
describe '#execute' do
subject { service.execute(artifacts_file, params, metadata_file: metadata_file) }
@ -42,6 +50,12 @@ RSpec.describe Ci::CreateJobArtifactsService do
expect(new_artifact.file_sha256).to eq(artifacts_sha256)
end
it 'does not track the job user_id' do
subject
expect(unique_metrics_report_uploaders).to eq(0)
end
context 'when metadata file is also uploaded' do
let(:metadata_file) do
file_to_upload('spec/fixtures/ci_build_artifacts_metadata.gz', sha256: artifacts_sha256)
@ -174,6 +188,20 @@ RSpec.describe Ci::CreateJobArtifactsService do
end
end
context 'when artifact_type is metrics' do
before do
allow(job).to receive(:user_id).and_return(123)
end
let(:params) { { 'artifact_type' => 'metrics', 'artifact_format' => 'gzip' }.with_indifferent_access }
it 'tracks the job user_id' do
subject
expect(unique_metrics_report_uploaders).to eq(1)
end
end
context 'when artifact type is cluster_applications' do
let(:artifacts_file) do
file_to_upload('spec/fixtures/helm/helm_list_v2_prometheus_missing.json.gz', sha256: artifacts_sha256)

View File

@ -537,7 +537,7 @@ RSpec.describe Ci::CreatePipelineService do
it 'pull it from Auto-DevOps' do
pipeline = execute_service
expect(pipeline).to be_auto_devops_source
expect(pipeline.builds.map(&:name)).to match_array(%w[build code_quality eslint-sast secret_detection_default_branch test])
expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection_default_branch test])
end
end

View File

@ -67,9 +67,9 @@ RSpec.shared_context 'conan file upload endpoints' do
include WorkhorseHelpers
include HttpBasicAuthHelpers
include_context 'workhorse headers'
let(:jwt) { build_jwt(personal_access_token) }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_header) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
let(:headers_with_token) { build_token_auth_header(jwt.encoded).merge(workhorse_header) }
let(:headers_with_token) { build_token_auth_header(jwt.encoded).merge(workhorse_headers) }
let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz"}
end

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
RSpec.shared_context 'workhorse headers' do
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token } }
end

View File

@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_context 'Debian repository shared context' do |object_type|
include_context 'workhorse headers'
before do
stub_feature_flags(debian_packages: true)
end
@ -37,16 +39,15 @@ RSpec.shared_context 'Debian repository shared context' do |object_type|
let(:params) { workhorse_params }
let(:auth_headers) { {} }
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:workhorse_headers) do
let(:wh_headers) do
if method == :put
{ 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => workhorse_token }
workhorse_headers
else
{}
end
end
let(:headers) { auth_headers.merge(workhorse_headers) }
let(:headers) { auth_headers.merge(wh_headers) }
let(:send_rewritten_field) { true }

View File

@ -123,7 +123,7 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
context 'with a request that bypassed gitlab-workhorse' do
let(:headers) do
basic_auth_header(user.username, personal_access_token.token)
.merge(workhorse_header)
.merge(workhorse_headers)
.tap { |h| h.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER) }
end

View File

@ -24,7 +24,7 @@ end
RSpec.shared_examples 'deploy token for package uploads' do
context 'with deploy token headers' do
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_header) }
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@ -35,7 +35,7 @@ RSpec.shared_examples 'deploy token for package uploads' do
end
context 'invalid token' do
let(:headers) { basic_auth_header(deploy_token.username, 'bar').merge(workhorse_header) }
let(:headers) { basic_auth_header(deploy_token.username, 'bar').merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end
@ -102,7 +102,7 @@ end
RSpec.shared_examples 'job token for package uploads' do
context 'with job token headers' do
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token).merge(workhorse_header) }
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token).merge(workhorse_headers) }
before do
project.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
@ -114,13 +114,13 @@ RSpec.shared_examples 'job token for package uploads' do
end
context 'invalid token' do
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar').merge(workhorse_header) }
let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar').merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end
context 'invalid user' do
let(:headers) { basic_auth_header('foo', job.token).merge(workhorse_header) }
let(:headers) { basic_auth_header('foo', job.token).merge(workhorse_headers) }
it_behaves_like 'returning response status', :unauthorized
end