-
+
diff --git a/app/assets/javascripts/issue_show/incident.js b/app/assets/javascripts/issue_show/incident.js
index a34e75ee64a..618fb551f28 100644
--- a/app/assets/javascripts/issue_show/incident.js
+++ b/app/assets/javascripts/issue_show/incident.js
@@ -3,6 +3,7 @@ import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import issuableApp from './components/app.vue';
import incidentTabs from './components/incidents/incident_tabs.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
Vue.use(VueApollo);
@@ -11,7 +12,7 @@ export default function initIssuableApp(issuableData = {}) {
defaultClient: createDefaultClient(),
});
- const { projectNamespace, projectPath, iid } = issuableData;
+ const { iid, projectNamespace, projectPath, slaFeatureAvailable } = issuableData;
return new Vue({
el: document.getElementById('js-issuable-app'),
@@ -22,6 +23,7 @@ export default function initIssuableApp(issuableData = {}) {
provide: {
fullPath: `${projectNamespace}/${projectPath}`,
iid,
+ slaFeatureAvailable: parseBoolean(slaFeatureAvailable),
},
render(createElement) {
return createElement('issuable-app', {
diff --git a/app/presenters/issue_presenter.rb b/app/presenters/issue_presenter.rb
index 185fcd3e934..0b498ce97d8 100644
--- a/app/presenters/issue_presenter.rb
+++ b/app/presenters/issue_presenter.rb
@@ -11,3 +11,5 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated
issue.subscribed?(current_user, issue.project)
end
end
+
+IssuePresenter.prepend_if_ee('EE::IssuePresenter')
diff --git a/app/views/projects/feature_flags/index.html.haml b/app/views/projects/feature_flags/index.html.haml
index f425de91d12..7d48cba74d0 100644
--- a/app/views/projects/feature_flags/index.html.haml
+++ b/app/views/projects/feature_flags/index.html.haml
@@ -7,6 +7,8 @@
"feature-flags-help-page-path" => help_page_path("operations/feature_flags"),
"feature-flags-client-libraries-help-page-path" => help_page_path("operations/feature_flags", anchor: "choose-a-client-library"),
"feature-flags-client-example-help-page-path" => help_page_path("operations/feature_flags", anchor: "golang-application-example"),
+ "feature-flags-limit-exceeded" => @project.actual_limits.exceeded?(:project_feature_flags, @project.operations_feature_flags.count),
+ "feature-flags-limit" => @project.actual_limits.project_feature_flags,
"unleash-api-url" => (unleash_api_url(@project) if can?(current_user, :admin_feature_flag, @project)),
"unleash-api-instance-id" => (unleash_api_instance_id(@project) if can?(current_user, :admin_feature_flag, @project)),
"can-user-admin-feature-flag" => can?(current_user, :admin_feature_flag, @project),
diff --git a/changelogs/unreleased/241663-incident-sla-logic.yml b/changelogs/unreleased/241663-incident-sla-logic.yml
new file mode 100644
index 00000000000..5b06e8e751f
--- /dev/null
+++ b/changelogs/unreleased/241663-incident-sla-logic.yml
@@ -0,0 +1,5 @@
+---
+title: Add Issuable Service Level Agreement (SLA) table
+merge_request: 44253
+author:
+type: added
diff --git a/changelogs/unreleased/feature-flag-limits-ux.yml b/changelogs/unreleased/feature-flag-limits-ux.yml
new file mode 100644
index 00000000000..7b1fa628ea9
--- /dev/null
+++ b/changelogs/unreleased/feature-flag-limits-ux.yml
@@ -0,0 +1,5 @@
+---
+title: Feature Flags limits UX and documentation
+merge_request: 44089
+author:
+type: added
diff --git a/db/migrate/20201002012659_add_issuable_sla_table.rb b/db/migrate/20201002012659_add_issuable_sla_table.rb
new file mode 100644
index 00000000000..c43187bf93a
--- /dev/null
+++ b/db/migrate/20201002012659_add_issuable_sla_table.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class AddIssuableSlaTable < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def change
+ create_table :issuable_slas do |t|
+ t.references :issue, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.datetime_with_timezone :due_at, null: false
+ end
+ end
+end
diff --git a/db/schema_migrations/20201002012659 b/db/schema_migrations/20201002012659
new file mode 100644
index 00000000000..6a6d33389f9
--- /dev/null
+++ b/db/schema_migrations/20201002012659
@@ -0,0 +1 @@
+8a12c3c4f674d2a36df56a89bfd32e0f3945e73605460bdf2a8b0aa1308f5b19
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 7ac26402d8e..38a89fe7dc4 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12739,6 +12739,21 @@ CREATE SEQUENCE issuable_severities_id_seq
ALTER SEQUENCE issuable_severities_id_seq OWNED BY issuable_severities.id;
+CREATE TABLE issuable_slas (
+ id bigint NOT NULL,
+ issue_id bigint NOT NULL,
+ due_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE issuable_slas_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE issuable_slas_id_seq OWNED BY issuable_slas.id;
+
CREATE TABLE issue_assignees (
user_id integer NOT NULL,
issue_id integer NOT NULL
@@ -17561,6 +17576,8 @@ ALTER TABLE ONLY ip_restrictions ALTER COLUMN id SET DEFAULT nextval('ip_restric
ALTER TABLE ONLY issuable_severities ALTER COLUMN id SET DEFAULT nextval('issuable_severities_id_seq'::regclass);
+ALTER TABLE ONLY issuable_slas ALTER COLUMN id SET DEFAULT nextval('issuable_slas_id_seq'::regclass);
+
ALTER TABLE ONLY issue_email_participants ALTER COLUMN id SET DEFAULT nextval('issue_email_participants_id_seq'::regclass);
ALTER TABLE ONLY issue_links ALTER COLUMN id SET DEFAULT nextval('issue_links_id_seq'::regclass);
@@ -18689,6 +18706,9 @@ ALTER TABLE ONLY ip_restrictions
ALTER TABLE ONLY issuable_severities
ADD CONSTRAINT issuable_severities_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY issuable_slas
+ ADD CONSTRAINT issuable_slas_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY issue_email_participants
ADD CONSTRAINT issue_email_participants_pkey PRIMARY KEY (id);
@@ -20485,6 +20505,8 @@ CREATE INDEX index_ip_restrictions_on_group_id ON ip_restrictions USING btree (g
CREATE UNIQUE INDEX index_issuable_severities_on_issue_id ON issuable_severities USING btree (issue_id);
+CREATE UNIQUE INDEX index_issuable_slas_on_issue_id ON issuable_slas USING btree (issue_id);
+
CREATE UNIQUE INDEX index_issue_assignees_on_issue_id_and_user_id ON issue_assignees USING btree (issue_id, user_id);
CREATE INDEX index_issue_assignees_on_user_id ON issue_assignees USING btree (user_id);
@@ -20541,8 +20563,6 @@ CREATE INDEX index_issues_on_updated_at ON issues USING btree (updated_at);
CREATE INDEX index_issues_on_updated_by_id ON issues USING btree (updated_by_id) WHERE (updated_by_id IS NOT NULL);
-CREATE INDEX index_issues_project_id_issue_type_incident ON issues USING btree (project_id) WHERE (issue_type = 1);
-
CREATE UNIQUE INDEX index_jira_connect_installations_on_client_key ON jira_connect_installations USING btree (client_key);
CREATE INDEX index_jira_connect_subscriptions_on_namespace_id ON jira_connect_subscriptions USING btree (namespace_id);
@@ -22852,6 +22872,9 @@ ALTER TABLE ONLY gpg_signatures
ALTER TABLE ONLY vulnerability_user_mentions
ADD CONSTRAINT fk_rails_1a41c485cd FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
+ALTER TABLE ONLY issuable_slas
+ ADD CONSTRAINT fk_rails_1b8768cd63 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY board_assignees
ADD CONSTRAINT fk_rails_1c0ff59e82 FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE CASCADE;
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 076658ead0e..fda3816312c 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -84,6 +84,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Operations](operations/index.md): Keeping GitLab up and running (clean up Redis sessions, moving repositories, Sidekiq MemoryKiller, Puma).
- [Restart GitLab](restart_gitlab.md): Learn how to restart GitLab and its components.
- [Invalidate Markdown cache](invalidate_markdown_cache.md): Invalidate any cached Markdown.
+- [Instance review](instance_review.md): Request a free review of your GitLab instance.
#### Updating GitLab
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index c25390e5f98..e647c020e01 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -528,10 +528,14 @@ More information can be found in the [Push event activities limit and bulk push
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218017) in GitLab 13.4.
-On GitLab.com, the maximum file size for a package that's uploaded to the [GitLab Package Registry](../user/packages/package_registry/index.md)
-is 5 gigabytes.
+On GitLab.com, the maximum file size for a package that's uploaded to the [GitLab Package Registry](../user/packages/package_registry/index.md) varies by format:
-Limits are set per package type.
+- Conan: 3GB
+- Generic: 5GB
+- Maven: 3GB
+- NPM: 500MB
+- NuGet: 500MB
+- PyPI: 3GB
To set this limit on a self-managed installation, run the following in the
[GitLab Rails console](troubleshooting/debug.md#starting-a-rails-console-session):
diff --git a/doc/administration/instance_review.md b/doc/administration/instance_review.md
index 326305f4517..7eadb54804b 100644
--- a/doc/administration/instance_review.md
+++ b/doc/administration/instance_review.md
@@ -1,13 +1,25 @@
+---
+stage: Growth
+group: Conversion
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
+---
+
# Instance Review **(CORE ONLY)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/6995) in [GitLab Core](https://about.gitlab.com/pricing/) 11.3.
-If you are running a medium size instance (50+ users) of GitLab Core edition you are qualified for a free Instance Review. You can find the button in the User menu.
+If you are running a medium size instance (50+ users) of
+[GitLab Core](https://about.gitlab.com/pricing/) edition, you are qualified for a
+free Instance Review.
-
+1. Sign in as a user with Admin [permissions](../user/permissions.md).
+1. In the top menu, click your user icon, and select
+ **Get a free instance review**:
-When you click the button you will be redirected to a form with prefilled data obtained from your instance.
+ 
-Once you submit the data to GitLab Inc. you can see the initial report.
+1. GitLab redirects you to a form with prefilled data obtained from your instance.
+1. Click **Submit** to see the initial report.
-Additionally you will be contacted by our team for further review which should help you to improve your usage of GitLab.
+A GitLab team member will contact you for further review, to provide suggestions
+that will help you improve your usage of GitLab.
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 2fe72f53d87..3ce04b0c189 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -23,8 +23,8 @@ GET /projects/:id/registry/repositories
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) accessible by the authenticated user. |
-| `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. |
-| `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
+| `tags` | boolean | no | If the parameter is included as true, each repository includes an array of `"tags"` in the response. |
+| `tags_count` | boolean | no | If the parameter is included as true, each repository includes `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/projects/5/registry/repositories"
@@ -66,8 +66,8 @@ GET /groups/:id/registry/repositories
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) accessible by the authenticated user. |
-| `tags` | boolean | no | If the parameter is included as true, each repository will include an array of `"tags"` in the response. |
-| `tags_count` | boolean | no | If the parameter is included as true, each repository will include `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
+| `tags` | boolean | no | If the parameter is included as true, each repository includes an array of `"tags"` in the response. |
+| `tags_count` | boolean | no | If the parameter is included as true, each repository includes `"tags_count"` in the response ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32141) in GitLab 13.1). |
```shell
curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/groups/2/registry/repositories?tags=1&tags_count=true"
@@ -250,7 +250,7 @@ DELETE /projects/:id/registry/repositories/:repository_id/tags
| `repository_id` | integer | yes | The ID of registry repository. |
| `name_regex` | string | no | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to delete. To delete all tags specify `.*`. **Note:** `name_regex` is deprecated in favor of `name_regex_delete`. This field is validated. |
| `name_regex_delete` | string | yes | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to delete. To delete all tags specify `.*`. This field is validated. |
-| `name_regex_keep` | string | no | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to keep. This value will override any matches from `name_regex_delete`. This field is validated. Note: setting to `.*` will result in a no-op. |
+| `name_regex_keep` | string | no | The [re2](https://github.com/google/re2/wiki/Syntax) regex of the name to keep. This value overrides any matches from `name_regex_delete`. This field is validated. Note: setting to `.*` results in a no-op. |
| `keep_n` | integer | no | The amount of latest tags of given name to keep. |
| `older_than` | string | no | Tags to delete that are older than the given time, written in human readable form `1h`, `1d`, `1month`. |
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 146f15cf3a7..bd231c76b14 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -6725,6 +6725,11 @@ type EpicIssue implements CurrentUserTodos & Noteable {
"""
severity: IssuableSeverity
+ """
+ Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled.
+ """
+ slaDueAt: Time
+
"""
State of the issue
"""
@@ -8880,6 +8885,11 @@ type Issue implements CurrentUserTodos & Noteable {
"""
severity: IssuableSeverity
+ """
+ Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled.
+ """
+ slaDueAt: Time
+
"""
State of the issue
"""
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 25c4014a4dc..f70814267ef 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -18532,6 +18532,20 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "slaDueAt",
+ "description": "Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "state",
"description": "State of the issue",
@@ -24220,6 +24234,20 @@
"isDeprecated": false,
"deprecationReason": null
},
+ {
+ "name": "slaDueAt",
+ "description": "Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
{
"name": "state",
"description": "State of the issue",
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 11205622c82..51bc2176102 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1066,6 +1066,7 @@ Relationship between an epic and an issue.
| `relationPath` | String | URI path of the epic-issue relation |
| `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) |
| `severity` | IssuableSeverity | Severity level of the incident |
+| `slaDueAt` | Time | Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled. |
| `state` | IssueState! | State of the issue |
| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
@@ -1256,6 +1257,7 @@ Represents a recorded measurement (object count) for the Admins.
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
| `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) |
| `severity` | IssuableSeverity | Severity level of the incident |
+| `slaDueAt` | Time | Timestamp of when the issue SLA expires. Returns null if `incident_sla_dev` feature flag is disabled. |
| `state` | IssueState! | State of the issue |
| `statusPagePublishedIncident` | Boolean | Indicates whether an issue is published to the status page |
| `subscribed` | Boolean! | Indicates the currently logged in user is subscribed to the issue |
diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md
index db12e6eb316..00ebfe5ccf8 100644
--- a/doc/operations/feature_flags.md
+++ b/doc/operations/feature_flags.md
@@ -56,6 +56,20 @@ To create and enable a feature flag:
You can change these settings by clicking the **{pencil}** (edit) button
next to any feature flag in the list.
+## Maximum number of feature flags
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/254379) in GitLab 13.5.
+
+The maximum number of feature flags per project on self-managed GitLab instances
+is 200. On GitLab.com, the maximum number is determined by [GitLab.com tier](https://about.gitlab.com/pricing/):
+
+| Tier | Number of feature flags per project |
+|----------|-------------------------------------|
+| Free | 50 |
+| Bronze | 100 |
+| Silver | 150 |
+| Gold | 200 |
+
## Feature flag strategies
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35555) in GitLab 13.0.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index 6fedcb41a89..6b65a4a9e5e 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -253,20 +253,24 @@ production: &base
## Upgrade your subscription tier
-To upgrade your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team as this
-can't be done in the Customers Portal. You can either send an email to `renewals@gitlab.com`, or
-complete the [**Contact Sales**](https://about.gitlab.com/sales/) form. Include details of which subscription you want to upgrade and the desired tier in your message.
+To upgrade your [GitLab tier](https://about.gitlab.com/pricing/):
-After messaging the sales team, the workflow is as follows:
+1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in).
+1. Select the **Upgrade** button on the relevant subscription card on the
+ [Manage purchases](https://customers.gitlab.com/subscriptions) page.
+1. Select the desired upgrade.
+1. Confirm the active form of payment, or add a new form of payment.
+1. Select the **I accept the Privacy Policy and Terms of Service** checkbox.
+1. Select **Purchase**.
-1. Receive a reply from the sales team, asking for confirmation of the upgrade.
-1. Reply to the sales team, confirming details of the upgrade.
-1. Receive a quote from the sales team.
-1. Sign and return the quote.
-1. Receive the new license.
-1. Upload the new license. For details, see [Uploading your license](../../user/admin_area/license.md#uploading-your-license).
+The following is emailed to you:
-The new subscription tier is active when the license file is uploaded.
+- A payment receipt. You can also access this information in the Customers Portal under
+ [**View invoices**](https://customers.gitlab.com/receipts).
+- A new license.
+
+[Upload the new license](../../user/admin_area/license.md#uploading-your-license) to your instance.
+The new tier takes effect when the new license is uploaded.
## Subscription expiry
diff --git a/doc/user/application_security/container_scanning/index.md b/doc/user/application_security/container_scanning/index.md
index 73f5404713f..9e7f98dd4fc 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -223,6 +223,14 @@ in an offline environment if you prefer using only locally available Docker imag
recommend keeping the pull policy setting to `always` if not in an offline environment, as this
enables the use of updated scanners in your CI/CD pipelines.
+##### Support for Custom Certificate Authorities
+
+Support for custom certificate authorities was introduced in the following versions:
+
+| Analyzer | Version |
+| -------- | ------- |
+| `klar` | [v2.3.0](https://gitlab.com/gitlab-org/security-products/analyzers/klar/-/releases/v2.3.0) |
+
#### Make GitLab container scanning analyzer images available inside your Docker registry
For container scanning, import the following default images from `registry.gitlab.com` into your
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 67d2ae2d3a7..9290c51a8b8 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -392,6 +392,18 @@ For details on saving and transporting Docker images as a file, see Docker's doc
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
+#### Support for Custom Certificate Authorities
+
+Support for custom certificate authorities was introduced in the following versions.
+
+| Analyzer | Version |
+| -------- | ------- |
+| `gemnasium` | [v2.8.0](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/releases/v2.8.0) |
+| `gemnasium-maven` | [v2.9.0](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven/-/releases/v2.9.0) |
+| `gemnasium-python` | [v2.7.0](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-python/-/releases/v2.7.0) |
+| `retire.js` | [v2.4.0](https://gitlab.com/gitlab-org/security-products/analyzers/retire.js/-/releases/v2.4.0) |
+| `bundler-audit` | [v2.4.0](https://gitlab.com/gitlab-org/security-products/analyzers/bundler-audit/-/releases/v2.4.0) |
+
### Set dependency scanning CI job variables to use local dependency scanning analyzers
Add the following configuration to your `.gitlab-ci.yml` file. You must change the value of
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 3b1c1b05e66..2ea60513d68 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -519,6 +519,25 @@ For details on saving and transporting Docker images as a file, see Docker's doc
[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
+#### If support for Custom Certificate Authorities are needed
+
+Support for custom certificate authorities was introduced in the following versions.
+
+| Analyzer | Version |
+| -------- | ------- |
+| `bandit` | [v2.3.0](https://gitlab.com/gitlab-org/security-products/analyzers/bandit/-/releases/v2.3.0) |
+| `brakeman` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman/-/releases/v2.1.0) |
+| `eslint` | [v2.9.2](https://gitlab.com/gitlab-org/security-products/analyzers/eslint/-/releases/v2.9.2) |
+| `flawfinder` | [v2.3.0](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder/-/releases/v2.3.0) |
+| `gosec` | [v2.5.0](https://gitlab.com/gitlab-org/security-products/analyzers/gosec/-/releases/v2.5.0) |
+| `kubesec` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/kubesec/-/releases/v2.1.0) |
+| `nodejs-scan` | [v2.9.5](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan/-/releases/v2.9.5) |
+| `phpcs-security-audit` | [v2.8.2](https://gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit/-/releases/v2.8.2) |
+| `pmd-apex` | [v2.1.0](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex/-/releases/v2.1.0) |
+| `security-code-scan` | [v2.7.3](https://gitlab.com/gitlab-org/security-products/analyzers/security-code-scan/-/releases/v2.7.3) |
+| `sobelow` | [v2.2.0](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow/-/releases/v2.2.0) |
+| `spotbugs` | [v2.7.1](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs/-/releases/v2.7.1) |
+
### Set SAST CI job variables to use local SAST analyzers
Add the following configuration to your `.gitlab-ci.yml` file. You must replace
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 4cc5bd020f3..1204460bd75 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -173,6 +173,32 @@ We have created a [short video walkthrough](https://youtu.be/wDtc_K00Y0A) showca
+### Make GitLab Secret Detection analyzer image available inside your Docker registry
+
+Import the following default Secret Detection analyzer images from `registry.gitlab.com` into your
+[local Docker container registry](../../packages/container_registry/index.md):
+
+```plaintext
+registry.gitlab.com/gitlab-org/security-products/analyzers/secrets:3
+```
+
+The process for importing Docker images into a local offline Docker registry depends on
+**your network security policy**. Please consult your IT staff to find an accepted and approved
+process by which external resources can be imported or temporarily accessed. Note that these scanners are [updated periodically](../index.md#maintenance-and-update-of-the-vulnerabilities-database)
+with new definitions, so consider if you're able to make periodic updates yourself.
+
+For details on saving and transporting Docker images as a file, see Docker's documentation on
+[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), [`docker load`](https://docs.docker.com/engine/reference/commandline/load/),
+[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and [`docker import`](https://docs.docker.com/engine/reference/commandline/import/).
+
+#### If support for Custom Certificate Authorities are needed
+
+Support for custom certificate authorities was introduced in the following versions.
+
+| Analyzer | Version |
+| -------- | ------- |
+| secrets | [v3.0.0](https://gitlab.com/gitlab-org/security-products/analyzers/secrets/-/releases/v3.0.0) |
+
## Troubleshooting
### Getting warning message `gl-secret-detection-report.json: no matching files`
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index c40db409903..113bb2a6d78 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -154,7 +154,7 @@ To add the GitLab NuGet Repository as a source for .NET, create a file named `nu
When uploading packages, note that:
-- The maximum allowed size is 50 Megabytes.
+- The Package Registry on GitLab.com can store up to 500 MB of content. This limit is [configurable for self-managed GitLab instances](../../../administration/instance_limits.md#package-registry-limits).
- If you upload the same package with the same version multiple times, each consecutive upload
is saved as a separate file. When installing a package, GitLab serves the most recent file.
- When uploading packages to GitLab, they are not displayed in the packages UI of your project
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 9ec5df8cde9..a0526ba0414 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -403,9 +403,15 @@ ee:
- issues:
- epic_issue:
- :epic
+ - :issuable_sla
- protected_branches:
- :unprotect_access_levels
- protected_environments:
- :deploy_access_levels
- :service_desk_setting
- :security_setting
+
+ included_attributes:
+ issuable_sla:
+ - :issue
+ - :due_at
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ed78572427a..3e7ff0e5ee7 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11152,6 +11152,9 @@ msgstr ""
msgid "FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
msgstr ""
+msgid "FeatureFlags|Feature flags limit reached (%{featureFlagsLimit}). Delete one or more feature flags before adding new ones."
+msgstr ""
+
msgid "FeatureFlags|Flag becomes read only soon"
msgstr ""
@@ -13280,6 +13283,9 @@ msgstr ""
msgid "HighlightBar|Original alert:"
msgstr ""
+msgid "HighlightBar|Time to SLA:"
+msgstr ""
+
msgid "History"
msgstr ""
@@ -13836,6 +13842,9 @@ msgstr ""
msgid "Incident|There was an issue loading alert data. Please try again."
msgstr ""
+msgid "Incident|There was an issue loading incident data. Please try again."
+msgstr ""
+
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
diff --git a/spec/frontend/feature_flags/components/feature_flags_spec.js b/spec/frontend/feature_flags/components/feature_flags_spec.js
index c9ab7695158..7f9e98c97ef 100644
--- a/spec/frontend/feature_flags/components/feature_flags_spec.js
+++ b/spec/frontend/feature_flags/components/feature_flags_spec.js
@@ -1,7 +1,7 @@
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import MockAdapter from 'axios-mock-adapter';
-import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
+import { GlAlert, GlEmptyState, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import Api from '~/api';
import createStore from '~/feature_flags/store/index';
@@ -20,14 +20,17 @@ localVue.use(Vuex);
describe('Feature flags', () => {
const mockData = {
- csrfToken: 'testToken',
- featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
- featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
- unleashApiUrl: `${TEST_HOST}/api/unleash`,
canUserConfigure: true,
- canUserRotateToken: true,
+ // canUserRotateToken: true,
+ csrfToken: 'testToken',
+ featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
+ featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
+ featureFlagsHelpPagePath: '/help/feature-flags',
+ featureFlagsLimit: '200',
+ featureFlagsLimitExceeded: false,
newFeatureFlagPath: 'feature-flags/new',
newUserListPath: '/user-list/new',
+ unleashApiUrl: `${TEST_HOST}/api/unleash`,
};
const mockState = {
@@ -60,6 +63,7 @@ describe('Feature flags', () => {
const configureButton = () => wrapper.find('[data-testid="ff-configure-button"]');
const newButton = () => wrapper.find('[data-testid="ff-new-button"]');
const newUserListButton = () => wrapper.find('[data-testid="ff-new-list-button"]');
+ const limitAlert = () => wrapper.find(GlAlert);
beforeEach(() => {
mock = new MockAdapter(axios);
@@ -82,28 +86,64 @@ describe('Feature flags', () => {
wrapper = null;
});
+ describe('when limit exceeded', () => {
+ const propsData = { ...mockData, featureFlagsLimitExceeded: true };
+
+ beforeEach(done => {
+ mock
+ .onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
+ .reply(200, getRequestData, {});
+ factory(propsData);
+ setImmediate(done);
+ });
+
+ it('makes the new feature flag button do nothing if clicked', () => {
+ expect(newButton().exists()).toBe(true);
+ expect(newButton().props('disabled')).toBe(false);
+ expect(newButton().props('href')).toBe(undefined);
+ });
+
+ it('shows a feature flags limit reached alert', () => {
+ expect(limitAlert().exists()).toBe(true);
+ expect(
+ limitAlert()
+ .find(GlSprintf)
+ .attributes('message'),
+ ).toContain('Feature flags limit reached');
+ });
+
+ describe('when the alert is dismissed', () => {
+ beforeEach(async () => {
+ await limitAlert().vm.$emit('dismiss');
+ });
+
+ it('hides the alert', async () => {
+ expect(limitAlert().exists()).toBe(false);
+ });
+
+ it('re-shows the alert if the new feature flag button is clicked', async () => {
+ await newButton().vm.$emit('click');
+
+ expect(limitAlert().exists()).toBe(true);
+ });
+ });
+ });
+
describe('without permissions', () => {
const propsData = {
- csrfToken: 'testToken',
- errorStateSvgPath: '/assets/illustrations/feature_flag.svg',
- featureFlagsHelpPagePath: '/help/feature-flags',
+ ...mockData,
canUserConfigure: false,
canUserRotateToken: false,
- featureFlagsClientLibrariesHelpPagePath: '/help/feature-flags#unleash-clients',
- featureFlagsClientExampleHelpPagePath: '/help/feature-flags#client-example',
- unleashApiUrl: `${TEST_HOST}/api/unleash`,
+ newFeatureFlagPath: null,
+ newUserListPath: null,
};
beforeEach(done => {
mock
.onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.reply(200, getRequestData, {});
-
factory(propsData);
-
- setImmediate(() => {
- done();
- });
+ setImmediate(done);
});
it('does not render configure button', () => {
@@ -197,9 +237,7 @@ describe('Feature flags', () => {
factory();
jest.spyOn(store, 'dispatch');
- setImmediate(() => {
- done();
- });
+ setImmediate(done);
});
it('should render a table with feature flags', () => {
@@ -267,10 +305,7 @@ describe('Feature flags', () => {
describe('in user lists tab', () => {
beforeEach(done => {
factory();
-
- setImmediate(() => {
- done();
- });
+ setImmediate(done);
});
beforeEach(() => {
wrapper.find('[data-testid="user-lists-tab"]').vm.$emit('changeTab');
@@ -295,10 +330,7 @@ describe('Feature flags', () => {
Api.fetchFeatureFlagUserLists.mockRejectedValueOnce();
factory();
-
- setImmediate(() => {
- done();
- });
+ setImmediate(done);
});
it('should render error state', () => {
@@ -329,10 +361,7 @@ describe('Feature flags', () => {
.onGet(`${TEST_HOST}/endpoint.json`, { params: { scope: FEATURE_FLAG_SCOPE, page: '1' } })
.reply(200, getRequestData, {});
factory();
-
- setImmediate(() => {
- done();
- });
+ setImmediate(done);
});
it('should fire the rotate action when a `token` event is received', () => {
diff --git a/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js b/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js
index 766a27015bb..c1ab4433761 100644
--- a/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js
+++ b/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js
@@ -1,4 +1,5 @@
import { shallowMount } from '@vue/test-utils';
+import merge from 'lodash/merge';
import { GlLink } from '@gitlab/ui';
import HighlightBar from '~/issue_show/components/incidents/highlight_bar.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
@@ -16,12 +17,17 @@ describe('Highlight Bar', () => {
title: 'Alert 1',
};
- const mountComponent = () => {
- wrapper = shallowMount(HighlightBar, {
- propsData: {
- alert,
- },
- });
+ const mountComponent = options => {
+ wrapper = shallowMount(
+ HighlightBar,
+ merge(
+ {
+ propsData: { alert },
+ provide: { fullPath: 'test', iid: 1, slaFeatureAvailable: true },
+ },
+ options,
+ ),
+ );
};
beforeEach(() => {
@@ -37,22 +43,52 @@ describe('Highlight Bar', () => {
const findLink = () => wrapper.find(GlLink);
- it('renders a link to the alert page', () => {
- expect(findLink().exists()).toBe(true);
- expect(findLink().attributes('href')).toBe(alert.detailsUrl);
- expect(findLink().attributes('title')).toBe(alert.title);
- expect(findLink().text()).toBe(`#${alert.iid}`);
+ describe('empty state', () => {
+ beforeEach(() => {
+ mountComponent({ propsData: { alert: null } });
+ });
+
+ it('renders a empty component', () => {
+ expect(wrapper.isVisible()).toBe(false);
+ });
});
- it('renders formatted start time of the alert', () => {
- const formattedDate = '2020-05-29 UTC';
- formatDate.mockReturnValueOnce(formattedDate);
- mountComponent();
- expect(formatDate).toHaveBeenCalledWith(alert.startedAt, 'yyyy-mm-dd Z');
- expect(wrapper.text()).toContain(formattedDate);
+ describe('alert present', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('renders a link to the alert page', () => {
+ expect(findLink().exists()).toBe(true);
+ expect(findLink().attributes('href')).toBe(alert.detailsUrl);
+ expect(findLink().attributes('title')).toBe(alert.title);
+ expect(findLink().text()).toBe(`#${alert.iid}`);
+ });
+
+ it('renders formatted start time of the alert', () => {
+ const formattedDate = '2020-05-29 UTC';
+ formatDate.mockReturnValueOnce(formattedDate);
+ mountComponent();
+ expect(formatDate).toHaveBeenCalledWith(alert.startedAt, 'yyyy-mm-dd Z');
+ expect(wrapper.text()).toContain(formattedDate);
+ });
+
+ it('renders a number of alert events', () => {
+ expect(wrapper.text()).toContain(alert.eventCount);
+ });
});
- it('renders a number of alert events', () => {
- expect(wrapper.text()).toContain(alert.eventCount);
+ describe('when child data is present', () => {
+ beforeEach(() => {
+ mountComponent({
+ data() {
+ return { hasChildData: true };
+ },
+ });
+ });
+
+ it('renders the highlight bar component', () => {
+ expect(wrapper.isVisible()).toBe(true);
+ });
});
});
diff --git a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
index 6babba37b57..9b22fe4e85a 100644
--- a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js
@@ -57,7 +57,6 @@ describe('Incident Tabs component', () => {
it('does not show the alert details tab', () => {
expect(findAlertDetailsComponent().exists()).toBe(false);
- expect(findHighlightBarComponent().exists()).toBe(false);
});
});
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index edb9a613f85..5ee7fb2adbf 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -30,6 +30,7 @@ issues:
- metrics
- timelogs
- issuable_severity
+- issuable_sla
- issue_assignees
- closed_by
- epic_issue
@@ -713,3 +714,5 @@ system_note_metadata:
- description_version
status_page_published_incident:
- issue
+issuable_sla:
+ - issue
diff --git a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
index 9737a0f39fc..7a9e7d8afba 100644
--- a/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
+++ b/spec/lib/gitlab/import_export/import_test_coverage_spec.rb
@@ -23,6 +23,7 @@ RSpec.describe 'Test coverage of the Project Import' do
project.issues.notes.events
project.issues.notes.events.push_event_payload
project.issues.milestone.events.push_event_payload
+ project.issues.issuable_sla
project.issues.issue_milestones
project.issues.issue_milestones.milestone
project.issues.resource_label_events.label.priorities
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 5ca7c5b7a91..e3d1f2c9368 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -855,3 +855,6 @@ ProjectSecuritySetting:
- auto_fix_sast
- created_at
- updated_at
+IssuableSla:
+ - issue_id
+ - due_at