Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-08-27 18:10:29 +00:00
parent eef9c80f1c
commit da50206243
56 changed files with 1197 additions and 608 deletions

View file

@ -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`.

View file

@ -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

View file

@ -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();
},

View file

@ -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" />

View file

@ -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

View file

@ -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

View file

@ -3,6 +3,7 @@
module DesignManagement
class DesignsFinder
include Gitlab::Allowable
include FinderMethods
# Params:
# ids: integer[]

View file

@ -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)

View 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

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: Add a system note on Alert creation
merge_request: 40128
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Remove secret_detection job from vendored SAST CI template
merge_request: 40028
author:
type: removed

View file

@ -0,0 +1,5 @@
---
title: Adds CI Platform Metrics bookkeeping model
merge_request: 40036
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Fix the broken CSS on the pipeline graph
merge_request: 40386
author:
type: fixed

View 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

View file

@ -0,0 +1 @@
ce4d108c6587943ab3740dcc39298d6877d7317ec1023d8d263cecd9f1e0f478

View file

@ -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);

View file

@ -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

View file

@ -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 |

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -61,6 +61,10 @@ module Gitlab
)
end
def has_required_attributes?
project && title && starts_at_raw
end
private
def plain_gitlab_fingerprint

View file

@ -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:

View file

@ -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 ""

View file

@ -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",

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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', () => {

View file

@ -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);
});
});

View file

@ -10,7 +10,7 @@
*/
const useLocalStorage = fn => {
const origLocalStorage = window.localStorage;
let currentLocalStorage;
let currentLocalStorage = origLocalStorage;
Object.defineProperty(window, 'localStorage', {
get: () => currentLocalStorage,

View file

@ -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');

View file

@ -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);

View file

@ -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

View file

@ -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

View 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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) }

View file

@ -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

View file

@ -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) }

View file

@ -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"