Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
eef9c80f1c
commit
da50206243
56 changed files with 1197 additions and 608 deletions
|
@ -81,7 +81,13 @@ nodejs-scan-sast:
|
|||
secrets-sast:
|
||||
extends: .sast
|
||||
image:
|
||||
name: "$SAST_ANALYZER_IMAGE_PREFIX/secrets:$SAST_ANALYZER_IMAGE_TAG"
|
||||
name: "$SAST_ANALYZER_IMAGE_PREFIX/secrets:3"
|
||||
artifacts:
|
||||
paths:
|
||||
- gl-secret-detection-report.json # GitLab-specific
|
||||
reports:
|
||||
sast: gl-secret-detection-report.json
|
||||
expire_in: 1 week # GitLab-specific
|
||||
|
||||
# We need to duplicate this job's definition because it seems it's impossible to
|
||||
# override an included `only.refs`.
|
||||
|
|
|
@ -12,37 +12,39 @@ and verify the issue you're about to submit isn't a duplicate.
|
|||
|
||||
### Summary
|
||||
|
||||
(Summarize the bug encountered concisely)
|
||||
<!-- Summarize the bug encountered concisely. -->
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
(How one can reproduce the issue - this is very important)
|
||||
<!-- Describe how one can reproduce the issue - this is very important. Please use an ordered list. -->
|
||||
|
||||
### Example Project
|
||||
|
||||
(If possible, please create an example project here on GitLab.com that exhibits the problematic behavior, and link to it here in the bug report)
|
||||
|
||||
(If you are using an older version of GitLab, this will also determine whether the bug is fixed in a more recent version)
|
||||
<!-- If possible, please create an example project here on GitLab.com that exhibits the problematic
|
||||
behavior, and link to it here in the bug report. If you are using an older version of GitLab, this
|
||||
will also determine whether the bug is fixed in a more recent version. -->
|
||||
|
||||
### What is the current *bug* behavior?
|
||||
|
||||
(What actually happens)
|
||||
<!-- Describe what actually happens. -->
|
||||
|
||||
### What is the expected *correct* behavior?
|
||||
|
||||
(What you should see instead)
|
||||
<!-- Describe what you should see instead. -->
|
||||
|
||||
### Relevant logs and/or screenshots
|
||||
|
||||
(Paste any relevant logs - please use code blocks (```) to format console output,
|
||||
logs, and code as it's tough to read otherwise.)
|
||||
<!-- Paste any relevant logs - please use code blocks (```) to format console output, logs, and code
|
||||
as it's tough to read otherwise. -->
|
||||
|
||||
### Output of checks
|
||||
|
||||
(If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com)
|
||||
<!-- If you are reporting a bug on GitLab.com, write: This bug happens on GitLab.com -->
|
||||
|
||||
#### Results of GitLab environment info
|
||||
|
||||
<!-- Input any relevant GitLab environment information if needed. -->
|
||||
|
||||
<details>
|
||||
<summary>Expand for output related to GitLab environment info</summary>
|
||||
|
||||
|
@ -59,6 +61,8 @@ logs, and code as it's tough to read otherwise.)
|
|||
|
||||
#### Results of GitLab application Check
|
||||
|
||||
<!-- Input any relevant GitLab application check information if needed. -->
|
||||
|
||||
<details>
|
||||
<summary>Expand for output related to the GitLab application check</summary>
|
||||
<pre>
|
||||
|
@ -76,6 +80,6 @@ logs, and code as it's tough to read otherwise.)
|
|||
|
||||
### Possible fixes
|
||||
|
||||
(If you can, link to the line of code that might be responsible for the problem)
|
||||
<!-- If you can, link to the line of code that might be responsible for the problem. -->
|
||||
|
||||
/label ~bug
|
||||
|
|
|
@ -168,7 +168,7 @@ export default {
|
|||
|
||||
// if this upload resulted in a new version being created, redirect user to the latest version
|
||||
if (!this.isLatestVersion) {
|
||||
this.$router.push({ name: DESIGNS_ROUTE_NAME });
|
||||
this.$router.push({ name: DESIGNS_ROUTE_NAME }, () => {});
|
||||
}
|
||||
this.resetFilesToBeSaved();
|
||||
},
|
||||
|
|
|
@ -131,7 +131,8 @@ export default {
|
|||
>
|
||||
<div
|
||||
:style="{
|
||||
paddingLeft: `16px`,
|
||||
paddingLeft: `${graphLeftPadding}px`,
|
||||
paddingRight: `${graphRightPadding}px`,
|
||||
}"
|
||||
>
|
||||
<gl-loading-icon v-if="isLoading" class="m-auto" size="lg" />
|
||||
|
|
|
@ -15,6 +15,9 @@ class Projects::TodosController < Projects::ApplicationController
|
|||
IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
|
||||
when "merge_request"
|
||||
MergeRequestsFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
|
||||
when "design"
|
||||
issue = IssuesFinder.new(current_user, project_id: @project.id).find(params[:issue_id])
|
||||
DesignManagement::DesignsFinder.new(issue, current_user).find(params[:issuable_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ module FinderMethods
|
|||
def if_authorized(result)
|
||||
# Return the result if the finder does not perform authorization checks.
|
||||
# this is currently the case in the `MilestoneFinder`
|
||||
return result unless respond_to?(:current_user)
|
||||
return result unless respond_to?(:current_user, true)
|
||||
|
||||
if can_read_object?(result)
|
||||
result
|
||||
|
@ -44,9 +44,14 @@ module FinderMethods
|
|||
# for Todos
|
||||
return true unless DeclarativePolicy.has_policy?(object)
|
||||
|
||||
model_name = object&.model_name || model.model_name
|
||||
Ability.allowed?(current_user, :"read_#{to_ability_name(object)}", object)
|
||||
end
|
||||
|
||||
Ability.allowed?(current_user, :"read_#{model_name.singular}", object)
|
||||
def to_ability_name(object)
|
||||
return object.to_ability_name if object.respond_to?(:to_ability_name)
|
||||
|
||||
# Not all objects define `#to_ability_name`, so attempt to derive it:
|
||||
object.model_name.singular
|
||||
end
|
||||
|
||||
# This fetches the model from the `ActiveRecord::Relation` but does not
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
module DesignManagement
|
||||
class DesignsFinder
|
||||
include Gitlab::Allowable
|
||||
include FinderMethods
|
||||
|
||||
# Params:
|
||||
# ids: integer[]
|
||||
|
|
|
@ -33,7 +33,8 @@ module SystemNoteHelper
|
|||
'designs_removed' => 'doc-image',
|
||||
'designs_discussion_added' => 'doc-image',
|
||||
'status' => 'status',
|
||||
'alert_issue_added' => 'issues'
|
||||
'alert_issue_added' => 'issues',
|
||||
'new_alert_added' => 'warning'
|
||||
}.freeze
|
||||
|
||||
def system_note_icon_name(note)
|
||||
|
|
27
app/models/ci_platform_metric.rb
Normal file
27
app/models/ci_platform_metric.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CiPlatformMetric < ApplicationRecord
|
||||
validates :recorded_at, presence: true
|
||||
validates :platform_target, presence: true, length: { maximum: 255 }
|
||||
validates :count, presence: true
|
||||
|
||||
CI_VARIABLE_KEY = "AUTO_DEVOPS_PLATFORM_TARGET"
|
||||
|
||||
def self.update!
|
||||
# This work can NOT be done in-database because value is encrypted.
|
||||
# However, for "AUTO_DEVOPS_PLATFORM_TARGET", these values are only
|
||||
# encrypted as a matter of course, rather than as a need for secrecy.
|
||||
# So this is not a security risk, but exposing other keys possibly could be.
|
||||
variables = Ci::Variable.by_key(CI_VARIABLE_KEY)
|
||||
update_recorded_at = Time.zone.now
|
||||
counts = variables.group_by(&:value).map do |value, variables|
|
||||
{
|
||||
recorded_at: update_recorded_at,
|
||||
platform_target: value,
|
||||
count: variables.count
|
||||
}
|
||||
end
|
||||
|
||||
create(counts)
|
||||
end
|
||||
end
|
|
@ -440,6 +440,22 @@ class Issue < ApplicationRecord
|
|||
key = Gitlab::Routing.url_helpers.realtime_changes_project_issue_path(project, self)
|
||||
Gitlab::EtagCaching::Store.new.touch(key)
|
||||
end
|
||||
|
||||
def find_next_gap_before
|
||||
super
|
||||
rescue ActiveRecord::QueryCanceled => e
|
||||
# Symptom of running out of space - schedule rebalancing
|
||||
IssueRebalancingWorker.perform_async(id)
|
||||
raise e
|
||||
end
|
||||
|
||||
def find_next_gap_after
|
||||
super
|
||||
rescue ActiveRecord::QueryCanceled => e
|
||||
# Symptom of running out of space - schedule rebalancing
|
||||
IssueRebalancingWorker.perform_async(id)
|
||||
raise e
|
||||
end
|
||||
end
|
||||
|
||||
Issue.prepend_if_ee('EE::Issue')
|
||||
|
|
|
@ -20,7 +20,7 @@ class SystemNoteMetadata < ApplicationRecord
|
|||
title time_tracking branch milestone discussion task moved
|
||||
opened closed merged duplicate locked unlocked outdated
|
||||
tag due_date pinned_embed cherry_pick health_status approved unapproved
|
||||
status alert_issue_added relate unrelate
|
||||
status alert_issue_added relate unrelate new_alert_added
|
||||
].freeze
|
||||
|
||||
validates :note, presence: true
|
||||
|
|
|
@ -6,7 +6,7 @@ module AlertManagement
|
|||
include ::IncidentManagement::Settings
|
||||
|
||||
def execute
|
||||
return bad_request unless parsed_alert.valid?
|
||||
return bad_request unless incoming_payload.has_required_attributes?
|
||||
|
||||
process_alert_management_alert
|
||||
|
||||
|
@ -15,22 +15,17 @@ module AlertManagement
|
|||
|
||||
private
|
||||
|
||||
delegate :firing?, :resolved?, :gitlab_fingerprint, :ends_at, to: :parsed_alert
|
||||
|
||||
def parsed_alert
|
||||
strong_memoize(:parsed_alert) do
|
||||
Gitlab::Alerting::Alert.new(project: project, payload: params)
|
||||
def process_alert_management_alert
|
||||
if incoming_payload.resolved?
|
||||
process_resolved_alert_management_alert
|
||||
else
|
||||
process_firing_alert_management_alert
|
||||
end
|
||||
end
|
||||
|
||||
def process_alert_management_alert
|
||||
process_firing_alert_management_alert if firing?
|
||||
process_resolved_alert_management_alert if resolved?
|
||||
end
|
||||
|
||||
def process_firing_alert_management_alert
|
||||
if am_alert.present?
|
||||
am_alert.register_new_event!
|
||||
if alert.persisted?
|
||||
alert.register_new_event!
|
||||
reset_alert_management_alert_status
|
||||
else
|
||||
create_alert_management_alert
|
||||
|
@ -40,47 +35,42 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def reset_alert_management_alert_status
|
||||
return if am_alert.trigger
|
||||
return if alert.trigger
|
||||
|
||||
logger.warn(
|
||||
message: 'Unable to update AlertManagement::Alert status to triggered',
|
||||
project_id: project.id,
|
||||
alert_id: am_alert.id
|
||||
alert_id: alert.id
|
||||
)
|
||||
end
|
||||
|
||||
def create_alert_management_alert
|
||||
new_alert = AlertManagement::Alert.new(am_alert_params.merge(ended_at: nil))
|
||||
if new_alert.save
|
||||
new_alert.execute_services
|
||||
@am_alert = new_alert
|
||||
if alert.save
|
||||
alert.execute_services
|
||||
SystemNoteService.create_new_alert(alert, Gitlab::AlertManagement::AlertParams::MONITORING_TOOLS[:prometheus])
|
||||
return
|
||||
end
|
||||
|
||||
logger.warn(
|
||||
message: 'Unable to create AlertManagement::Alert',
|
||||
project_id: project.id,
|
||||
alert_errors: new_alert.errors.messages
|
||||
alert_errors: alert.errors.messages
|
||||
)
|
||||
end
|
||||
|
||||
def am_alert_params
|
||||
Gitlab::AlertManagement::AlertParams.from_prometheus_alert(project: project, parsed_alert: parsed_alert)
|
||||
end
|
||||
|
||||
def process_resolved_alert_management_alert
|
||||
return if am_alert.blank?
|
||||
return unless alert.persisted?
|
||||
return unless auto_close_incident?
|
||||
|
||||
if am_alert.resolve(ends_at)
|
||||
close_issue(am_alert.issue)
|
||||
if alert.resolve(incoming_payload.ends_at)
|
||||
close_issue(alert.issue)
|
||||
return
|
||||
end
|
||||
|
||||
logger.warn(
|
||||
message: 'Unable to update AlertManagement::Alert status to resolved',
|
||||
project_id: project.id,
|
||||
alert_id: am_alert.id
|
||||
alert_id: alert.id
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -95,19 +85,44 @@ module AlertManagement
|
|||
end
|
||||
|
||||
def process_incident_alert
|
||||
return unless am_alert
|
||||
return if am_alert.issue
|
||||
return unless alert.persisted?
|
||||
return if alert.issue
|
||||
|
||||
IncidentManagement::ProcessAlertWorker.perform_async(nil, nil, am_alert.id)
|
||||
IncidentManagement::ProcessAlertWorker.perform_async(nil, nil, alert.id)
|
||||
end
|
||||
|
||||
def logger
|
||||
@logger ||= Gitlab::AppLogger
|
||||
end
|
||||
|
||||
def am_alert
|
||||
strong_memoize(:am_alert) do
|
||||
AlertManagement::Alert.not_resolved.for_fingerprint(project, gitlab_fingerprint).first
|
||||
def alert
|
||||
strong_memoize(:alert) do
|
||||
existing_alert || new_alert
|
||||
end
|
||||
end
|
||||
|
||||
def existing_alert
|
||||
strong_memoize(:existing_alert) do
|
||||
AlertManagement::Alert.not_resolved.for_fingerprint(project, incoming_payload.gitlab_fingerprint).first
|
||||
end
|
||||
end
|
||||
|
||||
def new_alert
|
||||
strong_memoize(:new_alert) do
|
||||
AlertManagement::Alert.new(
|
||||
**incoming_payload.alert_params,
|
||||
ended_at: nil
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def incoming_payload
|
||||
strong_memoize(:incoming_payload) do
|
||||
Gitlab::AlertManagement::Payload.parse(
|
||||
project,
|
||||
params,
|
||||
monitoring_tool: Gitlab::AlertManagement::Payload::MONITORING_TOOLS[:prometheus]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ module Projects
|
|||
def create_alert
|
||||
alert = AlertManagement::Alert.create(am_alert_params)
|
||||
alert.execute_services if alert.persisted?
|
||||
SystemNoteService.create_new_alert(alert, 'Generic Alert Endpoint')
|
||||
|
||||
alert
|
||||
end
|
||||
|
|
|
@ -308,6 +308,10 @@ module SystemNoteService
|
|||
::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project, author: author).new_alert_issue(issue)
|
||||
end
|
||||
|
||||
def create_new_alert(alert, monitoring_tool)
|
||||
::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project).create_new_alert(monitoring_tool)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_requests_service(noteable, project, author)
|
||||
|
|
|
@ -2,6 +2,21 @@
|
|||
|
||||
module SystemNotes
|
||||
class AlertManagementService < ::SystemNotes::BaseService
|
||||
# Called when the a new AlertManagement::Alert has been created
|
||||
#
|
||||
# alert - AlertManagement::Alert object.
|
||||
#
|
||||
# Example Note text:
|
||||
#
|
||||
# "GitLab Alert Bot logged an alert from Prometheus"
|
||||
#
|
||||
# Returns the created Note object
|
||||
def create_new_alert(monitoring_tool)
|
||||
body = "logged an alert from **#{monitoring_tool}**"
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, User.alert_bot, body, action: 'new_alert_added'))
|
||||
end
|
||||
|
||||
# Called when the status of an AlertManagement::Alert has changed
|
||||
#
|
||||
# alert - AlertManagement::Alert object.
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
= render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
|
||||
|
||||
.tab-content
|
||||
#js-tab-pipeline.tab-pane.w-1000
|
||||
#js-tab-pipeline.tab-pane.gl-absolute.gl-left-0.gl-w-full
|
||||
#js-pipeline-graph-vue
|
||||
|
||||
#js-tab-builds.tab-pane
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add a system note on Alert creation
|
||||
merge_request: 40128
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove secret_detection job from vendored SAST CI template
|
||||
merge_request: 40028
|
||||
author:
|
||||
type: removed
|
5
changelogs/unreleased/ci-platform-metrics.yml
Normal file
5
changelogs/unreleased/ci-platform-metrics.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Adds CI Platform Metrics bookkeeping model
|
||||
merge_request: 40036
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/fix-css-pipeline-graph.yml
Normal file
5
changelogs/unreleased/fix-css-pipeline-graph.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix the broken CSS on the pipeline graph
|
||||
merge_request: 40386
|
||||
author:
|
||||
type: fixed
|
27
db/migrate/20200820204041_create_ci_platform_metrics.rb
Normal file
27
db/migrate/20200820204041_create_ci_platform_metrics.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateCiPlatformMetrics < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
unless table_exists?(:ci_platform_metrics)
|
||||
create_table :ci_platform_metrics do |t|
|
||||
t.datetime_with_timezone :recorded_at, null: false
|
||||
t.text :platform_target, null: false
|
||||
t.integer :count, null: false
|
||||
end
|
||||
end
|
||||
|
||||
add_text_limit :ci_platform_metrics, :platform_target, 255
|
||||
add_concurrent_index :ci_variables, :key
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :ci_platform_metrics
|
||||
remove_concurrent_index :ci_variables, :key
|
||||
end
|
||||
end
|
1
db/schema_migrations/20200820204041
Normal file
1
db/schema_migrations/20200820204041
Normal file
|
@ -0,0 +1 @@
|
|||
ce4d108c6587943ab3740dcc39298d6877d7317ec1023d8d263cecd9f1e0f478
|
|
@ -10233,6 +10233,23 @@ CREATE SEQUENCE public.ci_pipelines_id_seq
|
|||
|
||||
ALTER SEQUENCE public.ci_pipelines_id_seq OWNED BY public.ci_pipelines.id;
|
||||
|
||||
CREATE TABLE public.ci_platform_metrics (
|
||||
id bigint NOT NULL,
|
||||
recorded_at timestamp with time zone NOT NULL,
|
||||
platform_target text NOT NULL,
|
||||
count integer NOT NULL,
|
||||
CONSTRAINT check_f922abc32b CHECK ((char_length(platform_target) <= 255))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.ci_platform_metrics_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE public.ci_platform_metrics_id_seq OWNED BY public.ci_platform_metrics.id;
|
||||
|
||||
CREATE TABLE public.ci_refs (
|
||||
id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
|
@ -16857,6 +16874,8 @@ ALTER TABLE ONLY public.ci_pipelines ALTER COLUMN id SET DEFAULT nextval('public
|
|||
|
||||
ALTER TABLE ONLY public.ci_pipelines_config ALTER COLUMN pipeline_id SET DEFAULT nextval('public.ci_pipelines_config_pipeline_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.ci_platform_metrics ALTER COLUMN id SET DEFAULT nextval('public.ci_platform_metrics_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.ci_refs ALTER COLUMN id SET DEFAULT nextval('public.ci_refs_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.ci_resource_groups ALTER COLUMN id SET DEFAULT nextval('public.ci_resource_groups_id_seq'::regclass);
|
||||
|
@ -17821,6 +17840,9 @@ ALTER TABLE ONLY public.ci_pipelines_config
|
|||
ALTER TABLE ONLY public.ci_pipelines
|
||||
ADD CONSTRAINT ci_pipelines_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.ci_platform_metrics
|
||||
ADD CONSTRAINT ci_platform_metrics_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.ci_refs
|
||||
ADD CONSTRAINT ci_refs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -19403,6 +19425,8 @@ CREATE INDEX index_ci_triggers_on_owner_id ON public.ci_triggers USING btree (ow
|
|||
|
||||
CREATE INDEX index_ci_triggers_on_project_id ON public.ci_triggers USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_ci_variables_on_key ON public.ci_variables USING btree (key);
|
||||
|
||||
CREATE UNIQUE INDEX index_ci_variables_on_project_id_and_key_and_environment_scope ON public.ci_variables USING btree (project_id, key, environment_scope);
|
||||
|
||||
CREATE INDEX index_cluster_agent_tokens_on_agent_id ON public.cluster_agent_tokens USING btree (agent_id);
|
||||
|
|
|
@ -379,7 +379,7 @@ application server, or a Gitaly node.
|
|||
CAUTION: **Caution:**
|
||||
If you have data on an already existing storage called
|
||||
`default`, you should configure the virtual storage with another name and
|
||||
[migrate the data to the Praefect storage](#migrating-existing-repositories-to-praefect)
|
||||
[migrate the data to the Gitaly Cluster storage](#migrate-existing-repositories-to-gitaly-cluster)
|
||||
afterwards.
|
||||
|
||||
Replace `PRAEFECT_INTERNAL_TOKEN` with a strong secret, which will be used by
|
||||
|
@ -802,7 +802,8 @@ Particular attention should be shown to:
|
|||
|
||||
CAUTION: **Caution:**
|
||||
If you have existing data stored on the default Gitaly storage,
|
||||
you should [migrate the data your Praefect storage first](#migrating-existing-repositories-to-praefect).
|
||||
you should [migrate the data your Gitaly Cluster storage](#migrate-existing-repositories-to-gitaly-cluster)
|
||||
first.
|
||||
|
||||
```ruby
|
||||
gitaly['enable'] = false
|
||||
|
@ -1230,17 +1231,30 @@ The command will return a list of repositories that were found to be
|
|||
inconsistent against the current primary. Each of these inconsistencies will
|
||||
also be logged with an accompanying replication job ID.
|
||||
|
||||
## Migrating existing repositories to Praefect
|
||||
## Migrate existing repositories to Gitaly Cluster
|
||||
|
||||
If your GitLab instance already has repositories, these won't be migrated
|
||||
automatically.
|
||||
If your GitLab instance already has repositories on single Gitaly nodes, these aren't migrated to
|
||||
Gitaly Cluster automatically.
|
||||
|
||||
Repositories may be moved from one storage location using the [Project repository storage moves API](../../api/project_repository_storage_moves.md):
|
||||
|
||||
```shell
|
||||
curl --request POST --header "Private-Token: <your_access_token>" --header "Content-Type: application/json" \
|
||||
--data '{"destination_storage_name":"praefect"}' "https://gitlab.example.com/api/v4/projects/123/repository_storage_moves"
|
||||
```
|
||||
To move repositories to Gitaly Cluster:
|
||||
|
||||
1. [Schedule a move](../../api/project_repository_storage_moves.md#schedule-a-repository-storage-move-for-a-project)
|
||||
for the first repository using the API. For example:
|
||||
|
||||
```shell
|
||||
curl --request POST --header "Private-Token: <your_access_token>" --header "Content-Type: application/json" \
|
||||
--data '{"destination_storage_name":"praefect"}' "https://gitlab.example.com/api/v4/projects/123/repository_storage_moves"
|
||||
```
|
||||
|
||||
1. Using the ID that is returned, [query the repository move](../../api/project_repository_storage_moves.md#get-a-single-repository-storage-move-for-a-project)
|
||||
using the API. The query indicates either:
|
||||
- The move has completed successfully. The `state` field is `finished`.
|
||||
- The move is in progress. Re-query the repository move until it completes successfully.
|
||||
- The move has failed. Most failures are temporary and are solved by rescheduling the move.
|
||||
|
||||
1. Once the move is successful, repeat these steps for all repositories for your projects.
|
||||
|
||||
## Debugging Praefect
|
||||
|
||||
|
|
|
@ -1207,3 +1207,26 @@ the `commit_committer_check` and `reject_unsigned_commits` parameters:
|
|||
...
|
||||
}
|
||||
```
|
||||
|
||||
### Add group push rule **(STARTER)**
|
||||
|
||||
Adds [push rules](../user/group/index.md#group-push-rules-starter) to the specified group.
|
||||
|
||||
```plaintext
|
||||
POST /groups/:id/push_rule
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------------------------------------------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) |
|
||||
| `deny_delete_tag` **(STARTER)** | boolean | no | Deny deleting a tag |
|
||||
| `member_check` **(STARTER)** | boolean | no | Allows only GitLab users to author commits |
|
||||
| `prevent_secrets` **(STARTER)** | boolean | no | [Files that are likely to contain secrets](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/checks/files_denylist.yml) will be rejected |
|
||||
| `commit_message_regex` **(STARTER)** | string | no | All commit messages must match the regular expression provided in this attribute, e.g. `Fixed \d+\..*` |
|
||||
| `commit_message_negative_regex` **(STARTER)** | string | no | Commit messages matching the regular expression provided in this attribute will not be allowed, e.g. `ssh\:\/\/` |
|
||||
| `branch_name_regex` **(STARTER)** | string | no | All branch names must match the regular expression provided in this attribute, e.g. `(feature|hotfix)\/*` |
|
||||
| `author_email_regex` **(STARTER)** | string | no | All commit author emails must match the regular expression provided in this attribute, e.g. `@my-company.com$` |
|
||||
| `file_name_regex` **(STARTER)** | string | no | Filenames matching the regular expression provided in this attribute will **not** be allowed, e.g. `(jar|exe)$` |
|
||||
| `max_file_size` **(STARTER)** | integer | no | Maximum file size (MB) allowed |
|
||||
| `commit_committer_check` **(PREMIUM)** | boolean | no | Only commits pushed using verified emails will be allowed |
|
||||
| `reject_unsigned_commits` **(PREMIUM)** | boolean | no | Only commits signed through GPG will be allowed |
|
||||
|
|
|
@ -9,8 +9,22 @@ type: reference
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31285) in GitLab 13.0.
|
||||
|
||||
Project repository storage can be moved. To retrieve project repository storage moves using the API,
|
||||
you must [authenticate yourself](README.md#authentication) as an administrator.
|
||||
Project repositories can be moved between storages. This can be useful when
|
||||
[migrating to Gitaly Cluster](../administration/gitaly/praefect.md#migrate-existing-repositories-to-gitaly-cluster),
|
||||
for example.
|
||||
|
||||
As project repository storage moves are processed, they transition through different states. Values
|
||||
of `state` are:
|
||||
|
||||
- `initial`
|
||||
- `scheduled`
|
||||
- `started`
|
||||
- `finished`
|
||||
- `failed`
|
||||
- `replicated`
|
||||
- `cleanup_failed`
|
||||
|
||||
This API requires you to [authenticate yourself](README.md#authentication) as an administrator.
|
||||
|
||||
## Retrieve all project repository storage moves
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,9 +25,11 @@ Docker image with the fuzz engine to run your app.
|
|||
|
||||
| Language | Fuzzing Engine | Example |
|
||||
|----------|----------------|---------|
|
||||
| C/C++ | [libFuzzer](https://llvm.org/docs/LibFuzzer.html) | [c-cpp-example](https://gitlab.com/gitlab-org/security-products/demos/c-cpp-fuzzing-example) |
|
||||
| GoLang | [go-fuzz (libFuzzer support)](https://github.com/dvyukov/go-fuzz) | [go-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/go-fuzzing-example) |
|
||||
| Rust | [cargo-fuzz (libFuzzer support)](https://github.com/rust-fuzz/cargo-fuzz) | |
|
||||
| C/C++ | [libFuzzer](https://llvm.org/docs/LibFuzzer.html) | [c-cpp-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/c-cpp-fuzzing-example) |
|
||||
| GoLang | [go-fuzz (libFuzzer support)](https://github.com/dvyukov/go-fuzz) | [go-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/go-fuzzing-example) |
|
||||
| Swift | [libfuzzer](https://github.com/apple/swift/blob/master/docs/libFuzzerIntegration.md) | [swift-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/swift-fuzzing-example) |
|
||||
| Rust | [cargo-fuzz (libFuzzer support)](https://github.com/rust-fuzz/cargo-fuzz) | [rust-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/rust-fuzzing-example) |
|
||||
| Java | [JQF](https://github.com/rohanpadhye/JQF) | [java-fuzzing-example](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/java-fuzzing-example) |
|
||||
|
||||
## Configuration
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ SAST supports the following official analyzers:
|
|||
- [`nodejs-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan) (NodeJsScan)
|
||||
- [`phpcs-security-audit`](https://gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit) (PHP CS security-audit)
|
||||
- [`pmd-apex`](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex) (PMD (Apex only))
|
||||
- [`secrets`](https://gitlab.com/gitlab-org/security-products/analyzers/secrets) (Secrets (Gitleaks & TruffleHog secret detectors))
|
||||
- [`security-code-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan) (Security Code Scan (.NET))
|
||||
- [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix))
|
||||
- [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT))
|
||||
|
|
|
@ -65,7 +65,6 @@ The following table shows which languages, package managers and frameworks are s
|
|||
|--------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| .NET Core | [Security Code Scan](https://security-code-scan.github.io) | 11.0, [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
| .NET Framework | [Security Code Scan](https://security-code-scan.github.io) | 13.0, [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
| Any | [Gitleaks](https://github.com/zricethezav/gitleaks) and [TruffleHog](https://github.com/dxa4481/truffleHog) | 11., [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
| Apex (Salesforce) | [PMD](https://pmd.github.io/pmd/index.html) | 12.1, [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
| C/C++ | [Flawfinder](https://github.com/david-a-wheeler/flawfinder) | 10.7, [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
| Elixir (Phoenix) | [Sobelow](https://github.com/nccgroup/sobelow) | 11.10, [moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.3 |
|
||||
|
@ -340,11 +339,7 @@ Some analyzers make it possible to filter out vulnerabilities under a given thre
|
|||
| `SAST_BANDIT_EXCLUDED_PATHS` | | Comma-separated list of paths to exclude from scan. Uses Python's [`fnmatch` syntax](https://docs.python.org/2/library/fnmatch.html); For example: `'*/tests/*, */venv/*'` |
|
||||
| `SAST_BRAKEMAN_LEVEL` | 1 | Ignore Brakeman vulnerabilities under given confidence level. Integer, 1=Low 3=High. |
|
||||
| `SAST_FLAWFINDER_LEVEL` | 1 | Ignore Flawfinder vulnerabilities under given risk level. Integer, 0=No risk, 5=High risk. |
|
||||
| `SAST_GITLEAKS_ENTROPY_LEVEL` | 8.0 | Minimum entropy for secret detection. Float, 0.0 = low, 8.0 = high. |
|
||||
| `SAST_GOSEC_LEVEL` | 0 | Ignore Gosec vulnerabilities under given confidence level. Integer, 0=Undefined, 1=Low, 2=Medium, 3=High. |
|
||||
| `SAST_GITLEAKS_COMMIT_FROM` | | The commit a Gitleaks scan starts at. |
|
||||
| `SAST_GITLEAKS_COMMIT_TO` | | The commit a Gitleaks scan ends at. |
|
||||
| `SAST_GITLEAKS_HISTORIC_SCAN` | `false` | Flag to enable a historic Gitleaks scan. |
|
||||
|
||||
#### Docker-in-Docker orchestrator
|
||||
|
||||
|
@ -543,7 +538,6 @@ registry.gitlab.com/gitlab-org/security-products/analyzers/kubesec:2
|
|||
registry.gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/pmd-apex:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/secrets:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/security-code-scan:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/sobelow:2
|
||||
registry.gitlab.com/gitlab-org/security-products/analyzers/spotbugs:2
|
||||
|
|
|
@ -265,32 +265,6 @@ license_scanning:
|
|||
You can supply a custom root certificate to complete TLS verification by using the
|
||||
`ADDITIONAL_CA_CERT_BUNDLE` [environment variable](#available-variables).
|
||||
|
||||
To bypass TLS verification, you can use a custom [`pip.conf`](https://pip.pypa.io/en/stable/user_guide/#config-file)
|
||||
file to configure trusted hosts.
|
||||
|
||||
The following `gitlab-ci.yml` file uses a [`before_script`](../../../ci/yaml/README.md#before_script-and-after_script)
|
||||
to inject a custom [`pip.conf`](https://pip.pypa.io/en/stable/user_guide/#config-file):
|
||||
|
||||
```yaml
|
||||
include:
|
||||
- template: Security/License-Scanning.gitlab-ci.yml
|
||||
|
||||
license_scanning:
|
||||
variables:
|
||||
PIP_INDEX_URL: 'https://pypi.example.com/simple/'
|
||||
before_script:
|
||||
- mkdir -p ~/.config/pip/
|
||||
- cp pip.conf ~/.config/pip/pip.conf
|
||||
```
|
||||
|
||||
The [`pip.conf`](https://pip.pypa.io/en/stable/reference/pip/) allows you to specify a list of
|
||||
[trusted hosts](https://pip.pypa.io/en/stable/reference/pip/#cmdoption-trusted-host):
|
||||
|
||||
```plaintext
|
||||
[global]
|
||||
trusted-host = pypi.example.com
|
||||
```
|
||||
|
||||
#### Using private Python repos
|
||||
|
||||
If you have a private Python repository you can use the `PIP_INDEX_URL` [environment variable](#available-variables)
|
||||
|
|
|
@ -20,6 +20,7 @@ module Gitlab
|
|||
:alert_markdown,
|
||||
:alert_title,
|
||||
:annotations,
|
||||
:description,
|
||||
:ends_at,
|
||||
:environment,
|
||||
:environment_name,
|
||||
|
@ -29,11 +30,12 @@ module Gitlab
|
|||
:gitlab_fingerprint,
|
||||
:gitlab_prometheus_alert_id,
|
||||
:gitlab_y_label,
|
||||
:description,
|
||||
:has_required_attributes?,
|
||||
:hosts,
|
||||
:metric_id,
|
||||
:metrics_dashboard_url,
|
||||
:monitoring_tool,
|
||||
:resolved?,
|
||||
:runbook,
|
||||
:service,
|
||||
:severity,
|
||||
|
@ -121,6 +123,14 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def resolved?
|
||||
status == 'resolved'
|
||||
end
|
||||
|
||||
def has_required_attributes?
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def plain_gitlab_fingerprint; end
|
||||
|
|
|
@ -61,6 +61,10 @@ module Gitlab
|
|||
)
|
||||
end
|
||||
|
||||
def has_required_attributes?
|
||||
project && title && starts_at_raw
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def plain_gitlab_fingerprint
|
||||
|
|
|
@ -9,7 +9,7 @@ variables:
|
|||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||
|
||||
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, secrets, sobelow, pmd-apex, kubesec"
|
||||
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, sobelow, pmd-apex, kubesec"
|
||||
SAST_EXCLUDED_PATHS: "spec, test, tests, tmp"
|
||||
SAST_ANALYZER_IMAGE_TAG: 2
|
||||
SAST_DISABLE_DIND: "true"
|
||||
|
@ -186,18 +186,6 @@ pmd-apex-sast:
|
|||
exists:
|
||||
- '**/*.cls'
|
||||
|
||||
secrets-sast:
|
||||
extends: .sast-analyzer
|
||||
image:
|
||||
name: "$SAST_ANALYZER_IMAGE"
|
||||
variables:
|
||||
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/secrets:$SAST_ANALYZER_IMAGE_TAG"
|
||||
rules:
|
||||
- if: $SAST_DISABLED || $SAST_DISABLE_DIND == 'false'
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH &&
|
||||
$SAST_DEFAULT_ANALYZERS =~ /secrets/
|
||||
|
||||
security-code-scan-sast:
|
||||
extends: .sast-analyzer
|
||||
image:
|
||||
|
|
|
@ -12025,6 +12025,9 @@ msgstr ""
|
|||
msgid "Group project URLs are prefixed with the group namespace"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group push rule exists, try updating"
|
||||
msgstr ""
|
||||
|
||||
msgid "Group requires separate account"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
"vue": "^2.6.10",
|
||||
"vue-apollo": "^3.0.3",
|
||||
"vue-loader": "^15.9.0",
|
||||
"vue-router": "^3.0.2",
|
||||
"vue-router": "^3.4.3",
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"vue-virtual-scroll-list": "^1.4.4",
|
||||
"vuedraggable": "^2.23.0",
|
||||
|
|
|
@ -77,7 +77,7 @@ module QA
|
|||
|
||||
# The host key detection process is interrupted if we navigate away
|
||||
# from the page before the fingerprint appears.
|
||||
find_element(:fingerprints_list, text: /.*/)
|
||||
find_element(:fingerprints_list, text: /.*/, wait: 60)
|
||||
end
|
||||
|
||||
def mirror_repository
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
require('spec_helper')
|
||||
|
||||
RSpec.describe Projects::TodosController do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let(:design) { create(:design, project: project, issue: issue) }
|
||||
let(:parent) { project }
|
||||
|
||||
shared_examples 'project todos actions' do
|
||||
shared_examples 'issuable todo actions' do
|
||||
it_behaves_like 'todos actions'
|
||||
|
||||
context 'when not authorized for resource' do
|
||||
|
@ -40,7 +41,7 @@ RSpec.describe Projects::TodosController do
|
|||
format: 'html'
|
||||
end
|
||||
|
||||
it_behaves_like 'project todos actions'
|
||||
it_behaves_like 'issuable todo actions'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -57,7 +58,31 @@ RSpec.describe Projects::TodosController do
|
|||
format: 'html'
|
||||
end
|
||||
|
||||
it_behaves_like 'project todos actions'
|
||||
it_behaves_like 'issuable todo actions'
|
||||
end
|
||||
end
|
||||
|
||||
context 'Designs' do
|
||||
include DesignManagementTestHelpers
|
||||
|
||||
before do
|
||||
enable_design_management
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
def post_create
|
||||
post :create,
|
||||
params: {
|
||||
namespace_id: project.namespace,
|
||||
project_id: project,
|
||||
issue_id: issue.id,
|
||||
issuable_id: design.id,
|
||||
issuable_type: 'design'
|
||||
},
|
||||
format: 'html'
|
||||
end
|
||||
|
||||
it_behaves_like 'todos actions'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
9
spec/factories/ci_platform_metrics.rb
Normal file
9
spec/factories/ci_platform_metrics.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :ci_platform_metric do
|
||||
recorded_at { Time.zone.now }
|
||||
platform_target { generate(:title) }
|
||||
count { SecureRandom.random_number(100) }
|
||||
end
|
||||
end
|
|
@ -7,8 +7,6 @@ RSpec.describe FinderMethods do
|
|||
Class.new do
|
||||
include FinderMethods
|
||||
|
||||
attr_reader :current_user
|
||||
|
||||
def initialize(user)
|
||||
@current_user = user
|
||||
end
|
||||
|
@ -16,6 +14,10 @@ RSpec.describe FinderMethods do
|
|||
def execute
|
||||
Project.all.order(id: :desc)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :current_user
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -478,16 +478,15 @@ describe('Design management index page', () => {
|
|||
describe('on non-latest version', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
||||
});
|
||||
|
||||
router.replace({
|
||||
it('does not render design checkboxes', async () => {
|
||||
await router.replace({
|
||||
name: DESIGNS_ROUTE_NAME,
|
||||
query: {
|
||||
version: '2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render design checkboxes', () => {
|
||||
expect(findDesignCheckboxes()).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -514,13 +513,6 @@ describe('Design management index page', () => {
|
|||
files: [{ name: 'image.png', type: 'image/png' }],
|
||||
getData: () => 'test.png',
|
||||
};
|
||||
|
||||
router.replace({
|
||||
name: DESIGNS_ROUTE_NAME,
|
||||
query: {
|
||||
version: '2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not call paste event if designs wrapper is not hovered', () => {
|
||||
|
|
|
@ -450,16 +450,15 @@ describe('Design management index page', () => {
|
|||
describe('on non-latest version', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ designs: mockDesigns, allVersions: [mockVersion] });
|
||||
});
|
||||
|
||||
router.replace({
|
||||
it('does not render design checkboxes', async () => {
|
||||
await router.replace({
|
||||
name: DESIGNS_ROUTE_NAME,
|
||||
query: {
|
||||
version: '2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render design checkboxes', () => {
|
||||
expect(findDesignCheckboxes()).toHaveLength(0);
|
||||
});
|
||||
|
||||
|
@ -482,16 +481,9 @@ describe('Design management index page', () => {
|
|||
});
|
||||
|
||||
event = new Event('paste');
|
||||
|
||||
router.replace({
|
||||
name: DESIGNS_ROUTE_NAME,
|
||||
query: {
|
||||
version: '2',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('calls onUploadDesign with valid paste', () => {
|
||||
it('calls onUploadDesign with valid paste', async () => {
|
||||
event.clipboardData = {
|
||||
files: [{ name: 'image.png', type: 'image/png' }],
|
||||
getData: () => 'test.png',
|
||||
|
@ -532,11 +524,12 @@ describe('Design management index page', () => {
|
|||
});
|
||||
|
||||
describe('when navigating', () => {
|
||||
it('ensures fullscreen layout is not applied', () => {
|
||||
it('ensures fullscreen layout is not applied', async () => {
|
||||
createComponent(true);
|
||||
|
||||
wrapper.vm.$router.push('/designs');
|
||||
expect(mockPageEl.classList.remove).toHaveBeenCalledTimes(1);
|
||||
await wrapper.vm.$router.replace('/');
|
||||
await wrapper.vm.$router.replace('/designs');
|
||||
expect(mockPageEl.classList.remove).toHaveBeenCalledTimes(2);
|
||||
expect(mockPageEl.classList.remove).toHaveBeenCalledWith(...DESIGN_DETAIL_LAYOUT_CLASSLIST);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
const useLocalStorage = fn => {
|
||||
const origLocalStorage = window.localStorage;
|
||||
let currentLocalStorage;
|
||||
let currentLocalStorage = origLocalStorage;
|
||||
|
||||
Object.defineProperty(window, 'localStorage', {
|
||||
get: () => currentLocalStorage,
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
import { useLocalStorageSpy } from './local_storage_helper';
|
||||
|
||||
useLocalStorageSpy();
|
||||
describe('block before helper is installed', () => {
|
||||
it('should leave original localStorage intact', () => {
|
||||
expect(localStorage.getItem).toEqual(expect.any(Function));
|
||||
expect(jest.isMockFunction(localStorage.getItem)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('localStorage helper', () => {
|
||||
useLocalStorageSpy();
|
||||
|
||||
it('mocks localStorage but works exactly like original localStorage', () => {
|
||||
localStorage.setItem('test', 'testing');
|
||||
localStorage.setItem('test2', 'testing');
|
||||
|
|
|
@ -17,9 +17,13 @@ describe('~/ide/sync_router_and_store', () => {
|
|||
|
||||
const getRouterCurrentPath = () => router.currentRoute.fullPath;
|
||||
const getStoreCurrentPath = () => store.state.router.fullPath;
|
||||
const updateRouter = path => {
|
||||
const updateRouter = async path => {
|
||||
if (getRouterCurrentPath() === path) {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push(path);
|
||||
return waitForPromises();
|
||||
await waitForPromises();
|
||||
};
|
||||
const updateStore = path => {
|
||||
store.dispatch('router/push', path);
|
||||
|
|
|
@ -175,4 +175,36 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
|
|||
it { is_expected.to eq(environment) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#resolved?' do
|
||||
before do
|
||||
allow(parsed_payload).to receive(:status).and_return(status)
|
||||
end
|
||||
|
||||
subject { parsed_payload.resolved? }
|
||||
|
||||
context 'when status is not defined' do
|
||||
let(:status) { nil }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when status is not resovled' do
|
||||
let(:status) { 'firing' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'when status is resovled' do
|
||||
let(:status) { 'resolved' }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_required_attributes?' do
|
||||
subject { parsed_payload.has_required_attributes? }
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -204,4 +204,37 @@ RSpec.describe Gitlab::AlertManagement::Payload::Prometheus do
|
|||
it { is_expected.to be_nil }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#has_required_attributes?' do
|
||||
let(:starts_at) { Time.current.change(usec: 0).utc }
|
||||
let(:raw_payload) { { 'annotations' => { 'title' => 'title' }, 'startsAt' => starts_at.rfc3339 } }
|
||||
|
||||
subject { parsed_payload.has_required_attributes? }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
|
||||
context 'without project' do
|
||||
let(:parsed_payload) { described_class.new(project: nil, payload: raw_payload) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'without title' do
|
||||
let(:raw_payload) { { 'startsAt' => starts_at.rfc3339 } }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'without startsAt' do
|
||||
let(:raw_payload) { { 'annotations' => { 'title' => 'title' } } }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
|
||||
context 'without payload' do
|
||||
let(:parsed_payload) { described_class.new(project: project, payload: nil) }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
70
spec/models/ci_platform_metric_spec.rb
Normal file
70
spec/models/ci_platform_metric_spec.rb
Normal file
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe CiPlatformMetric do
|
||||
subject { build(:ci_platform_metric) }
|
||||
|
||||
describe 'validations' do
|
||||
it { is_expected.to validate_presence_of(:recorded_at) }
|
||||
it { is_expected.to validate_presence_of(:count) }
|
||||
it { is_expected.to validate_presence_of(:platform_target) }
|
||||
it { is_expected.to validate_length_of(:platform_target).is_at_most(255) }
|
||||
end
|
||||
|
||||
describe '.update!' do
|
||||
def platform_target_counts_by_day
|
||||
report = Hash.new { |hash, key| hash[key] = {} }
|
||||
CiPlatformMetric.all.each do |metric|
|
||||
date = metric.recorded_at.to_date
|
||||
report[date][metric.platform_target] = metric.count
|
||||
end
|
||||
report
|
||||
end
|
||||
|
||||
context "when there is already existing metrics data" do
|
||||
let!(:metric_1) { create(:ci_platform_metric) }
|
||||
let!(:metric_2) { create(:ci_platform_metric) }
|
||||
|
||||
it "does not erase any existing data" do
|
||||
CiPlatformMetric.update!
|
||||
|
||||
expect(CiPlatformMetric.all.to_a).to contain_exactly(metric_1, metric_2)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are multiple platform target variables" do
|
||||
let(:today) { Time.zone.local(1982, 4, 24) }
|
||||
let(:tomorrow) { today + 1.day }
|
||||
|
||||
it "updates platform target counts for that day" do
|
||||
Timecop.freeze(today) do
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "aws")
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "aws")
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "fargate")
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "fargate")
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "fargate")
|
||||
CiPlatformMetric.update!
|
||||
end
|
||||
Timecop.freeze(tomorrow) do
|
||||
create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: "fargate")
|
||||
CiPlatformMetric.update!
|
||||
end
|
||||
|
||||
expect(platform_target_counts_by_day).to eq({
|
||||
today.to_date => { "aws" => 2, "fargate" => 3 },
|
||||
tomorrow.to_date => { "aws" => 2, "fargate" => 4 }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are no platform target variables" do
|
||||
it "does not generate any new platform metrics" do
|
||||
create(:ci_variable, key: "KEY_WHATEVER", value: "aws")
|
||||
CiPlatformMetric.update!
|
||||
|
||||
expect(platform_target_counts_by_day).to eq({})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1183,4 +1183,32 @@ RSpec.describe Issue do
|
|||
expect(context[:label_url_method]).to eq(:project_issues_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'scheduling rebalancing' do
|
||||
before do
|
||||
allow(issue).to receive(:find_next_gap) { raise ActiveRecord::QueryCanceled }
|
||||
end
|
||||
|
||||
let(:project) { build(:project_empty_repo) }
|
||||
let(:issue) { build_stubbed(:issue, relative_position: 100, project: project) }
|
||||
|
||||
describe '#find_next_gap_before' do
|
||||
it 'schedules rebalancing if we time-out when finding a gap' do
|
||||
lhs = build_stubbed(:issue, relative_position: 99, project: project)
|
||||
to_move = build(:issue, project: project)
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id)
|
||||
|
||||
expect { to_move.move_between(lhs, issue) }.to raise_error(ActiveRecord::QueryCanceled)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_next_gap_after' do
|
||||
it 'schedules rebalancing if we time-out when finding a gap' do
|
||||
allow(issue).to receive(:find_next_gap) { raise ActiveRecord::QueryCanceled }
|
||||
expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id)
|
||||
|
||||
expect { issue.move_sequence_after }.to raise_error(ActiveRecord::QueryCanceled)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,8 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
subject(:execute) { described_class.new(project, nil, payload).execute }
|
||||
|
||||
context 'when alert payload is valid' do
|
||||
let(:parsed_alert) { Gitlab::Alerting::Alert.new(project: project, payload: payload) }
|
||||
let(:parsed_payload) { Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: 'Prometheus') }
|
||||
let(:fingerprint) { parsed_payload.gitlab_fingerprint }
|
||||
let(:payload) do
|
||||
{
|
||||
'status' => status,
|
||||
|
@ -39,25 +40,25 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
|
||||
context 'when Prometheus alert status is firing' do
|
||||
context 'when alert with the same fingerprint already exists' do
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||
|
||||
it_behaves_like 'adds an alert management alert event'
|
||||
|
||||
context 'existing alert is resolved' do
|
||||
let!(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
|
||||
|
||||
it_behaves_like 'creates an alert management alert'
|
||||
end
|
||||
|
||||
context 'existing alert is ignored' do
|
||||
let!(:alert) { create(:alert_management_alert, :ignored, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, :ignored, project: project, fingerprint: fingerprint) }
|
||||
|
||||
it_behaves_like 'adds an alert management alert event'
|
||||
end
|
||||
|
||||
context 'two existing alerts, one resolved one open' do
|
||||
let!(:resolved_alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:resolved_alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||
|
||||
it_behaves_like 'adds an alert management alert event'
|
||||
end
|
||||
|
@ -84,6 +85,10 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
context 'when alert can be created' do
|
||||
it_behaves_like 'creates an alert management alert'
|
||||
|
||||
it 'creates a system note corresponding to alert creation' do
|
||||
expect { subject }.to change(Note, :count).by(1)
|
||||
end
|
||||
|
||||
it 'processes the incident alert' do
|
||||
expect(IncidentManagement::ProcessAlertWorker)
|
||||
.to receive(:perform_async)
|
||||
|
@ -95,18 +100,15 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
end
|
||||
|
||||
context 'when alert cannot be created' do
|
||||
let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] })}
|
||||
let(:am_alert) { instance_double(AlertManagement::Alert, save: false, errors: errors) }
|
||||
|
||||
before do
|
||||
allow(AlertManagement::Alert).to receive(:new).and_return(am_alert)
|
||||
payload['annotations']['title'] = 'description' * 50
|
||||
end
|
||||
|
||||
it 'writes a warning to the log' do
|
||||
expect(Gitlab::AppLogger).to receive(:warn).with(
|
||||
message: 'Unable to create AlertManagement::Alert',
|
||||
project_id: project.id,
|
||||
alert_errors: { hosts: ['hosts array is over 255 chars'] }
|
||||
alert_errors: { title: ["is too long (maximum is 200 characters)"] }
|
||||
)
|
||||
|
||||
execute
|
||||
|
@ -126,7 +128,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
|
||||
context 'when Prometheus alert status is resolved' do
|
||||
let(:status) { 'resolved' }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||
|
||||
context 'when auto_resolve_incident set to true' do
|
||||
let_it_be(:operations_settings) { create(:project_incident_management_setting, project: project, auto_close_incident: true) }
|
||||
|
@ -142,7 +144,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
|||
stub_feature_flags(track_resource_state_change_events: state_tracking_enabled)
|
||||
end
|
||||
|
||||
let!(:alert) { create(:alert_management_alert, :with_issue, project: project, fingerprint: parsed_alert.gitlab_fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, :with_issue, project: project, fingerprint: fingerprint) }
|
||||
|
||||
it 'closes the issue' do
|
||||
issue = alert.issue
|
||||
|
|
|
@ -513,7 +513,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 secrets-sast test])
|
||||
expect(pipeline.builds.map(&:name)).to match_array(%w[build code_quality eslint-sast secret_detection_default_branch test])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -396,7 +396,7 @@ RSpec.describe NotificationService, :mailer do
|
|||
reset_delivered_emails!
|
||||
end
|
||||
|
||||
it 'sends emails to recipients' do
|
||||
it 'sends emails to recipients', :aggregate_failures do
|
||||
subject
|
||||
|
||||
expect_delivery_jobs_count(10)
|
||||
|
@ -730,7 +730,7 @@ RSpec.describe NotificationService, :mailer do
|
|||
let(:note) { create(:note_on_commit, project: project) }
|
||||
|
||||
before do
|
||||
build_team(note.project)
|
||||
build_team(project)
|
||||
build_group(project)
|
||||
reset_delivered_emails!
|
||||
allow(note.noteable).to receive(:author).and_return(@u_committer)
|
||||
|
@ -947,20 +947,25 @@ RSpec.describe NotificationService, :mailer do
|
|||
end
|
||||
|
||||
describe 'Issues', :deliver_mails_inline do
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, :public, namespace: group) }
|
||||
let(:another_project) { create(:project, :public, namespace: group) }
|
||||
let(:issue) { create :issue, project: project, assignees: [assignee], description: 'cc @participant @unsubscribed_mentioned' }
|
||||
|
||||
before do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :public, namespace: group) }
|
||||
|
||||
before_all do
|
||||
build_team(project)
|
||||
build_group(project)
|
||||
|
||||
add_users(project)
|
||||
end
|
||||
|
||||
before do
|
||||
add_user_subscriptions(issue)
|
||||
reset_delivered_emails!
|
||||
update_custom_notification(:new_issue, @u_guest_custom, resource: project)
|
||||
update_custom_notification(:new_issue, @u_custom_global)
|
||||
|
||||
issue.author.notified_of_own_activity = false
|
||||
end
|
||||
|
||||
describe '#new_issue' do
|
||||
|
@ -1578,18 +1583,22 @@ RSpec.describe NotificationService, :mailer do
|
|||
end
|
||||
|
||||
describe 'Merge Requests', :deliver_mails_inline do
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, :public, :repository, namespace: group) }
|
||||
let(:another_project) { create(:project, :public, namespace: group) }
|
||||
let(:assignees) { Array.wrap(assignee) }
|
||||
let(:author) { create(:user) }
|
||||
let(:merge_request) { create :merge_request, author: author, source_project: project, assignees: assignees, description: 'cc @participant' }
|
||||
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, :public, :repository, namespace: group) }
|
||||
|
||||
before_all do
|
||||
build_team(project)
|
||||
add_users(project)
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_maintainer(author)
|
||||
assignees.each { |assignee| project.add_maintainer(assignee) }
|
||||
build_team(project)
|
||||
add_users(project)
|
||||
add_user_subscriptions(merge_request)
|
||||
update_custom_notification(:new_merge_request, @u_guest_custom, resource: project)
|
||||
update_custom_notification(:new_merge_request, @u_custom_global)
|
||||
|
@ -2072,7 +2081,7 @@ RSpec.describe NotificationService, :mailer do
|
|||
end
|
||||
|
||||
describe 'Projects', :deliver_mails_inline do
|
||||
before do
|
||||
before_all do
|
||||
build_team(project)
|
||||
reset_delivered_emails!
|
||||
end
|
||||
|
|
|
@ -123,6 +123,10 @@ RSpec.describe Projects::Alerting::NotifyService do
|
|||
it_behaves_like 'creates an alert management alert'
|
||||
it_behaves_like 'assigns the alert properties'
|
||||
|
||||
it 'creates a system note corresponding to alert creation' do
|
||||
expect { subject }.to change(Note, :count).by(1)
|
||||
end
|
||||
|
||||
context 'existing alert with same fingerprint' do
|
||||
let(:fingerprint_sha) { Digest::SHA1.hexdigest(fingerprint) }
|
||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint_sha) }
|
||||
|
|
|
@ -739,4 +739,17 @@ RSpec.describe SystemNoteService do
|
|||
described_class.new_alert_issue(alert, alert.issue, author)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.create_new_alert' do
|
||||
let(:alert) { build(:alert_management_alert) }
|
||||
let(:monitoring_tool) { 'Prometheus' }
|
||||
|
||||
it 'calls AlertManagementService' do
|
||||
expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
|
||||
expect(service).to receive(:create_new_alert).with(monitoring_tool)
|
||||
end
|
||||
|
||||
described_class.create_new_alert(alert, monitoring_tool)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,19 @@ RSpec.describe ::SystemNotes::AlertManagementService do
|
|||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:noteable) { create(:alert_management_alert, :with_issue, :acknowledged, project: project) }
|
||||
|
||||
describe '#create_new_alert' do
|
||||
subject { described_class.new(noteable: noteable, project: project).create_new_alert('Some Service') }
|
||||
|
||||
it_behaves_like 'a system note' do
|
||||
let(:author) { User.alert_bot }
|
||||
let(:action) { 'new_alert_added' }
|
||||
end
|
||||
|
||||
it 'has the appropriate message' do
|
||||
expect(subject.note).to eq('logged an alert from **Some Service**')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#change_alert_status' do
|
||||
subject { described_class.new(noteable: noteable, project: project, author: author).change_alert_status(noteable) }
|
||||
|
||||
|
|
|
@ -12295,10 +12295,10 @@ vue-loader@^15.9.0:
|
|||
vue-hot-reload-api "^2.3.0"
|
||||
vue-style-loader "^4.1.0"
|
||||
|
||||
vue-router@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"
|
||||
integrity sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg==
|
||||
vue-router@^3.4.3:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.4.3.tgz#fa93768616ee338aa174f160ac965167fa572ffa"
|
||||
integrity sha512-BADg1mjGWX18Dpmy6bOGzGNnk7B/ZA0RxuA6qedY/YJwirMfKXIDzcccmHbQI0A6k5PzMdMloc0ElHfyOoX35A==
|
||||
|
||||
vue-runtime-helpers@^1.1.2:
|
||||
version "1.1.2"
|
||||
|
|
Loading…
Reference in a new issue