Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-18 15:10:16 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-18 15:10:16 +0300
commitf10eb9ebaefb0d6ff4ee7552dbf127dc70aaf27d (patch)
treeddf0000b12ccb57f3926785976c4ccd6eaf3aac3
parent89eff770d213e684b5fa4df121cb51a059e8d263 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/issue_templates/QA Failure.md8
-rw-r--r--.rubocop_todo.yml5
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue5
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/presenters/snippet_blob_presenter.rb15
-rw-r--r--app/views/users/show.html.haml21
-rw-r--r--changelogs/unreleased/210550-conan-missing-files.yml5
-rw-r--r--changelogs/unreleased/237864-fj-return-snippet-binary-blob-content.yml5
-rw-r--r--changelogs/unreleased/29564-showing-horizontal-scroll-bar-in-private-profile.yml5
-rw-r--r--changelogs/unreleased/ph-235529-replyExpandsCollapsedDiscussion.yml5
-rw-r--r--changelogs/unreleased/rails-save-bang-11.yml5
-rw-r--r--changelogs/unreleased/sy-move-alert-embeds-to-core.yml5
-rw-r--r--db/migrate/20200811055018_remove_not_null_constraint_on_type_from_audit_events.rb2
-rw-r--r--doc/README.md1
-rw-r--r--doc/administration/raketasks/check.md19
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql68
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json230
-rw-r--r--doc/api/graphql/reference/index.md20
-rw-r--r--doc/development/contributing/issue_workflow.md20
-rw-r--r--doc/development/merge_request_performance_guidelines.md2
-rw-r--r--doc/user/analytics/img/merge_request_analytics_v13_3.pngbin0 -> 54156 bytes
-rw-r--r--doc/user/analytics/index.md1
-rw-r--r--doc/user/analytics/merge_request_analytics.md42
-rw-r--r--doc/user/permissions.md1
-rw-r--r--lib/api/conan_packages.rb8
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb24
-rw-r--r--lib/banzai/filter/inline_alert_metrics_filter.rb47
-rw-r--r--lib/banzai/filter/inline_metrics_redactor_filter.rb6
-rw-r--r--lib/gitlab/metrics/dashboard/url.rb51
-rw-r--r--lib/gitlab/regex.rb2
-rw-r--r--locale/gitlab.pot6
-rw-r--r--rubocop/cop/migration/with_lock_retries_disallowed_method.rb1
-rw-r--r--spec/frontend/notes/components/noteable_discussion_spec.js17
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb13
-rw-r--r--spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb21
-rw-r--r--spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb15
-rw-r--r--spec/lib/gitlab/metrics/dashboard/url_spec.rb28
-rw-r--r--spec/presenters/snippet_blob_presenter_spec.rb73
-rw-r--r--spec/requests/api/conan_packages_spec.rb82
-rw-r--r--spec/support/shared_examples/snippet_blob_shared_examples.rb21
-rw-r--r--spec/views/notify/changed_milestone_email.html.haml_spec.rb2
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb2
-rw-r--r--spec/views/projects/merge_requests/show.html.haml_spec.rb18
-rw-r--r--spec/views/shared/_label_row.html.haml_spec.rb2
45 files changed, 811 insertions, 128 deletions
diff --git a/.gitlab/issue_templates/QA Failure.md b/.gitlab/issue_templates/QA Failure.md
index 2a8b1b2d2f9..772f363ae31 100644
--- a/.gitlab/issue_templates/QA Failure.md
+++ b/.gitlab/issue_templates/QA Failure.md
@@ -68,10 +68,10 @@ a nightly pipeline, select ~"found:nightly".
<!--
https://about.gitlab.com/handbook/engineering/quality/guidelines/#priorities:
-- ~P1: Tests that are needed to verify fundamental GitLab functionality.
-- ~P2: Tests that deal with external integrations which may take a longer time to debug and fix.
+- ~P::1: Tests that are needed to verify fundamental GitLab functionality.
+- ~P::2: Tests that deal with external integrations which may take a longer time to debug and fix.
-->
-/label ~P
+/label ~P::
-<!-- Select the current milestone if ~P1 or the next milestone if ~P2. -->
+<!-- Select the current milestone if ~P::1 or the next milestone if ~P::2. -->
/milestone %
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index c2e8bfa1140..b1eb0e59e77 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -877,7 +877,6 @@ Rails/SaveBang:
- 'ee/spec/support/shared_examples/requests/api/project_approval_rules_api_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/build_execute_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/issue_epic_shared_examples.rb'
- - 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- 'ee/spec/workers/adjourned_project_deletion_worker_spec.rb'
- 'ee/spec/workers/clear_shared_runners_minutes_worker_spec.rb'
- 'ee/spec/workers/create_github_webhook_worker_spec.rb'
@@ -1342,7 +1341,3 @@ Rails/SaveBang:
- 'spec/tasks/gitlab/web_hook_rake_spec.rb'
- 'spec/uploaders/file_uploader_spec.rb'
- 'spec/uploaders/object_storage_spec.rb'
- - 'spec/views/notify/changed_milestone_email.html.haml_spec.rb'
- - 'spec/views/projects/imports/new.html.haml_spec.rb'
- - 'spec/views/projects/merge_requests/show.html.haml_spec.rb'
- - 'spec/views/shared/_label_row.html.haml_spec.rb'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 212e34658c2..379a56d288b 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-02a62b036da8980f536c12a5f163f4d0bba54c55
+8302f636d0a3f1b83cb7e5420b2720e83e564306
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 7fe50d36c0c..ad049c4449e 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -149,9 +149,14 @@ export default {
'removePlaceholderNotes',
'toggleResolveNote',
'removeConvertedDiscussion',
+ 'expandDiscussion',
]),
showReplyForm() {
this.isReplying = true;
+
+ if (!this.discussion.expanded) {
+ this.expandDiscussion({ discussionId: this.discussion.id });
+ }
},
cancelReplyForm(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index b9398bd00f9..2f28361f62c 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -166,6 +166,14 @@
color: inherit;
}
+ // TODO remove this class once we can generate a correct hover utility from `gitlab/ui`,
+ // see here: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39286#note_396767000
+ .btn-link-hover:hover {
+ * {
+ @include gl-text-blue-800;
+ }
+ }
+
.issuable-header-text {
margin-top: 7px;
}
diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb
index 6557e3a4414..abe95f5c44d 100644
--- a/app/presenters/snippet_blob_presenter.rb
+++ b/app/presenters/snippet_blob_presenter.rb
@@ -4,7 +4,6 @@ class SnippetBlobPresenter < BlobPresenter
include GitlabRoutingHelper
def rich_data
- return if blob.binary?
return unless blob.rich_viewer
render_rich_partial
@@ -17,9 +16,11 @@ class SnippetBlobPresenter < BlobPresenter
end
def raw_path
- return gitlab_raw_snippet_blob_path(snippet, blob.path) if snippet_multiple_files?
+ snippet_blob_raw_route(only_path: true)
+ end
- gitlab_raw_snippet_path(snippet)
+ def raw_url
+ snippet_blob_raw_route
end
private
@@ -38,7 +39,7 @@ class SnippetBlobPresenter < BlobPresenter
def render_rich_partial
renderer.render("projects/blob/viewers/_#{blob.rich_viewer.partial_name}",
- locals: { viewer: blob.rich_viewer, blob: blob, blob_raw_path: raw_path },
+ locals: { viewer: blob.rich_viewer, blob: blob, blob_raw_path: raw_path, blob_raw_url: raw_url },
layout: false)
end
@@ -49,4 +50,10 @@ class SnippetBlobPresenter < BlobPresenter
ApplicationController.renderer.new('warden' => proxy)
end
+
+ def snippet_blob_raw_route(only_path: false)
+ return gitlab_raw_snippet_blob_url(snippet, blob.path, only_path: only_path) if snippet_multiple_files?
+
+ gitlab_raw_snippet_url(snippet, only_path: only_path)
+ end
end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 94482717d0e..e1d1df9de1a 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -160,15 +160,12 @@
.loading.hide
.spinner.spinner-md
- - if profile_tabs.empty?
- .row
- .col-12
- .svg-content
- = image_tag 'illustrations/profile_private_mode.svg'
- .col-12.text-center
- .text-content
- %h4
- - if @user.blocked?
- = s_('UserProfile|This user is blocked')
- - else
- = s_('UserProfile|This user has a private profile')
+ - if profile_tabs.empty?
+ .svg-content
+ = image_tag 'illustrations/profile_private_mode.svg'
+ .text-content.text-center
+ %h4
+ - if @user.blocked?
+ = s_('UserProfile|This user is blocked')
+ - else
+ = s_('UserProfile|This user has a private profile')
diff --git a/changelogs/unreleased/210550-conan-missing-files.yml b/changelogs/unreleased/210550-conan-missing-files.yml
new file mode 100644
index 00000000000..f5d8d13e4a7
--- /dev/null
+++ b/changelogs/unreleased/210550-conan-missing-files.yml
@@ -0,0 +1,5 @@
+---
+title: Conan packages allow for conan_sources.tgz and conan_export.tgz files
+merge_request: 39559
+author:
+type: fixed
diff --git a/changelogs/unreleased/237864-fj-return-snippet-binary-blob-content.yml b/changelogs/unreleased/237864-fj-return-snippet-binary-blob-content.yml
new file mode 100644
index 00000000000..4ca5683a7f6
--- /dev/null
+++ b/changelogs/unreleased/237864-fj-return-snippet-binary-blob-content.yml
@@ -0,0 +1,5 @@
+---
+title: Return snippet binary blob content in GraphQL
+merge_request: 39583
+author:
+type: changed
diff --git a/changelogs/unreleased/29564-showing-horizontal-scroll-bar-in-private-profile.yml b/changelogs/unreleased/29564-showing-horizontal-scroll-bar-in-private-profile.yml
new file mode 100644
index 00000000000..c0907170fb6
--- /dev/null
+++ b/changelogs/unreleased/29564-showing-horizontal-scroll-bar-in-private-profile.yml
@@ -0,0 +1,5 @@
+---
+title: Fix horizontal scrolling on blocked/private profile pages
+merge_request: 39568
+author:
+type: fixed
diff --git a/changelogs/unreleased/ph-235529-replyExpandsCollapsedDiscussion.yml b/changelogs/unreleased/ph-235529-replyExpandsCollapsedDiscussion.yml
new file mode 100644
index 00000000000..f43c77d68ca
--- /dev/null
+++ b/changelogs/unreleased/ph-235529-replyExpandsCollapsedDiscussion.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed discussion not expanding when replying to a collapsed discussion
+merge_request: 39571
+author:
+type: fixed
diff --git a/changelogs/unreleased/rails-save-bang-11.yml b/changelogs/unreleased/rails-save-bang-11.yml
new file mode 100644
index 00000000000..8e270df08cd
--- /dev/null
+++ b/changelogs/unreleased/rails-save-bang-11.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor spec/views/* and ee/spec/views/* to fix Rails/SaveBang Cop
+merge_request: 38981
+author: Rajendra Kadam
+type: fixed
diff --git a/changelogs/unreleased/sy-move-alert-embeds-to-core.yml b/changelogs/unreleased/sy-move-alert-embeds-to-core.yml
new file mode 100644
index 00000000000..be17f7ae68b
--- /dev/null
+++ b/changelogs/unreleased/sy-move-alert-embeds-to-core.yml
@@ -0,0 +1,5 @@
+---
+title: Move gitlab-managed alerts embeds to core as documented
+merge_request: 39509
+author:
+type: fixed
diff --git a/db/migrate/20200811055018_remove_not_null_constraint_on_type_from_audit_events.rb b/db/migrate/20200811055018_remove_not_null_constraint_on_type_from_audit_events.rb
index 629f357cd19..25c3ef71655 100644
--- a/db/migrate/20200811055018_remove_not_null_constraint_on_type_from_audit_events.rb
+++ b/db/migrate/20200811055018_remove_not_null_constraint_on_type_from_audit_events.rb
@@ -7,7 +7,6 @@ class RemoveNotNullConstraintOnTypeFromAuditEvents < ActiveRecord::Migration[6.0
disable_ddl_transaction!
- # rubocop:disable Migration/WithLockRetriesDisallowedMethod
# To avoid deadlock on audit_event and audit_event_part... since there is a trigger to insert record from audit_events
# to audit_events_part..., we need to ensure each ALTER TABLE command run in its own transaction.
def up
@@ -19,7 +18,6 @@ class RemoveNotNullConstraintOnTypeFromAuditEvents < ActiveRecord::Migration[6.0
change_column_null :audit_events, :type, true
end
end
- # rubocop:enable Migration/WithLockRetriesDisallowedMethod
def down
# no-op -- null values might be added after this constraint is removed.
diff --git a/doc/README.md b/doc/README.md
index 5e5d76374a2..49170b75dbd 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -157,6 +157,7 @@ The following documentation relates to the DevOps **Create** stage:
| [GitLab Pages](user/project/pages/index.md) | Build, test, and deploy your static website with GitLab Pages. |
| [Groups](user/group/index.md) and [Subgroups](user/group/subgroups/index.md) | Organize your projects in groups. |
| [Issues Analytics](user/group/issues_analytics/index.md) **(PREMIUM)** | Check how many issues were created per month. |
+| [Merge Request Analytics](user/analytics/merge_request_analytics.md) **(PREMIUM)** | Check your throughput productivity - how many merge requests were merged per month. |
| [Projects](user/project/index.md), including [project access](public_access/public_access.md)<br/>and [settings](user/project/settings/index.md) | Host source code, and control your project's visibility and set configuration. |
| [Search through GitLab](user/search/index.md) | Search for issues, merge requests, projects, groups, and todos. |
| [Snippets](user/snippets.md) | Snippets allow you to create little bits of code. |
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index da5caf3966f..15014fffd01 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -135,3 +135,22 @@ The LDAP check Rake task will test the bind DN and password credentials
(if configured) and will list a sample of LDAP users. This task is also
executed as part of the `gitlab:check` task, but can run independently.
See [LDAP Rake Tasks - LDAP Check](ldap.md#check) for details.
+
+## Troubleshooting
+
+The following are solutions to problems you might discover using the Rake tasks documented
+above.
+
+### Dangling commits
+
+`gitlab:git:fsck` can find dangling commits. To fix them, try
+[manually triggering housekeeping](../housekeeping.md#manual-housekeeping)
+for the affected project(s).
+
+If the issue persists, try triggering `gc` via the
+[Rails Console](../troubleshooting/navigating_gitlab_via_rails_console.md#starting-a-rails-console-session):
+
+```ruby
+p = Project.find_by_path("project-name")
+Projects::HousekeepingService.new(p, :gc).execute
+```
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index 387867309b8..43c50002779 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -1628,6 +1628,33 @@ type CiStageEdge {
node: CiStage
}
+type ClusterAgent {
+ """
+ Timestamp the cluster agent was created
+ """
+ createdAt: Time
+
+ """
+ ID of the cluster agent
+ """
+ id: ID!
+
+ """
+ Name of the cluster agent
+ """
+ name: String
+
+ """
+ The project this cluster agent is associated with
+ """
+ project: Project
+
+ """
+ Timestamp the cluster agent was updated
+ """
+ updatedAt: Time
+}
+
type Commit {
"""
Author of the commit
@@ -2261,6 +2288,46 @@ type CreateBranchPayload {
}
"""
+Autogenerated input type of CreateClusterAgent
+"""
+input CreateClusterAgentInput {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Name of the cluster agent
+ """
+ name: String!
+
+ """
+ Full path of the associated project for this cluster agent
+ """
+ projectPath: ID!
+}
+
+"""
+Autogenerated return type of CreateClusterAgent
+"""
+type CreateClusterAgentPayload {
+ """
+ A unique identifier for the client performing the mutation.
+ """
+ clientMutationId: String
+
+ """
+ Cluster agent created after mutation
+ """
+ clusterAgent: ClusterAgent
+
+ """
+ Errors encountered during execution of the mutation.
+ """
+ errors: [String!]!
+}
+
+"""
Autogenerated input type of CreateDiffNote
"""
input CreateDiffNoteInput {
@@ -9519,6 +9586,7 @@ type Mutation {
createAlertIssue(input: CreateAlertIssueInput!): CreateAlertIssuePayload
createAnnotation(input: CreateAnnotationInput!): CreateAnnotationPayload
createBranch(input: CreateBranchInput!): CreateBranchPayload
+ createClusterAgent(input: CreateClusterAgentInput!): CreateClusterAgentPayload
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
createEpic(input: CreateEpicInput!): CreateEpicPayload
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index b2d2bd61d5b..f4282facd30 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -4433,6 +4433,93 @@
},
{
"kind": "OBJECT",
+ "name": "ClusterAgent",
+ "description": null,
+ "fields": [
+ {
+ "name": "createdAt",
+ "description": "Timestamp the cluster agent was created",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "id",
+ "description": "ID of the cluster agent",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the cluster agent",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "project",
+ "description": "The project this cluster agent is associated with",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "Project",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "updatedAt",
+ "description": "Timestamp the cluster agent was updated",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "Time",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
"name": "Commit",
"description": null,
"fields": [
@@ -6060,6 +6147,122 @@
},
{
"kind": "INPUT_OBJECT",
+ "name": "CreateClusterAgentInput",
+ "description": "Autogenerated input type of CreateClusterAgent",
+ "fields": null,
+ "inputFields": [
+ {
+ "name": "projectPath",
+ "description": "Full path of the associated project for this cluster agent",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "ID",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "name",
+ "description": "Name of the cluster agent",
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ },
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "defaultValue": null
+ }
+ ],
+ "interfaces": null,
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "OBJECT",
+ "name": "CreateClusterAgentPayload",
+ "description": "Autogenerated return type of CreateClusterAgent",
+ "fields": [
+ {
+ "name": "clientMutationId",
+ "description": "A unique identifier for the client performing the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "clusterAgent",
+ "description": "Cluster agent created after mutation",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "ClusterAgent",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
+ "name": "errors",
+ "description": "Errors encountered during execution of the mutation.",
+ "args": [
+
+ ],
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "LIST",
+ "name": null,
+ "ofType": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "SCALAR",
+ "name": "String",
+ "ofType": null
+ }
+ }
+ }
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ }
+ ],
+ "inputFields": null,
+ "interfaces": [
+
+ ],
+ "enumValues": null,
+ "possibleTypes": null
+ },
+ {
+ "kind": "INPUT_OBJECT",
"name": "CreateDiffNoteInput",
"description": "Autogenerated input type of CreateDiffNote",
"fields": null,
@@ -26994,6 +27197,33 @@
"deprecationReason": null
},
{
+ "name": "createClusterAgent",
+ "description": null,
+ "args": [
+ {
+ "name": "input",
+ "description": null,
+ "type": {
+ "kind": "NON_NULL",
+ "name": null,
+ "ofType": {
+ "kind": "INPUT_OBJECT",
+ "name": "CreateClusterAgentInput",
+ "ofType": null
+ }
+ },
+ "defaultValue": null
+ }
+ ],
+ "type": {
+ "kind": "OBJECT",
+ "name": "CreateClusterAgentPayload",
+ "ofType": null
+ },
+ "isDeprecated": false,
+ "deprecationReason": null
+ },
+ {
"name": "createDiffNote",
"description": null,
"args": [
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index dd2fa5e191b..b766007c7de 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -264,6 +264,16 @@ Autogenerated return type of BoardListUpdateLimitMetrics
| --- | ---- | ---------- |
| `name` | String | Name of the stage |
+## ClusterAgent
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `createdAt` | Time | Timestamp the cluster agent was created |
+| `id` | ID! | ID of the cluster agent |
+| `name` | String | Name of the cluster agent |
+| `project` | Project | The project this cluster agent is associated with |
+| `updatedAt` | Time | Timestamp the cluster agent was updated |
+
## Commit
| Name | Type | Description |
@@ -360,6 +370,16 @@ Autogenerated return type of CreateBranch
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+## CreateClusterAgentPayload
+
+Autogenerated return type of CreateClusterAgent
+
+| Name | Type | Description |
+| --- | ---- | ---------- |
+| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
+| `clusterAgent` | ClusterAgent | Cluster agent created after mutation |
+| `errors` | String! => Array | Errors encountered during execution of the mutation. |
+
## CreateDiffNotePayload
Autogenerated return type of CreateDiffNote
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 9feaa485bd2..76175cb7b66 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -49,8 +49,8 @@ Most issues will have labels for at least one of the following:
- Team: `~"Technical Writing"`, `~Delivery`
- Specialization: `~frontend`, `~backend`, `~documentation`
- Release Scoping: `~Deliverable`, `~Stretch`, `~"Next Patch Release"`
-- Priority: `~P1`, `~P2`, `~P3`, `~P4`
-- Severity: ~`S1`, `~S2`, `~S3`, `~S4`
+- Priority: `~P::1`, `~P::2`, `~P::3`, `~P::4`
+- Severity: ~`S::1`, `~S::2`, `~S::3`, `~S::4`
All labels, their meaning and priority are defined on the
[labels page](https://gitlab.com/gitlab-org/gitlab/-/labels).
@@ -275,10 +275,10 @@ or ~"Stretch". Any open issue for a previous milestone should be labeled
We have the following priority labels:
-- ~P1
-- ~P2
-- ~P3
-- ~P4
+- ~P::1
+- ~P::2
+- ~P::3
+- ~P::4
Please refer to the issue triage [priority label](https://about.gitlab.com/handbook/engineering/quality/issue-triage/#priority) section in our handbook to see how it's used.
@@ -286,10 +286,10 @@ Please refer to the issue triage [priority label](https://about.gitlab.com/handb
We have the following severity labels:
-- ~S1
-- ~S2
-- ~S3
-- ~S4
+- ~S::1
+- ~S::2
+- ~S::3
+- ~S::4
Please refer to the issue triage [severity label](https://about.gitlab.com/handbook/engineering/quality/issue-triage/#severity) section in our handbook to see how it's used.
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index ed6d08a9894..b3829a82d59 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -211,7 +211,7 @@ For keeping transaction as minimal as possible, please consider using `AfterComm
module or `after_commit` AR hook.
Here is [an example](https://gitlab.com/gitlab-org/gitlab/-/issues/36154#note_247228859)
-that one request to Gitaly instance during transaction triggered a P1 issue.
+that one request to Gitaly instance during transaction triggered a P::1 issue.
## Eager Loading
diff --git a/doc/user/analytics/img/merge_request_analytics_v13_3.png b/doc/user/analytics/img/merge_request_analytics_v13_3.png
new file mode 100644
index 00000000000..f90f3625a51
--- /dev/null
+++ b/doc/user/analytics/img/merge_request_analytics_v13_3.png
Binary files differ
diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md
index 9e3d2c6039e..96aff919ccd 100644
--- a/doc/user/analytics/index.md
+++ b/doc/user/analytics/index.md
@@ -38,6 +38,7 @@ The following analytics features are available at the project level:
- [Code Review](code_review_analytics.md). **(STARTER)**
- [Insights](../group/insights/index.md). **(ULTIMATE)**
- [Issues](../group/issues_analytics/index.md). **(PREMIUM)**
+- [Merge Request](merge_request_analytics.md). **(STARTER)**
- [Repository](repository_analytics.md).
- [Value Stream](value_stream_analytics.md), enabled with the `cycle_analytics`
[feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-locally-in-development). **(STARTER)**
diff --git a/doc/user/analytics/merge_request_analytics.md b/doc/user/analytics/merge_request_analytics.md
new file mode 100644
index 00000000000..01295ae888b
--- /dev/null
+++ b/doc/user/analytics/merge_request_analytics.md
@@ -0,0 +1,42 @@
+---
+description: "Merge Request Analytics help you understand the efficiency of your code review process, and the productivity of your team." # Up to ~200 chars long. They will be displayed in Google Search snippets. It may help to write the page intro first, and then reuse it here.
+stage: Manage
+group: Analytics
+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
+---
+
+
+# Merge Request Analytics **(STARTER)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229045) in [GitLab Starter](https://about.gitlab.com/pricing/) 13.3.
+
+Merge Request Analytics helps you understand the efficiency of your code review process, and the productivity of your team.
+
+## Overview
+
+Merge Request Analytics displays information about all accepted merge requests.
+
+The Throughput chart shows the number of completed merge requests, by month. Merge request throughput is
+a common measure of productivity in software engineering. Although imperfect, the average throughput can
+be a meaningful benchmark of your team's overall productivity.
+
+To access Merge Request Analytics, from your project's menu, go to **Analytics > Merge Request**.
+
+![Merge Request Analytics](img/merge_request_analytics_v13_3.png "Merge Request Analytics - Throughput chart")
+
+## Use cases
+
+This feature is designed for [development team leaders](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
+and others who want to understand broad patterns in code review and productivity.
+
+You can use Merge Request Analytics to expose when your team is most and least productive, and
+identify improvements that might substantially accelerate your development cycle.
+
+Merge Request Analytics could be used when:
+
+- You want to know if you were more productive this month than last month, or 12 months ago.
+- You want to drill into low- or high-productivity months to understand the work that took place.
+
+## Permissions
+
+- On [Starter or Bronze tier](https://about.gitlab.com/pricing/) and above.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 39b61bd7bee..0b541eb248a 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -168,6 +168,7 @@ The following table depicts the various user permission levels in a project.
| View Code Review analytics **(STARTER)** | | ✓ | ✓ | ✓ | ✓ |
| View Insights **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Issues analytics **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View Merge Request analytics **(STARTER)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Repository analytics | | ✓ | ✓ | ✓ | ✓ |
| View Value Stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
diff --git a/lib/api/conan_packages.rb b/lib/api/conan_packages.rb
index 1d941e422a7..6888929874f 100644
--- a/lib/api/conan_packages.rb
+++ b/lib/api/conan_packages.rb
@@ -189,9 +189,7 @@ module API
authorize!(:read_package, project)
status 200
- upload_urls = package_upload_urls(::Packages::Conan::FileMetadatum::PACKAGE_FILES)
-
- present upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
end
desc 'Recipe Upload Urls' do
@@ -202,9 +200,7 @@ module API
authorize!(:read_package, project)
status 200
- upload_urls = recipe_upload_urls(::Packages::Conan::FileMetadatum::RECIPE_FILES)
-
- present upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
+ present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
end
desc 'Delete Package' do
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 30e690a5a1d..a5fde1af41e 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -28,22 +28,30 @@ module API
present_download_urls(::API::Entities::ConanPackage::ConanRecipeManifest, &:recipe_urls)
end
- def recipe_upload_urls(file_names)
+ def recipe_upload_urls
{ upload_urls: Hash[
- file_names.collect do |file_name|
+ file_names.select(&method(:recipe_file?)).map do |file_name|
[file_name, recipe_file_upload_url(file_name)]
end
] }
end
- def package_upload_urls(file_names)
+ def package_upload_urls
{ upload_urls: Hash[
- file_names.collect do |file_name|
+ file_names.select(&method(:package_file?)).map do |file_name|
[file_name, package_file_upload_url(file_name)]
end
] }
end
+ def recipe_file?(file_name)
+ file_name.in?(::Packages::Conan::FileMetadatum::RECIPE_FILES)
+ end
+
+ def package_file?(file_name)
+ file_name.in?(::Packages::Conan::FileMetadatum::PACKAGE_FILES)
+ end
+
def package_file_upload_url(file_name)
expose_url(
api_v4_packages_conan_v1_files_package_path(
@@ -130,6 +138,14 @@ module API
end
end
+ def file_names
+ json_payload = Gitlab::Json.parse(request.body.string)
+
+ bad_request!(nil) unless json_payload.is_a?(Hash)
+
+ json_payload.keys
+ end
+
def create_package_file_with_type(file_type, current_package)
unless params['file.size'] == 0
# conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
diff --git a/lib/banzai/filter/inline_alert_metrics_filter.rb b/lib/banzai/filter/inline_alert_metrics_filter.rb
new file mode 100644
index 00000000000..a6140d1ac81
--- /dev/null
+++ b/lib/banzai/filter/inline_alert_metrics_filter.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ # HTML filter that inserts a placeholder element for each
+ # reference to an alert dashboard.
+ class InlineAlertMetricsFilter < ::Banzai::Filter::InlineEmbedsFilter
+ include ::Gitlab::Routing
+ # Search params for selecting alert metrics links. A few
+ # simple checks is enough to boost performance without
+ # the cost of doing a full regex match.
+ def xpath_search
+ "descendant-or-self::a[contains(@href,'metrics_dashboard') and \
+ contains(@href,'prometheus/alerts') and \
+ starts-with(@href, '#{gitlab_domain}')]"
+ end
+
+ # Regular expression matching alert dashboard urls
+ def link_pattern
+ ::Gitlab::Metrics::Dashboard::Url.alert_regex
+ end
+
+ private
+
+ # Endpoint FE should hit to collect the appropriate
+ # chart information
+ def metrics_dashboard_url(params)
+ metrics_dashboard_namespace_project_prometheus_alert_url(
+ params['namespace'],
+ params['project'],
+ params['alert'],
+ embedded: true,
+ format: :json,
+ **query_params(params['url'])
+ )
+ end
+
+ # Parses query params out from full url string into hash.
+ #
+ # Ex) 'https://<root>/<project>/metrics_dashboard?title=Title&group=Group'
+ # --> { title: 'Title', group: 'Group' }
+ def query_params(url)
+ ::Gitlab::Metrics::Dashboard::Url.parse_query(url)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/inline_metrics_redactor_filter.rb b/lib/banzai/filter/inline_metrics_redactor_filter.rb
index 7f98a52d421..2259115acfc 100644
--- a/lib/banzai/filter/inline_metrics_redactor_filter.rb
+++ b/lib/banzai/filter/inline_metrics_redactor_filter.rb
@@ -81,6 +81,10 @@ module Banzai
Route.new(
::Gitlab::Metrics::Dashboard::Url.clusters_regex,
:read_cluster
+ ),
+ Route.new(
+ ::Gitlab::Metrics::Dashboard::Url.alert_regex,
+ :read_prometheus_alerts
)
]
end
@@ -147,5 +151,3 @@ module Banzai
end
end
end
-
-Banzai::Filter::InlineMetricsRedactorFilter.prepend_if_ee('EE::Banzai::Filter::InlineMetricsRedactorFilter')
diff --git a/lib/gitlab/metrics/dashboard/url.rb b/lib/gitlab/metrics/dashboard/url.rb
index 94f586614f7..160ecfb85c9 100644
--- a/lib/gitlab/metrics/dashboard/url.rb
+++ b/lib/gitlab/metrics/dashboard/url.rb
@@ -43,6 +43,39 @@ module Gitlab
end
end
+ # Matches dashboard urls for a metric chart embed
+ # for cluster metrics
+ #
+ # EX - https://<host>/<namespace>/<project>/-/clusters/<cluster_id>/?group=Cluster%20Health&title=Memory%20Usage&y_label=Memory%20(GiB)
+ def clusters_regex
+ strong_memoize(:clusters_regex) do
+ regex_for_project_metrics(
+ %r{
+ /clusters
+ /(?<cluster_id>\d+)
+ /?
+ }x
+ )
+ end
+ end
+
+ # Matches dashboard urls for a metric chart embed
+ # for a specifc firing GitLab alert
+ #
+ # EX - https://<host>/<namespace>/<project>/prometheus/alerts/<alert_id>/metrics_dashboard
+ def alert_regex
+ strong_memoize(:alert_regex) do
+ regex_for_project_metrics(
+ %r{
+ /prometheus
+ /alerts
+ /(?<alert>\d+)
+ /metrics_dashboard
+ }x
+ )
+ end
+ end
+
# Parses query params out from full url string into hash.
#
# Ex) 'https://<root>/<project>/<environment>/metrics?title=Title&group=Group'
@@ -60,22 +93,6 @@ module Gitlab
Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_environment_url(*args)
end
- # Matches dashboard urls for a metric chart embed
- # for cluster metrics
- #
- # EX - https://<host>/<namespace>/<project>/-/clusters/<cluster_id>/?group=Cluster%20Health&title=Memory%20Usage&y_label=Memory%20(GiB)
- def clusters_regex
- strong_memoize(:clusters_regex) do
- regex_for_project_metrics(
- %r{
- /clusters
- /(?<cluster_id>\d+)
- /?
- }x
- )
- end
- end
-
private
def regex_for_project_metrics(path_suffix_pattern)
@@ -107,5 +124,3 @@ module Gitlab
end
end
end
-
-Gitlab::Metrics::Dashboard::Url.extend_if_ee('::EE::Gitlab::Metrics::Dashboard::Url')
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 89f19cf86fc..1e1e0d856b7 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -3,7 +3,7 @@
module Gitlab
module Regex
module Packages
- CONAN_RECIPE_FILES = %w[conanfile.py conanmanifest.txt].freeze
+ CONAN_RECIPE_FILES = %w[conanfile.py conanmanifest.txt conan_sources.tgz conan_export.tgz].freeze
CONAN_PACKAGE_FILES = %w[conaninfo.txt conanmanifest.txt conan_package.tgz].freeze
def conan_file_name_regex
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c76c21a7fe9..ff2627e69a5 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5105,6 +5105,12 @@ msgstr ""
msgid "Cluster type must be specificed for Stages::ClusterEndpointInserter"
msgstr ""
+msgid "ClusterAgent|This feature is only available for premium plans"
+msgstr ""
+
+msgid "ClusterAgent|You have insufficient permissions to create a cluster agent for this project"
+msgstr ""
+
msgid "ClusterIntegration| This will permanently delete the following resources: <ul> <li>All installed applications and related resources</li> <li>The <code>gitlab-managed-apps</code> namespace</li> <li>Any project namespaces</li> <li><code>clusterroles</code></li> <li><code>clusterrolebindings</code></li> </ul>"
msgstr ""
diff --git a/rubocop/cop/migration/with_lock_retries_disallowed_method.rb b/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
index 9bf81e7db0c..a89c33c298b 100644
--- a/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
+++ b/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
@@ -18,6 +18,7 @@ module RuboCop
remove_column
execute
change_column_default
+ change_column_null
remove_foreign_key_if_exists
remove_foreign_key_without_error
table_exists?
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index 817a6b09056..1c6603899d3 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -89,6 +89,23 @@ describe('noteable_discussion component', () => {
});
});
+ it('should expand discussion', async () => {
+ const expandDiscussion = jest.fn();
+ const discussion = { ...discussionMock };
+ discussion.expanded = false;
+
+ wrapper.setProps({ discussion });
+ wrapper.setMethods({ expandDiscussion });
+
+ await wrapper.vm.$nextTick();
+
+ wrapper.vm.showReplyForm();
+
+ await wrapper.vm.$nextTick();
+
+ expect(expandDiscussion).toHaveBeenCalledWith({ discussionId: discussion.id });
+ });
+
it('does not render jump to thread button', () => {
expect(wrapper.find('*[data-original-title="Jump to next unresolved thread"]').exists()).toBe(
false,
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index 888806c4a45..1ad7c7bb9ff 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -224,21 +224,10 @@ RSpec.describe GitlabRoutingHelper do
subject { gitlab_raw_snippet_blob_url(snippet, blob.path, ref, args) }
- context 'for a PersonalSnippet' do
- let(:snippet) { personal_snippet }
-
- it { expect(subject).to eq("http://test.host/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}") }
- end
-
- context 'for a ProjectSnippet' do
- let(:snippet) { project_snippet }
-
- it { expect(subject).to eq("http://test.host/#{snippet.project.full_path}/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}") }
- end
+ it_behaves_like 'snippet blob raw url'
context 'when an argument is set' do
let(:args) { { inline: true } }
-
let(:snippet) { personal_snippet }
it { expect(subject).to eq("http://test.host/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}?inline=true") }
diff --git a/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb b/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb
new file mode 100644
index 00000000000..be40195f001
--- /dev/null
+++ b/spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Banzai::Filter::InlineAlertMetricsFilter do
+ include FilterSpecHelper
+
+ let(:params) { ['foo', 'bar', 12] }
+ let(:query_params) { {} }
+
+ let(:trigger_url) { urls.metrics_dashboard_namespace_project_prometheus_alert_url(*params, query_params) }
+ let(:dashboard_url) { urls.metrics_dashboard_namespace_project_prometheus_alert_url(*params, **query_params, embedded: true, format: :json) }
+
+ it_behaves_like 'a metrics embed filter'
+
+ context 'with query params specified' do
+ let(:query_params) { { timestamp: 'yesterday' } }
+
+ it_behaves_like 'a metrics embed filter'
+ end
+end
diff --git a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
index cafcaef8ae2..5f66844f498 100644
--- a/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb
@@ -74,5 +74,20 @@ RSpec.describe Banzai::Filter::InlineMetricsRedactorFilter do
end
end
end
+
+ context 'for an alert embed' do
+ let_it_be(:alert) { create(:prometheus_alert, project: project) }
+ let(:url) do
+ urls.metrics_dashboard_project_prometheus_alert_url(
+ project,
+ alert.prometheus_metric_id,
+ environment_id: alert.environment_id,
+ embedded: true
+ )
+ end
+
+ it_behaves_like 'redacts the embed placeholder'
+ it_behaves_like 'retains the embed placeholder when applicable'
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/dashboard/url_spec.rb b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
index 56556423b05..205e1000376 100644
--- a/spec/lib/gitlab/metrics/dashboard/url_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/url_spec.rb
@@ -102,6 +102,34 @@ RSpec.describe Gitlab::Metrics::Dashboard::Url do
it_behaves_like 'regex which matches url when expected'
end
+ describe '#alert_regex' do
+ let(:url) do
+ Gitlab::Routing.url_helpers.metrics_dashboard_namespace_project_prometheus_alert_url(
+ 'foo',
+ 'bar',
+ '1',
+ start: '2020-02-10T12:59:49.938Z',
+ end: '2020-02-10T20:59:49.938Z',
+ anchor: "anchor"
+ )
+ end
+
+ let(:expected_params) do
+ {
+ 'url' => url,
+ 'namespace' => 'foo',
+ 'project' => 'bar',
+ 'alert' => '1',
+ 'query' => "?end=2020-02-10T20%3A59%3A49.938Z&start=2020-02-10T12%3A59%3A49.938Z",
+ 'anchor' => '#anchor'
+ }
+ end
+
+ subject { described_class.alert_regex }
+
+ it_behaves_like 'regex which matches url when expected'
+ end
+
describe '#build_dashboard_url' do
it 'builds the url for the dashboard endpoint' do
url = described_class.build_dashboard_url('foo', 'bar', 1)
diff --git a/spec/presenters/snippet_blob_presenter_spec.rb b/spec/presenters/snippet_blob_presenter_spec.rb
index 00bdb982147..915f43fe572 100644
--- a/spec/presenters/snippet_blob_presenter_spec.rb
+++ b/spec/presenters/snippet_blob_presenter_spec.rb
@@ -13,13 +13,14 @@ RSpec.describe SnippetBlobPresenter do
subject { described_class.new(snippet.blob).rich_data }
context 'with PersonalSnippet' do
- let(:raw_url) { "http://127.0.0.1:3000/-/snippets/#{snippet.id}/raw" }
- let(:snippet) { build(:personal_snippet) }
+ let(:snippet) { create(:personal_snippet, :repository) }
- it 'returns nil when the snippet blob is binary' do
- allow(snippet.blob).to receive(:binary?).and_return(true)
+ context 'when blob is binary' do
+ it 'returns the HTML associated with the binary' do
+ allow(snippet).to receive(:blob).and_return(snippet.repository.blob_at('master', 'files/images/logo-black.png'))
- expect(subject).to be_nil
+ expect(subject).to include('file-content image_file')
+ end
end
context 'with markdown format' do
@@ -108,7 +109,7 @@ RSpec.describe SnippetBlobPresenter do
end
end
- describe '#raw_path' do
+ describe 'route helpers' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
@@ -118,28 +119,62 @@ RSpec.describe SnippetBlobPresenter do
project.add_developer(user)
end
- subject { described_class.new(snippet.blobs.first, current_user: user).raw_path }
+ describe '#raw_path' do
+ subject { described_class.new(snippet.blobs.first, current_user: user).raw_path }
+
+ it_behaves_like 'snippet blob raw path'
+
+ context 'with snippet_multiple_files feature disabled' do
+ before do
+ stub_feature_flags(snippet_multiple_files: false)
+ end
+
+ context 'with ProjectSnippet' do
+ let(:snippet) { project_snippet }
+
+ it 'returns the raw path' do
+ expect(subject).to eq "/#{snippet.project.full_path}/-/snippets/#{snippet.id}/raw"
+ end
+ end
+
+ context 'with PersonalSnippet' do
+ let(:snippet) { personal_snippet }
- it_behaves_like 'snippet blob raw path'
+ it 'returns the raw path' do
+ expect(subject).to eq "/-/snippets/#{snippet.id}/raw"
+ end
+ end
+ end
+ end
+
+ describe '#raw_url' do
+ subject { described_class.new(snippet.blobs.first, current_user: user).raw_url }
- context 'with snippet_multiple_files feature disabled' do
before do
- stub_feature_flags(snippet_multiple_files: false)
+ stub_default_url_options(host: 'test.host')
end
- context 'with ProjectSnippet' do
- let(:snippet) { project_snippet }
+ it_behaves_like 'snippet blob raw url'
- it 'returns the raw path' do
- expect(subject).to eq "/#{snippet.project.full_path}/-/snippets/#{snippet.id}/raw"
+ context 'with snippet_multiple_files feature disabled' do
+ before do
+ stub_feature_flags(snippet_multiple_files: false)
+ end
+
+ context 'with ProjectSnippet' do
+ let(:snippet) { project_snippet }
+
+ it 'returns the raw project snippet url' do
+ expect(subject).to eq("http://test.host/#{project_snippet.project.full_path}/-/snippets/#{project_snippet.id}/raw")
+ end
end
- end
- context 'with PersonalSnippet' do
- let(:snippet) { personal_snippet }
+ context 'with PersonalSnippet' do
+ let(:snippet) { personal_snippet }
- it 'returns the raw path' do
- expect(subject).to eq "/-/snippets/#{snippet.id}/raw"
+ it 'returns the raw personal snippet url' do
+ expect(subject).to eq("http://test.host/-/snippets/#{personal_snippet.id}/raw")
+ end
end
end
end
diff --git a/spec/requests/api/conan_packages_spec.rb b/spec/requests/api/conan_packages_spec.rb
index ac6a4171751..95798b060f1 100644
--- a/spec/requests/api/conan_packages_spec.rb
+++ b/spec/requests/api/conan_packages_spec.rb
@@ -331,6 +331,18 @@ RSpec.describe API::ConanPackages do
.and_return(presenter)
end
+ shared_examples 'rejects invalid upload_url params' do
+ context 'with unaccepted json format' do
+ let(:params) { %w[foo bar] }
+
+ it 'returns 400' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
+
describe 'GET /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
let(:recipe_path) { package.conan_recipe_path }
@@ -418,13 +430,14 @@ RSpec.describe API::ConanPackages do
let(:recipe_path) { package.conan_recipe_path }
let(:params) do
- { "conanfile.py": 24,
- "conanmanifext.txt": 123 }
+ { 'conanfile.py': 24,
+ 'conanmanifest.txt': 123 }
end
- subject { post api("/packages/conan/v1/conans/#{recipe_path}/upload_urls"), params: params, headers: headers }
+ subject { post api("/packages/conan/v1/conans/#{recipe_path}/upload_urls"), params: params.to_json, headers: headers }
it_behaves_like 'rejects invalid recipe'
+ it_behaves_like 'rejects invalid upload_url params'
it 'returns a set of upload urls for the files requested' do
subject
@@ -436,20 +449,58 @@ RSpec.describe API::ConanPackages do
expect(response.body).to eq(expected_response.to_json)
end
+
+ context 'with conan_sources and conan_export files' do
+ let(:params) do
+ { 'conan_sources.tgz': 345,
+ 'conan_export.tgz': 234,
+ 'conanmanifest.txt': 123 }
+ end
+
+ it 'returns upload urls for the additional files' do
+ subject
+
+ expected_response = {
+ 'conan_sources.tgz': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_sources.tgz",
+ 'conan_export.tgz': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_export.tgz",
+ 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
+ }
+
+ expect(response.body).to eq(expected_response.to_json)
+ end
+ end
+
+ context 'with an invalid file' do
+ let(:params) do
+ { 'invalid_file.txt': 10,
+ 'conanmanifest.txt': 123 }
+ end
+
+ it 'does not return the invalid file as an upload_url' do
+ subject
+
+ expected_response = {
+ 'conanmanifest.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
+ }
+
+ expect(response.body).to eq(expected_response.to_json)
+ end
+ end
end
describe 'POST /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel/packages/:conan_package_reference/upload_urls' do
let(:recipe_path) { package.conan_recipe_path }
let(:params) do
- { "conaninfo.txt": 24,
- "conanmanifext.txt": 123,
- "conan_package.tgz": 523 }
+ { 'conaninfo.txt': 24,
+ 'conanmanifest.txt': 123,
+ 'conan_package.tgz': 523 }
end
- subject { post api("/packages/conan/v1/conans/#{recipe_path}/packages/123456789/upload_urls"), params: params, headers: headers }
+ subject { post api("/packages/conan/v1/conans/#{recipe_path}/packages/123456789/upload_urls"), params: params.to_json, headers: headers }
it_behaves_like 'rejects invalid recipe'
+ it_behaves_like 'rejects invalid upload_url params'
it 'returns a set of upload urls for the files requested' do
expected_response = {
@@ -462,6 +513,23 @@ RSpec.describe API::ConanPackages do
expect(response.body).to eq(expected_response.to_json)
end
+
+ context 'with invalid files' do
+ let(:params) do
+ { 'conaninfo.txt': 24,
+ 'invalid_file.txt': 10 }
+ end
+
+ it 'returns upload urls only for the valid requested files' do
+ expected_response = {
+ 'conaninfo.txt': "#{Settings.gitlab.base_url}/api/v4/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt"
+ }
+
+ subject
+
+ expect(response.body).to eq(expected_response.to_json)
+ end
+ end
end
describe 'DELETE /api/v4/packages/conan/v1/conans/:package_name/package_version/:package_username/:package_channel' do
diff --git a/spec/support/shared_examples/snippet_blob_shared_examples.rb b/spec/support/shared_examples/snippet_blob_shared_examples.rb
index ba97688d017..3ed777ee4b8 100644
--- a/spec/support/shared_examples/snippet_blob_shared_examples.rb
+++ b/spec/support/shared_examples/snippet_blob_shared_examples.rb
@@ -22,3 +22,24 @@ RSpec.shared_examples 'snippet blob raw path' do
end
end
end
+
+RSpec.shared_examples 'snippet blob raw url' do
+ let(:blob) { snippet.blobs.first }
+ let(:ref) { blob.repository.root_ref }
+
+ context 'for PersonalSnippets' do
+ let(:snippet) { personal_snippet }
+
+ it 'returns the raw personal snippet blob url' do
+ expect(subject).to eq("http://test.host/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}")
+ end
+ end
+
+ context 'for ProjectSnippets' do
+ let(:snippet) { project_snippet }
+
+ it 'returns the raw project snippet blob url' do
+ expect(subject).to eq("http://test.host/#{snippet.project.full_path}/-/snippets/#{snippet.id}/raw/#{ref}/#{blob.path}")
+ end
+ end
+end
diff --git a/spec/views/notify/changed_milestone_email.html.haml_spec.rb b/spec/views/notify/changed_milestone_email.html.haml_spec.rb
index 50a06683409..03904ff0747 100644
--- a/spec/views/notify/changed_milestone_email.html.haml_spec.rb
+++ b/spec/views/notify/changed_milestone_email.html.haml_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'notify/changed_milestone_email.html.haml' do
context 'milestone with start and due dates' do
before do
- milestone.update(start_date: '2018-01-01', due_date: '2018-12-31')
+ milestone.update!(start_date: '2018-01-01', due_date: '2018-12-31')
end
it 'renders with date range' do
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
index edf9eadf924..7c171ee65b9 100644
--- a/spec/views/projects/imports/new.html.haml_spec.rb
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe "projects/imports/new.html.haml" do
let(:project) { create(:project_empty_repo, :import_failed, import_type: :gitlab_project, import_source: '/var/opt/gitlab/gitlab-rails/shared/tmp/project_exports/uploads/t.tar.gz', import_url: nil) }
before do
- project.import_state.update(last_error: '<a href="http://googl.com">Foo</a>')
+ project.import_state.update!(last_error: '<a href="http://googl.com">Foo</a>')
sign_in(user)
project.add_maintainer(user)
end
diff --git a/spec/views/projects/merge_requests/show.html.haml_spec.rb b/spec/views/projects/merge_requests/show.html.haml_spec.rb
index 32819fc2cb0..1acc07dabb6 100644
--- a/spec/views/projects/merge_requests/show.html.haml_spec.rb
+++ b/spec/views/projects/merge_requests/show.html.haml_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'projects/merge_requests/show.html.haml' do
describe 'merge request assignee sidebar' do
context 'when assignee is allowed to merge' do
it 'does not show a warning icon' do
- closed_merge_request.update(assignee_id: user.id)
+ closed_merge_request.update!(assignee_id: user.id)
project.add_maintainer(user)
assign(:issuable_sidebar, serialize_issuable_sidebar(user, project, closed_merge_request))
@@ -42,20 +42,4 @@ RSpec.describe 'projects/merge_requests/show.html.haml' do
expect(rendered).to have_css('a', visible: false, text: 'Close')
end
end
-
- context 'when the merge request is open' do
- it 'closes the merge request if the source project does not exist' do
- closed_merge_request.update(state: 'open')
- forked_project.destroy
- # Reload merge request so MergeRequest#source_project turns to `nil`
- closed_merge_request.reload
- preload_view_requirements
-
- render
-
- expect(closed_merge_request.reload.state).to eq('closed')
- expect(rendered).to have_css('a', visible: false, text: 'Reopen')
- expect(rendered).to have_css('a', visible: false, text: 'Close')
- end
- end
end
diff --git a/spec/views/shared/_label_row.html.haml_spec.rb b/spec/views/shared/_label_row.html.haml_spec.rb
index 1e2ed41bafc..8f8aa3072e2 100644
--- a/spec/views/shared/_label_row.html.haml_spec.rb
+++ b/spec/views/shared/_label_row.html.haml_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'shared/_label_row.html.haml' do
label_types.each do |label_type, label_factory|
let!(:label) do
- label_record = create(label_factory)
+ label_record = create(label_factory) # rubocop: disable Rails/SaveBang
label_record.present(issuable_subject: label_record.subject)
end