summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-24 18:11:28 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-24 18:11:28 +0000
commit22dc7bdafcf442b96ace849341fb87bca7160614 (patch)
tree6721da756b46eb4f5c6c85a08f57e794b2da6f79
parent958d8a85d32fece017eac7d99bf28860b01a49d8 (diff)
downloadgitlab-ce-22dc7bdafcf442b96ace849341fb87bca7160614.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml1
-rw-r--r--.rubocop_manual_todo.yml2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue4
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql2
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql13
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql18
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql18
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql3
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js2
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js26
-rw-r--r--app/assets/javascripts/pages/projects/imports/show/index.js4
-rw-r--r--app/assets/javascripts/tracking.js10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue12
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/experiments/application_experiment.rb4
-rw-r--r--app/helpers/in_product_marketing_helper.rb2
-rw-r--r--app/helpers/user_callouts_helper.rb2
-rw-r--r--app/models/user_callout.rb2
-rw-r--r--app/presenters/project_presenter.rb25
-rw-r--r--app/services/ci/register_job_service.rb55
-rw-r--r--app/views/dashboard/activity.html.haml2
-rw-r--r--app/views/dashboard/groups/index.html.haml2
-rw-r--r--app/views/dashboard/issues.html.haml2
-rw-r--r--app/views/dashboard/merge_requests.html.haml2
-rw-r--r--app/views/dashboard/projects/index.html.haml2
-rw-r--r--app/views/dashboard/projects/shared/_common.html.haml2
-rw-r--r--app/views/dashboard/todos/index.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/index.html.haml2
-rw-r--r--app/views/explore/projects/page_out_of_bounds.html.haml2
-rw-r--r--app/views/explore/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/trending.html.haml2
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml2
-rw-r--r--app/views/projects/artifacts/browse.html.haml2
-rw-r--r--changelogs/unreleased/247636-fix-logging-search-scope.yml5
-rw-r--r--changelogs/unreleased/300302-db-constraint-terraform-name.yml5
-rw-r--r--changelogs/unreleased/35579-remove-graphql_logging-feature-flag.yml5
-rw-r--r--changelogs/unreleased/add-space-download-icon.yml5
-rw-r--r--changelogs/unreleased/ellipsis-expand-state-commits.yml5
-rw-r--r--changelogs/unreleased/uusijani-gitlab-uusijani-master-patch-65195.yml5
-rw-r--r--config/feature_flags/development/gitlab_ci_builds_queuing_metrics.yml8
-rw-r--r--config/feature_flags/development/graphql_logging.yml8
-rw-r--r--config/feature_flags/experiment/repo_integrations_link.yml8
-rw-r--r--db/migrate/20210224132547_add_null_constraint_to_terraform_state_name.rb11
-rw-r--r--db/schema_migrations/202102241325471
-rw-r--r--db/structure.sql2
-rw-r--r--doc/operations/incident_management/integrations.md2
-rw-r--r--doc/user/project/repository/repository_mirroring.md6
-rw-r--r--doc/user/project/settings/project_access_tokens.md30
-rw-r--r--lib/gitlab/ci/queue/metrics.rb103
-rw-r--r--lib/gitlab/ci/variables/collection/item.rb14
-rw-r--r--lib/gitlab/ci/variables/collection/sort.rb30
-rw-r--r--lib/gitlab/graphql/query_analyzers/logger_analyzer.rb4
-rw-r--r--locale/gitlab.pot11
-rw-r--r--spec/controllers/search_controller_spec.rb32
-rw-r--r--spec/experiments/application_experiment_spec.rb4
-rw-r--r--spec/factories/ci/pipelines.rb8
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb4
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js4
-rw-r--r--spec/frontend/tracking_spec.js17
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb45
-rw-r--r--spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb56
-rw-r--r--spec/models/ci/build_spec.rb4
-rw-r--r--spec/models/ci/pipeline_spec.rb656
-rw-r--r--spec/presenters/project_presenter_spec.rb32
-rw-r--r--spec/views/admin/application_settings/_package_registry.html.haml_spec.rb8
-rw-r--r--vendor/project_templates/learn_gitlab_ultimate_trial.tar.gz (renamed from vendor/project_templates/learn_gitlab_gold_trial.tar.gz)bin115096 -> 115096 bytes
69 files changed, 839 insertions, 538 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index ac4763103b2..d928c645a6b 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -298,6 +298,7 @@
rules:
- <<: *if-not-canonical-namespace
when: never
+ - <<: *if-master-refs
- changes: *ci-build-images-patterns
- changes: *code-qa-patterns
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml
index 02962c0f798..06cf8b95db3 100644
--- a/.rubocop_manual_todo.yml
+++ b/.rubocop_manual_todo.yml
@@ -781,7 +781,7 @@ RSpec/AnyInstanceOf:
- 'ee/spec/services/slash_commands/global_slack_handler_spec.rb'
- 'ee/spec/support/helpers/ee/stub_configuration.rb'
- 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
- - 'ee/spec/support/shared_examples/features/gold_trial_callout_shared_examples.rb'
+ - 'ee/spec/support/shared_examples/features/ultimate_trial_callout_shared_examples.rb'
- 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_logs_event_source_info_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/member_shared_examples.rb'
- 'ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb'
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 02d22d5c70e..43cb55c6d01 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-ed7f8b993280a89b23d10eaaa286a0b4f3f604de
+b7d1a76c7837d4df1896d52b8d10097216750ac7
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
index f56dc05a5d2..bdf52da209e 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -1,16 +1,16 @@
<script>
+import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
+import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
import { fetchPolicies } from '~/lib/graphql';
import { s__ } from '~/locale';
import { typeSet } from '../constants';
-import createHttpIntegrationMutation from '../graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '../graphql/mutations/create_prometheus_integration.mutation.graphql';
import destroyHttpIntegrationMutation from '../graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '../graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '../graphql/mutations/reset_prometheus_token.mutation.graphql';
import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_current_http_integration.mutation.graphql';
import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_prometheus_integration.mutation.graphql';
-import updateHttpIntegrationMutation from '../graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
import getHttpIntegrationsQuery from '../graphql/queries/get_http_integrations.query.graphql';
diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
index 36446bbfe47..742228e2928 100644
--- a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
@@ -1,5 +1,5 @@
#import "./integration_item.fragment.graphql"
-#import "./http_integration_payload_data.fragment.graphql"
+#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql"
fragment HttpIntegrationItem on AlertManagementHttpIntegration {
...IntegrationItem
diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
index b7ea50ebc44..df6ad0b712d 100644
--- a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
@@ -1,14 +1,3 @@
fragment HttpIntegrationPayloadData on AlertManagementHttpIntegration {
- payloadExample
- payloadAttributeMappings {
- fieldName
- path
- type
- label
- }
- payloadAlertFields {
- path
- type
- label
- }
+ id
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
index 0c7d7627b6a..babcdea935d 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
@@ -1,21 +1,7 @@
#import "../fragments/http_integration_item.fragment.graphql"
-mutation createHttpIntegration(
- $projectPath: ID!
- $name: String!
- $active: Boolean!
- $payloadExample: JsonString
- $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!]
-) {
- httpIntegrationCreate(
- input: {
- projectPath: $projectPath
- name: $name
- active: $active
- payloadExample: $payloadExample
- payloadAttributeMappings: $payloadAttributeMappings
- }
- ) {
+mutation createHttpIntegration($projectPath: ID!, $name: String!, $active: Boolean!) {
+ httpIntegrationCreate(input: { projectPath: $projectPath, name: $name, active: $active }) {
errors
integration {
...HttpIntegrationItem
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
index 631937048c2..37df9ec25eb 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
@@ -1,21 +1,7 @@
#import "../fragments/http_integration_item.fragment.graphql"
-mutation updateHttpIntegration(
- $id: ID!
- $name: String!
- $active: Boolean!
- $payloadExample: JsonString
- $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!]
-) {
- httpIntegrationUpdate(
- input: {
- id: $id
- name: $name
- active: $active
- payloadExample: $payloadExample
- payloadAttributeMappings: $payloadAttributeMappings
- }
- ) {
+mutation updateHttpIntegration($id: ID!, $name: String!, $active: Boolean!) {
+ httpIntegrationUpdate(input: { id: $id, name: $name, active: $active }) {
errors
integration {
...HttpIntegrationItem
diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
index e50dd89347f..833a2d6c12f 100644
--- a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
@@ -1,11 +1,10 @@
-#import "../fragments/http_integration_payload_data.fragment.graphql"
+#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql"
# TODO: this query need to accept http integration id to request a sepcific integration
query getHttpIntegrations($projectPath: ID!) {
project(fullPath: $projectPath) {
alertManagementHttpIntegrations {
nodes {
- id
...HttpIntegrationPayloadData
}
}
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index 4b63143c4ba..30424fee46a 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -30,7 +30,7 @@ $(() => {
}
$('body').on('click', '.js-toggle-button', function toggleButton(e) {
- e.currentTarget.classList.toggle(e.currentTarget.dataset.toggleOpenClass || 'open');
+ e.currentTarget.classList.toggle(e.currentTarget.dataset.toggleOpenClass || 'selected');
toggleContainer($(this).closest('.js-toggle-container'));
const targetTag = e.currentTarget.tagName.toLowerCase();
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
index b31a926dbe9..0c3fdcf3e75 100644
--- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -7,21 +7,19 @@ import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
import initSearchSettings from '~/search_settings';
import initSettingsPanels from '~/settings_panels';
-document.addEventListener('DOMContentLoaded', () => {
- // Initialize expandable settings panels
- initSettingsPanels();
+// Initialize expandable settings panels
+initSettingsPanels();
- initFilteredSearch({
- page: FILTERED_SEARCH.ADMIN_RUNNERS,
- filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys,
- anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR,
- useDefaultState: false,
- });
+initFilteredSearch({
+ page: FILTERED_SEARCH.ADMIN_RUNNERS,
+ filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys,
+ anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR,
+ useDefaultState: false,
+});
- initSharedRunnersForm();
- initVariableList();
+initSharedRunnersForm();
+initVariableList();
- initInstallRunner();
+initInstallRunner();
- initSearchSettings();
-});
+initSearchSettings();
diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js
index d5f92baf054..8397826f8eb 100644
--- a/app/assets/javascripts/pages/projects/imports/show/index.js
+++ b/app/assets/javascripts/pages/projects/imports/show/index.js
@@ -1,5 +1,3 @@
import ProjectImport from '~/project_import';
-document.addEventListener('DOMContentLoaded', () => {
- new ProjectImport(); // eslint-disable-line no-new
-});
+new ProjectImport(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/tracking.js b/app/assets/javascripts/tracking.js
index 7b5562d4d2d..008eda5e505 100644
--- a/app/assets/javascripts/tracking.js
+++ b/app/assets/javascripts/tracking.js
@@ -1,4 +1,4 @@
-import { omitBy, isUndefined } from 'lodash';
+import { omitBy, isUndefined, get } from 'lodash';
const standardContext = { ...window.gl?.snowplowStandardContext };
@@ -30,11 +30,17 @@ const createEventPayload = (el, { suffix = '' } = {}) => {
let value = el.dataset.trackValue || el.value || undefined;
if (el.type === 'checkbox' && !el.checked) value = false;
+ let context = el.dataset.trackContext;
+ if (el.dataset.trackExperiment) {
+ const data = get(window, ['gon', 'global', 'experiment', el.dataset.trackExperiment]);
+ if (data) context = { schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', data };
+ }
+
const data = {
label: el.dataset.trackLabel,
property: el.dataset.trackProperty,
value,
- context: el.dataset.trackContext,
+ context,
};
return {
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index a0f14f558d2..9e0fc10f5d3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -177,7 +177,7 @@ export default class MergeRequestStore {
this.ciStatus = `${this.ciStatus}-with-warnings`;
}
- this.commitsCount = mergeRequest.commitCount || 10;
+ this.commitsCount = mergeRequest.commitCount;
this.branchMissing = !mergeRequest.sourceBranchExists || !mergeRequest.targetBranchExists;
this.hasConflicts = mergeRequest.conflicts;
this.hasMergeableDiscussionsState = mergeRequest.mergeableDiscussionsState === false;
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index bf1361f1a6a..a7699d19872 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -46,6 +46,11 @@ export default {
required: false,
default: false,
},
+ tooltipBoundary: {
+ type: String,
+ required: false,
+ default: null,
+ },
cssClass: {
type: String,
required: false,
@@ -75,8 +80,11 @@ export default {
<template>
<gl-button
- v-gl-tooltip="{ placement: tooltipPlacement, container: tooltipContainer }"
- v-gl-tooltip.hover.blur
+ v-gl-tooltip.hover.blur="{
+ placement: tooltipPlacement,
+ container: tooltipContainer,
+ boundary: tooltipBoundary,
+ }"
:class="cssClass"
:title="title"
:data-clipboard-text="clipboardText"
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 820b00a902e..2e8cfb0d432 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -125,7 +125,7 @@ class SearchController < ApplicationController
payload[:metadata] ||= {}
payload[:metadata]['meta.search.group_id'] = params[:group_id]
payload[:metadata]['meta.search.project_id'] = params[:project_id]
- payload[:metadata]['meta.search.scope'] = params[:scope]
+ payload[:metadata]['meta.search.scope'] = params[:scope] || @scope
payload[:metadata]['meta.search.filters.confidential'] = params[:confidential]
payload[:metadata]['meta.search.filters.state'] = params[:state]
payload[:metadata]['meta.search.force_search_results'] = params[:force_search_results]
diff --git a/app/experiments/application_experiment.rb b/app/experiments/application_experiment.rb
index 63d64745a96..ec73382ed3b 100644
--- a/app/experiments/application_experiment.rb
+++ b/app/experiments/application_experiment.rb
@@ -25,6 +25,10 @@ class ApplicationExperiment < Gitlab::Experiment # rubocop:disable Gitlab/Namesp
))
end
+ def exclude!
+ @excluded = true
+ end
+
def rollout_strategy
# no-op override in inherited class as desired
end
diff --git a/app/helpers/in_product_marketing_helper.rb b/app/helpers/in_product_marketing_helper.rb
index a0e533d3fb8..ccf3fe51870 100644
--- a/app/helpers/in_product_marketing_helper.rb
+++ b/app/helpers/in_product_marketing_helper.rb
@@ -47,7 +47,7 @@ module InProductMarketingHelper
s_('InProductMarketing|Are your runners ready?')
],
trial: [
- s_('InProductMarketing|Start a free trial of GitLab Gold – no CC required'),
+ s_('InProductMarketing|Start a free trial of GitLab Ultimate – no CC required'),
s_('InProductMarketing|Improve app security with a 30-day trial'),
s_('InProductMarketing|Start with a GitLab Gold free trial')
],
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index f55a6c3c9e5..6a242d000ae 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -31,7 +31,7 @@ module UserCalloutsHelper
render 'shared/flash_user_callout', flash_type: flash_type, message: message, feature_name: feature_name
end
- def render_dashboard_gold_trial(user)
+ def render_dashboard_ultimate_trial(user)
end
def render_account_recovery_regular_check
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index d93fe611538..bb5a9dceaeb 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -7,7 +7,7 @@ class UserCallout < ApplicationRecord
gke_cluster_integration: 1,
gcp_signup_offer: 2,
cluster_security_warning: 3,
- gold_trial: 4, # EE-only
+ ultimate_trial: 4, # EE-only
geo_enable_hashed_storage: 5, # EE-only
geo_migrate_hashed_storage: 6, # EE-only
canary_deployment: 7, # EE-only
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index a2d25e425da..59a9fb64a36 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -41,7 +41,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
contribution_guide_anchor_data,
autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
kubernetes_cluster_anchor_data,
- gitlab_ci_anchor_data
+ gitlab_ci_anchor_data,
+ integrations_anchor_data
].compact.reject(&:is_link).sort_by.with_index { |item, idx| [item.class_modifier ? 0 : 1, idx] }
end
@@ -57,7 +58,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
license_anchor_data,
changelog_anchor_data,
contribution_guide_anchor_data,
- gitlab_ci_anchor_data
+ gitlab_ci_anchor_data,
+ integrations_anchor_data
].compact.reject { |item| item.is_link }
end
@@ -422,6 +424,25 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
private
+ def integrations_anchor_data
+ experiment(:repo_integrations_link, project: project) do |e|
+ e.exclude! unless can?(current_user, :admin_project, project)
+
+ e.use {} # nil control
+ e.try do
+ label = statistic_icon('settings') + _('Configure Integrations')
+ AnchorData.new(false, label, project_settings_integrations_path(project), nil, nil, nil, {
+ 'track-event': 'click',
+ 'track-experiment': e.name
+ })
+ end
+
+ e.run # call run so the return value will be the AnchorData (or nil)
+
+ e.track(:view, value: project.id) # track an event for the view, with project id
+ end
+ end
+
def cicd_missing?
current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index cd3db4a0205..2baf8eb5997 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -4,7 +4,7 @@ module Ci
# This class responsible for assigning
# proper pending build to runner on runner API request
class RegisterJobService
- attr_reader :runner
+ attr_reader :runner, :metrics
Result = Struct.new(:build, :build_json, :valid?)
@@ -13,8 +13,18 @@ module Ci
@metrics = ::Gitlab::Ci::Queue::Metrics.new(runner)
end
- # rubocop: disable CodeReuse/ActiveRecord
def execute(params = {})
+ @metrics.increment_queue_operation(:queue_attempt)
+
+ @metrics.observe_queue_time do
+ process_queue(params)
+ end
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def process_queue(params)
builds =
if runner.instance_type?
builds_for_shared_runner
@@ -24,8 +34,6 @@ module Ci
builds_for_project_runner
end
- valid = true
-
# pick builds that does not have other tags than runner's one
builds = builds.matches_tag_ids(runner.tags.ids)
@@ -39,14 +47,23 @@ module Ci
builds = builds.queued_before(params[:job_age].seconds.ago)
end
+ @metrics.observe_queue_size(-> { builds.to_a.size })
+
+ valid = true
+ depth = 0
+
builds.each do |build|
+ depth += 1
+ @metrics.increment_queue_operation(:queue_iteration)
+
result = process_build(build, params)
next unless result
if result.valid?
@metrics.register_success(result.build)
+ @metrics.observe_queue_depth(:found, depth)
- return result
+ return result # rubocop:disable Cop/AvoidReturnFromBlocks
else
# The usage of valid: is described in
# handling of ActiveRecord::StaleObjectError
@@ -54,22 +71,30 @@ module Ci
end
end
+ @metrics.increment_queue_operation(:queue_conflict) unless valid
+ @metrics.observe_queue_depth(:conflict, depth) unless valid
+ @metrics.observe_queue_depth(:not_found, depth) if valid
@metrics.register_failure
+
Result.new(nil, nil, valid)
end
# rubocop: enable CodeReuse/ActiveRecord
- private
-
def process_build(build, params)
- return unless runner.can_pick?(build)
+ if runner.can_pick?(build)
+ @metrics.increment_queue_operation(:build_can_pick)
+ else
+ @metrics.increment_queue_operation(:build_not_pick)
+
+ return
+ end
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
if assign_runner!(build, params)
present_build!(build)
end
- rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
+ rescue ActiveRecord::StaleObjectError
# We are looping to find another build that is not conflicting
# It also indicates that this build can be picked and passed to runner.
# If we don't do it, basically a bunch of runners would be competing for a build
@@ -79,8 +104,16 @@ module Ci
# In case we hit the concurrency-access lock,
# we still have to return 409 in the end,
# to make sure that this is properly handled by runner.
+ @metrics.increment_queue_operation(:build_conflict_lock)
+
+ Result.new(nil, nil, false)
+ rescue StateMachines::InvalidTransition
+ @metrics.increment_queue_operation(:build_conflict_transition)
+
Result.new(nil, nil, false)
rescue => ex
+ @metrics.increment_queue_operation(:build_conflict_exception)
+
# If an error (e.g. GRPC::DeadlineExceeded) occurred constructing
# the result, consider this as a failure to be retried.
scheduler_failure!(build)
@@ -106,8 +139,12 @@ module Ci
failure_reason, _ = pre_assign_runner_checks.find { |_, check| check.call(build, params) }
if failure_reason
+ @metrics.increment_queue_operation(:runner_pre_assign_checks_failed)
+
build.drop!(failure_reason)
else
+ @metrics.increment_queue_operation(:runner_pre_assign_checks_success)
+
build.run!
end
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index 1e93613e978..0ddee68e93f 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -3,7 +3,7 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- page_title _("Activity")
- header_title _("Activity"), activity_dashboard_path
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index afe4f1b84c2..fdfc2c5adb8 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -2,7 +2,7 @@
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
= render 'dashboard/groups_head'
- if params[:filter].blank? && @groups.empty?
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index b3ee5034204..5a7eb46771b 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -4,7 +4,7 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
.page-title-holder.d-flex.align-items-center
%h1.page-title= _('Issues')
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 2111b66d26e..d47df24b1b9 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -2,7 +2,7 @@
- page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
.page-title-holder.d-flex.align-items-start.flex-column.flex-sm-row.align-items-sm-center
%h1.page-title= _('Merge Requests')
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 6f4d53c79a7..1f4bd06aea4 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -12,7 +12,7 @@
callouts_feature_id: UserCalloutsHelper::CUSTOMIZE_HOMEPAGE,
track_label: 'home_page' } }
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
diff --git a/app/views/dashboard/projects/shared/_common.html.haml b/app/views/dashboard/projects/shared/_common.html.haml
index aa55f5a4e9c..17dcb072152 100644
--- a/app/views/dashboard/projects/shared/_common.html.haml
+++ b/app/views/dashboard/projects/shared/_common.html.haml
@@ -2,7 +2,7 @@
- breadcrumb_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
= render "projects/last_push"
= render 'dashboard/projects_head', project_tab_filter: :starred
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 9301f24d6a4..d78059b6aed 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -2,7 +2,7 @@
- page_title _("To-Do List")
- header_title _("To-Do List"), dashboard_todos_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- add_page_specific_style 'page_bundles/todos'
.page-title-holder.d-flex.align-items-center
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index f36f30d3638..60132818193 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -2,7 +2,7 @@
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- if current_user
= render 'dashboard/groups_head'
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 44456b6c015..ae59d9c728b 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -3,7 +3,7 @@
- header_title _("Projects"), dashboard_projects_path
- page_canonical_link explore_projects_url
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- if current_user
= render 'dashboard/projects_head', project_tab_filter: :explore
diff --git a/app/views/explore/projects/page_out_of_bounds.html.haml b/app/views/explore/projects/page_out_of_bounds.html.haml
index 0ee77ffd7d7..c554cce3dc6 100644
--- a/app/views/explore/projects/page_out_of_bounds.html.haml
+++ b/app/views/explore/projects/page_out_of_bounds.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- if current_user
= render 'dashboard/projects_head', project_tab_filter: :explore
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index ec92852ddde..a1f2fea5134 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- if current_user
= render 'dashboard/projects_head', project_tab_filter: :starred
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index ed508fa2506..e23f63b0064 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_dashboard_gold_trial(current_user)
+= render_dashboard_ultimate_trial(current_user)
- if current_user
= render 'dashboard/projects_head', project_tab_filter: :explore_trending
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 0284c779cfa..3853f428447 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -9,7 +9,7 @@
%h4.gl-mt-0
= _('Register Two-Factor Authenticator')
%p
- = _('Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).')
+ = _('Use a one-time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).')
.col-lg-8
- if current_user.two_factor_otp_enabled?
%p
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index b363f0d4325..3ebac785d55 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -18,7 +18,7 @@
.tree-controls<
= link_to download_project_job_artifacts_path(@project, @build),
rel: 'nofollow', download: '', class: 'gl-button btn btn-default download' do
- = sprite_icon('download')
+ = sprite_icon('download', css_class: 'gl-mr-2')
Download artifacts archive
.tree-content-holder
diff --git a/changelogs/unreleased/247636-fix-logging-search-scope.yml b/changelogs/unreleased/247636-fix-logging-search-scope.yml
new file mode 100644
index 00000000000..c9e5a7a7a58
--- /dev/null
+++ b/changelogs/unreleased/247636-fix-logging-search-scope.yml
@@ -0,0 +1,5 @@
+---
+title: 'Search: Log search.scope for the default scope'
+merge_request: 54684
+author:
+type: fixed
diff --git a/changelogs/unreleased/300302-db-constraint-terraform-name.yml b/changelogs/unreleased/300302-db-constraint-terraform-name.yml
new file mode 100644
index 00000000000..bbbe52224fb
--- /dev/null
+++ b/changelogs/unreleased/300302-db-constraint-terraform-name.yml
@@ -0,0 +1,5 @@
+---
+title: Added non-null constraint to terraform state name
+merge_request: 54940
+author:
+type: changed
diff --git a/changelogs/unreleased/35579-remove-graphql_logging-feature-flag.yml b/changelogs/unreleased/35579-remove-graphql_logging-feature-flag.yml
new file mode 100644
index 00000000000..d6b8caec999
--- /dev/null
+++ b/changelogs/unreleased/35579-remove-graphql_logging-feature-flag.yml
@@ -0,0 +1,5 @@
+---
+title: Remove graphql_logging feature flag
+merge_request: 54984
+author:
+type: other
diff --git a/changelogs/unreleased/add-space-download-icon.yml b/changelogs/unreleased/add-space-download-icon.yml
new file mode 100644
index 00000000000..85012d9bbcb
--- /dev/null
+++ b/changelogs/unreleased/add-space-download-icon.yml
@@ -0,0 +1,5 @@
+---
+title: Add space next to download icon in download artifacts button
+merge_request: 54228
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/ellipsis-expand-state-commits.yml b/changelogs/unreleased/ellipsis-expand-state-commits.yml
new file mode 100644
index 00000000000..1e655514f72
--- /dev/null
+++ b/changelogs/unreleased/ellipsis-expand-state-commits.yml
@@ -0,0 +1,5 @@
+---
+title: Add selected state for ellipsis button in a commit
+merge_request: 54754
+author: Yogi (@yo)
+type: changed
diff --git a/changelogs/unreleased/uusijani-gitlab-uusijani-master-patch-65195.yml b/changelogs/unreleased/uusijani-gitlab-uusijani-master-patch-65195.yml
new file mode 100644
index 00000000000..7ed23b2f681
--- /dev/null
+++ b/changelogs/unreleased/uusijani-gitlab-uusijani-master-patch-65195.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed typo on Two-Factor Authentication page
+merge_request: 54565
+author: Jani Uusitalo @uusijani
+type: fixed
diff --git a/config/feature_flags/development/gitlab_ci_builds_queuing_metrics.yml b/config/feature_flags/development/gitlab_ci_builds_queuing_metrics.yml
new file mode 100644
index 00000000000..49dd857f57f
--- /dev/null
+++ b/config/feature_flags/development/gitlab_ci_builds_queuing_metrics.yml
@@ -0,0 +1,8 @@
+---
+name: gitlab_ci_builds_queuing_metrics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54909
+rollout_issue_url:
+milestone: '13.10'
+type: development
+group: group::continuous integration
+default_enabled: false
diff --git a/config/feature_flags/development/graphql_logging.yml b/config/feature_flags/development/graphql_logging.yml
deleted file mode 100644
index c3615215048..00000000000
--- a/config/feature_flags/development/graphql_logging.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: graphql_logging
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/27885
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/35579
-milestone: '12.0'
-type: development
-group: group::project management
-default_enabled: true
diff --git a/config/feature_flags/experiment/repo_integrations_link.yml b/config/feature_flags/experiment/repo_integrations_link.yml
new file mode 100644
index 00000000000..943429a84e7
--- /dev/null
+++ b/config/feature_flags/experiment/repo_integrations_link.yml
@@ -0,0 +1,8 @@
+---
+name: repo_integrations_link
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54652/
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285154
+milestone: '13.10'
+type: experiment
+group: group::adoption
+default_enabled: false
diff --git a/db/migrate/20210224132547_add_null_constraint_to_terraform_state_name.rb b/db/migrate/20210224132547_add_null_constraint_to_terraform_state_name.rb
new file mode 100644
index 00000000000..d9f23311cf5
--- /dev/null
+++ b/db/migrate/20210224132547_add_null_constraint_to_terraform_state_name.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddNullConstraintToTerraformStateName < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ change_column_null :terraform_states, :name, false
+ end
+end
diff --git a/db/schema_migrations/20210224132547 b/db/schema_migrations/20210224132547
new file mode 100644
index 00000000000..f38b394f082
--- /dev/null
+++ b/db/schema_migrations/20210224132547
@@ -0,0 +1 @@
+ac0f71b427be1fb583474e352ce9468a1270c6e67fa40f9071751a0485f21160 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 6c463a668d2..b829666c1d3 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -17599,7 +17599,7 @@ CREATE TABLE terraform_states (
locked_at timestamp with time zone,
locked_by_user_id bigint,
uuid character varying(32) NOT NULL,
- name character varying(255),
+ name character varying(255) NOT NULL,
versioning_enabled boolean DEFAULT true NOT NULL
);
diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md
index 4985772eadc..d5f3643695a 100644
--- a/doc/operations/incident_management/integrations.md
+++ b/doc/operations/incident_management/integrations.md
@@ -4,7 +4,7 @@ group: Health
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/#assignments
---
-# Alert integrations **(FREE)**
+# Integrations **(FREE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13203) in GitLab Ultimate 12.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42640) to GitLab Free in 12.8.
diff --git a/doc/user/project/repository/repository_mirroring.md b/doc/user/project/repository/repository_mirroring.md
index d9d9fd8bcd3..98d33b7461e 100644
--- a/doc/user/project/repository/repository_mirroring.md
+++ b/doc/user/project/repository/repository_mirroring.md
@@ -588,3 +588,9 @@ If you receive an "13:Received RST_STREAM with error code 2" while mirroring to
### 4:Deadline Exceeded
When upgrading to GitLab 11.11.8 or newer, a change in how usernames are represented means that you may need to update your mirroring username and password to ensure that `%40` characters are replaced with `@`.
+
+### Connection blocked because server only allows public key authentication
+
+As the error indicates, the connection is getting blocked between GitLab and the remote repository. Even if a [TCP Check](../../../administration/raketasks/maintenance.md#check-tcp-connectivity-to-a-remote-site) is successful, you will need to check any networking components in the route from GitLab to the remote Server to ensure there's no blockage.
+
+For example, we've seen this error when a Firewall was performing a `Deep SSH Inspection` on outgoing packets.
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index 590f549577e..cda39508835 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -78,33 +78,3 @@ the following table.
| `write_registry` | Allows write-access (push) to [container registry](../../packages/container_registry/index.md). |
| `read_repository` | Allows read-only access (pull) to the repository. |
| `write_repository` | Allows read-write access (pull, push) to the repository. |
-
-### Enable or disable project access tokens
-
-Project access tokens are deployed behind a feature flag that is **enabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
-can disable it for your instance, globally or by project.
-
-To disable it globally:
-
-```ruby
-Feature.disable(:resource_access_token)
-```
-
-To disable it for a specific project:
-
-```ruby
-Feature.disable(:resource_access_token, project)
-```
-
-To enable it globally:
-
-```ruby
-Feature.enable(:resource_access_token)
-```
-
-To enable it for a specific project:
-
-```ruby
-Feature.enable(:resource_access_token, project)
-```
diff --git a/lib/gitlab/ci/queue/metrics.rb b/lib/gitlab/ci/queue/metrics.rb
index 441f1b1dca1..aa02dd106d5 100644
--- a/lib/gitlab/ci/queue/metrics.rb
+++ b/lib/gitlab/ci/queue/metrics.rb
@@ -7,10 +7,34 @@ module Gitlab
extend Gitlab::Utils::StrongMemoize
QUEUE_DURATION_SECONDS_BUCKETS = [1, 3, 10, 30, 60, 300, 900, 1800, 3600].freeze
+ QUEUE_DEPTH_TOTAL_BUCKETS = [1, 2, 3, 5, 8, 16, 32, 50, 100, 250, 500, 1000, 2000, 5000].freeze
+ QUEUE_SIZE_TOTAL_BUCKETS = [1, 5, 10, 50, 100, 500, 1000, 2000, 5000].freeze
+ QUEUE_ITERATION_DURATION_SECONDS_BUCKETS = [0.1, 0.3, 0.5, 1, 5, 10, 30, 60, 180, 300].freeze
+
METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'
DEFAULT_METRICS_SHARD = 'default'
JOBS_RUNNING_FOR_PROJECT_MAX_BUCKET = 5.freeze
+ OPERATION_COUNTERS = [
+ :build_can_pick,
+ :build_not_pick,
+ :build_conflict_lock,
+ :build_conflict_exception,
+ :build_conflict_transition,
+ :queue_attempt,
+ :queue_conflict,
+ :queue_iteration,
+ :queue_replication_lag,
+ :runner_pre_assign_checks_failed,
+ :runner_pre_assign_checks_success
+ ].to_set.freeze
+
+ QUEUE_DEPTH_HISTOGRAMS = [
+ :found,
+ :not_found,
+ :conflict
+ ].to_set.freeze
+
attr_reader :runner
def initialize(runner)
@@ -47,6 +71,43 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
+ def increment_queue_operation(operation)
+ if !Rails.env.production? && !OPERATION_COUNTERS.include?(operation)
+ raise ArgumentError, "unknown queue operation: #{operation}"
+ end
+
+ self.class.queue_operations_total.increment(operation: operation)
+ end
+
+ def observe_queue_depth(queue, size)
+ return unless Feature.enabled?(:gitlab_ci_builds_queuing_metrics, default_enabled: false)
+
+ if !Rails.env.production? && !QUEUE_DEPTH_HISTOGRAMS.include?(queue)
+ raise ArgumentError, "unknown queue depth label: #{queue}"
+ end
+
+ self.class.queue_depth_total.observe({ queue: queue }, size.to_f)
+ end
+
+ def observe_queue_size(size_proc)
+ return unless Feature.enabled?(:gitlab_ci_builds_queuing_metrics, default_enabled: false)
+
+ self.class.queue_size_total.observe({}, size_proc.call.to_f)
+ end
+
+ def observe_queue_time
+ start_time = ::Gitlab::Metrics::System.monotonic_time
+
+ result = yield
+
+ return result unless Feature.enabled?(:gitlab_ci_builds_queuing_metrics, default_enabled: false)
+
+ seconds = ::Gitlab::Metrics::System.monotonic_time - start_time
+ self.class.queue_iteration_duration_seconds.observe({}, seconds.to_f)
+
+ result
+ end
+
def self.failed_attempt_counter
strong_memoize(:failed_attempt_counter) do
name = :job_register_attempts_failed_total
@@ -75,6 +136,48 @@ module Gitlab
Gitlab::Metrics.histogram(name, comment, labels, buckets)
end
end
+
+ def self.queue_operations_total
+ strong_memoize(:queue_operations_total) do
+ name = :gitlab_ci_queue_operations_total
+ comment = 'Counts all the operations that are happening inside a queue'
+
+ Gitlab::Metrics.counter(name, comment)
+ end
+ end
+
+ def self.queue_depth_total
+ strong_memoize(:queue_depth_total) do
+ name = :gitlab_ci_queue_depth_total
+ comment = 'Size of a CI/CD builds queue in relation to the operation result'
+ labels = {}
+ buckets = QUEUE_DEPTH_TOTAL_BUCKETS
+
+ Gitlab::Metrics.histogram(name, comment, labels, buckets)
+ end
+ end
+
+ def self.queue_size_total
+ strong_memoize(:queue_size_total) do
+ name = :gitlab_ci_queue_size_total
+ comment = 'Size of initialized CI/CD builds queue'
+ labels = {}
+ buckets = QUEUE_SIZE_TOTAL_BUCKETS
+
+ Gitlab::Metrics.histogram(name, comment, labels, buckets)
+ end
+ end
+
+ def self.queue_iteration_duration_seconds
+ strong_memoize(:queue_iteration_duration_seconds) do
+ name = :gitlab_ci_queue_iteration_duration_seconds
+ comment = 'Time it takes to find a build in CI/CD queue'
+ labels = {}
+ buckets = QUEUE_ITERATION_DURATION_SECONDS_BUCKETS
+
+ Gitlab::Metrics.histogram(name, comment, labels, buckets)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb
index d32afee2e99..1b81d6a02e5 100644
--- a/lib/gitlab/ci/variables/collection/item.rb
+++ b/lib/gitlab/ci/variables/collection/item.rb
@@ -5,13 +5,13 @@ module Gitlab
module Variables
class Collection
class Item
+ include Gitlab::Utils::StrongMemoize
+
def initialize(key:, value:, public: true, file: false, masked: false)
raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless
value.is_a?(String) || value.nil?
- @variable = {
- key: key, value: value, public: public, file: file, masked: masked
- }
+ @variable = { key: key, value: value, public: public, file: file, masked: masked }
end
def value
@@ -26,6 +26,14 @@ module Gitlab
to_runner_variable == self.class.fabricate(other).to_runner_variable
end
+ def depends_on
+ strong_memoize(:depends_on) do
+ next unless ExpandVariables.possible_var_reference?(value)
+
+ value.scan(ExpandVariables::VARIABLES_REGEXP).map(&:first)
+ end
+ end
+
##
# If `file: true` has been provided we expose it, otherwise we
# don't expose `file` attribute at all (stems from what the runner
diff --git a/lib/gitlab/ci/variables/collection/sort.rb b/lib/gitlab/ci/variables/collection/sort.rb
index 40f129f62d1..94273ab3d67 100644
--- a/lib/gitlab/ci/variables/collection/sort.rb
+++ b/lib/gitlab/ci/variables/collection/sort.rb
@@ -27,11 +27,11 @@ module Gitlab
strong_memoize(:errors) do
# Check for cyclic dependencies and build error message in that case
- errors = each_strongly_connected_component.filter_map do |component|
+ cyclic_vars = each_strongly_connected_component.filter_map do |component|
component.map { |v| v[:key] }.inspect if component.size > 1
end
- "circular variable reference detected: #{errors.join(', ')}" if errors.any?
+ "circular variable reference detected: #{cyclic_vars.join(', ')}" if cyclic_vars.any?
end
end
@@ -51,29 +51,11 @@ module Gitlab
@collection.each(&block)
end
- def tsort_each_child(variable, &block)
- each_variable_reference(variable.value, &block)
- end
-
- def input_vars
- strong_memoize(:input_vars) do
- @collection.index_by { |env| env[:key] }
- end
- end
-
- def walk_references(value)
- return unless ExpandVariables.possible_var_reference?(value)
+ def tsort_each_child(var_item, &block)
+ depends_on = var_item.depends_on
+ return unless depends_on
- value.scan(ExpandVariables::VARIABLES_REGEXP) do |var_ref|
- yield(input_vars, var_ref.first)
- end
- end
-
- def each_variable_reference(value)
- walk_references(value) do |vars_hash, ref_var_name|
- variable = vars_hash.dig(ref_var_name)
- yield variable if variable
- end
+ depends_on.filter_map { |var_ref_name| @collection[var_ref_name] }.each(&block)
end
end
end
diff --git a/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb b/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
index 0665ea8b6c9..8acd27869a9 100644
--- a/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
+++ b/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
@@ -9,10 +9,6 @@ module Gitlab
FIELD_USAGE_ANALYZER = GraphQL::Analysis::FieldUsage.new { |query, used_fields, used_deprecated_fields| [used_fields, used_deprecated_fields] }
ALL_ANALYZERS = [COMPLEXITY_ANALYZER, DEPTH_ANALYZER, FIELD_USAGE_ANALYZER].freeze
- def analyze?(query)
- Feature.enabled?(:graphql_logging, default_enabled: true)
- end
-
def initial_value(query)
variables = process_variables(query.provided_variables)
default_initial_values(query).merge({
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c9fb3b39247..f1838bc3dc6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7736,6 +7736,9 @@ msgstr ""
msgid "Configure Gitaly timeouts."
msgstr ""
+msgid "Configure Integrations"
+msgstr ""
+
msgid "Configure Let's Encrypt"
msgstr ""
@@ -10634,7 +10637,7 @@ msgstr ""
msgid "Discover|Security capabilities, integrated into your development lifecycle"
msgstr ""
-msgid "Discover|See the other features of the %{linkStart}gold plan%{linkEnd}"
+msgid "Discover|See the other features of the %{linkStart}ultimate plan%{linkEnd}"
msgstr ""
msgid "Discover|Start a free trial"
@@ -15643,7 +15646,7 @@ msgstr ""
msgid "InProductMarketing|Start a GitLab Gold trial today in less than one minute, no credit card required."
msgstr ""
-msgid "InProductMarketing|Start a free trial of GitLab Gold – no CC required"
+msgid "InProductMarketing|Start a free trial of GitLab Ultimate – no CC required"
msgstr ""
msgid "InProductMarketing|Start a trial"
@@ -21097,7 +21100,7 @@ msgstr ""
msgid "Only project members will be imported. Group members will be skipped."
msgstr ""
-msgid "Only projects created under a Gold license are available in Security Dashboards."
+msgid "Only projects created under a Ultimate license are available in Security Dashboards."
msgstr ""
msgid "Only verified users with an email address in any of these domains can be added to the group."
@@ -32265,7 +32268,7 @@ msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr ""
-msgid "Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA)."
+msgid "Use a one-time password authenticator on your mobile device or computer to enable two-factor authentication (2FA)."
msgstr ""
msgid "Use cURL"
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 95cea10f0d0..7a5e5efedf1 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -261,23 +261,29 @@ RSpec.describe SearchController do
describe '#append_info_to_payload' do
it 'appends search metadata for logging' do
- last_payload = nil
- original_append_info_to_payload = controller.method(:append_info_to_payload)
-
- expect(controller).to receive(:append_info_to_payload) do |payload|
- original_append_info_to_payload.call(payload)
- last_payload = payload
+ expect(controller).to receive(:append_info_to_payload).and_wrap_original do |method, payload|
+ method.call(payload)
+
+ expect(payload[:metadata]['meta.search.group_id']).to eq('123')
+ expect(payload[:metadata]['meta.search.project_id']).to eq('456')
+ expect(payload[:metadata]).not_to have_key('meta.search.search')
+ expect(payload[:metadata]['meta.search.scope']).to eq('issues')
+ expect(payload[:metadata]['meta.search.force_search_results']).to eq('true')
+ expect(payload[:metadata]['meta.search.filters.confidential']).to eq('true')
+ expect(payload[:metadata]['meta.search.filters.state']).to eq('true')
end
get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456', confidential: true, state: true, force_search_results: true }
+ end
+
+ it 'appends the default scope in meta.search.scope' do
+ expect(controller).to receive(:append_info_to_payload).and_wrap_original do |method, payload|
+ method.call(payload)
+
+ expect(payload[:metadata]['meta.search.scope']).to eq('projects')
+ end
- expect(last_payload[:metadata]['meta.search.group_id']).to eq('123')
- expect(last_payload[:metadata]['meta.search.project_id']).to eq('456')
- expect(last_payload[:metadata]).not_to have_key('meta.search.search')
- expect(last_payload[:metadata]['meta.search.scope']).to eq('issues')
- expect(last_payload[:metadata]['meta.search.force_search_results']).to eq('true')
- expect(last_payload[:metadata]['meta.search.filters.confidential']).to eq('true')
- expect(last_payload[:metadata]['meta.search.filters.state']).to eq('true')
+ get :show, params: { search: 'hello world', group_id: '123', project_id: '456' }
end
end
end
diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb
index 3803fa10ab3..2595512eec3 100644
--- a/spec/experiments/application_experiment_spec.rb
+++ b/spec/experiments/application_experiment_spec.rb
@@ -82,6 +82,10 @@ RSpec.describe ApplicationExperiment, :experiment do
end
end
+ it "can exclude from within the block" do
+ expect(described_class.new('namespaced/stub') { |e| e.exclude! }).to be_excluded
+ end
+
describe "tracking events", :snowplow do
it "doesn't track if we shouldn't track" do
allow(subject).to receive(:should_track?).and_return(false)
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index e0d7ad3c133..87b9a6c0e23 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -40,6 +40,10 @@ FactoryBot.define do
end
end
+ trait :created do
+ status { :created }
+ end
+
factory :ci_pipeline do
transient { ci_ref_presence { true } }
@@ -53,10 +57,6 @@ FactoryBot.define do
failure_reason { :config_error }
end
- trait :created do
- status { :created }
- end
-
trait :preparing do
status { :preparing }
end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index 9a2333cf787..1d9f256a819 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -89,7 +89,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
)
end
- it 'successfully fills and submits the form', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/322666' do
+ it 'successfully fills and submits the form' do
visit project_settings_operations_path(project)
wait_for_requests
@@ -152,7 +152,7 @@ RSpec.describe 'Projects > Settings > For a forked project', :js do
end
context 'grafana integration settings form' do
- it 'successfully fills and completes the form', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/322666' do
+ it 'successfully fills and completes the form' do
visit project_settings_operations_path(project)
wait_for_requests
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
index 409805fdbf7..8e63b24284b 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -2,6 +2,8 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import VueApollo from 'vue-apollo';
+import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
+import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
import { useMockIntersectionObserver } from 'helpers/mock_dom_observer';
import waitForPromises from 'helpers/wait_for_promises';
@@ -9,14 +11,12 @@ import IntegrationsList from '~/alerts_settings/components/alerts_integrations_l
import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_form.vue';
import AlertsSettingsWrapper from '~/alerts_settings/components/alerts_settings_wrapper.vue';
import { typeSet } from '~/alerts_settings/constants';
-import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql';
import destroyHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql';
import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql';
import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql';
-import updateHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import {
diff --git a/spec/frontend/tracking_spec.js b/spec/frontend/tracking_spec.js
index a9f33b9a069..726dc0edede 100644
--- a/spec/frontend/tracking_spec.js
+++ b/spec/frontend/tracking_spec.js
@@ -183,6 +183,7 @@ describe('Tracking', () => {
<input class="dropdown" data-track-event="toggle_dropdown"/>
<div data-track-event="nested_event"><span class="nested"></span></div>
<input data-track-eventbogus="click_bogusinput" data-track-label="_label_" value="_value_"/>
+ <input data-track-event="click_input3" data-track-experiment="example" value="_value_"/>
`);
});
@@ -242,6 +243,22 @@ describe('Tracking', () => {
expect(eventSpy).toHaveBeenCalledWith('_category_', 'nested_event', {});
});
+
+ it('brings in experiment data if linked to an experiment', () => {
+ const data = {
+ variant: 'candidate',
+ experiment: 'repo_integrations_link',
+ key: '2bff73f6bb8cc11156c50a8ba66b9b8b',
+ };
+
+ window.gon.global = { experiment: { example: data } };
+ document.querySelector('[data-track-event="click_input3"]').click();
+
+ expect(eventSpy).toHaveBeenCalledWith('_category_', 'click_input3', {
+ value: '_value_',
+ context: { schema: 'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', data },
+ });
+ });
});
describe('tracking page loaded events', () => {
diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
index 42fbb88cceb..d806c466309 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -70,6 +70,43 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
end
end
+ describe '#depends_on' do
+ let(:item) { Gitlab::Ci::Variables::Collection::Item.new(**variable) }
+
+ subject { item.depends_on }
+
+ context 'table tests' do
+ using RSpec::Parameterized::TableSyntax
+
+ where do
+ {
+ "no variable references": {
+ variable: { key: 'VAR', value: 'something' },
+ expected_depends_on: nil
+ },
+ "simple variable reference": {
+ variable: { key: 'VAR', value: 'something_$VAR2' },
+ expected_depends_on: %w(VAR2)
+ },
+ "complex expansion": {
+ variable: { key: 'VAR', value: 'something_${VAR2}_$VAR3' },
+ expected_depends_on: %w(VAR2 VAR3)
+ },
+ "complex expansions for Windows": {
+ variable: { key: 'variable3', value: 'key%variable%%variable2%' },
+ expected_depends_on: %w(variable variable2)
+ }
+ }
+ end
+
+ with_them do
+ it 'contains referenced variable names' do
+ is_expected.to eq(expected_depends_on)
+ end
+ end
+ end
+ end
+
describe '.fabricate' do
it 'supports using a hash' do
resource = described_class.fabricate(variable)
@@ -140,5 +177,13 @@ RSpec.describe Gitlab::Ci::Variables::Collection::Item do
.to eq(key: 'VAR', value: 'value', public: true, file: true, masked: false)
end
end
+
+ context 'when referencing a variable' do
+ it '#depends_on contains names of dependencies' do
+ runner_variable = described_class.new(key: 'CI_VAR', value: '${CI_VAR_2}-123-$CI_VAR_3')
+
+ expect(runner_variable.depends_on).to eq(%w(CI_VAR_2 CI_VAR_3))
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb b/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
index 138765afd8a..8450396284a 100644
--- a/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
+++ b/spec/lib/gitlab/graphql/query_analyzers/logger_analyzer_spec.rb
@@ -5,42 +5,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer do
subject { described_class.new }
- describe '#analyze?' do
- context 'feature flag disabled' do
- before do
- stub_feature_flags(graphql_logging: false)
- end
-
- it 'disables the analyzer' do
- expect(subject.analyze?(anything)).to be_falsey
- end
- end
-
- context 'feature flag enabled by default' do
- let(:monotonic_time_before) { 42 }
- let(:monotonic_time_after) { 500 }
- let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
-
- it 'enables the analyzer' do
- expect(subject.analyze?(anything)).to be_truthy
- end
-
- it 'returns a duration in seconds' do
- allow(GraphQL::Analysis).to receive(:analyze_query).and_return([4, 2, [[], []]])
- allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
- allow(Gitlab::GraphqlLogger).to receive(:info)
-
- expected_duration = monotonic_time_duration
- memo = subject.initial_value(spy('query'))
-
- subject.final_value(memo)
-
- expect(memo).to have_key(:duration_s)
- expect(memo[:duration_s]).to eq(expected_duration)
- end
- end
- end
-
describe '#initial_value' do
it 'filters out sensitive variables' do
doc = GraphQL.parse <<-GRAPHQL
@@ -58,4 +22,24 @@ RSpec.describe Gitlab::Graphql::QueryAnalyzers::LoggerAnalyzer do
expect(subject.initial_value(query)[:variables]).to eq('{:body=>"[FILTERED]"}')
end
end
+
+ describe '#final_value' do
+ let(:monotonic_time_before) { 42 }
+ let(:monotonic_time_after) { 500 }
+ let(:monotonic_time_duration) { monotonic_time_after - monotonic_time_before }
+
+ it 'returns a duration in seconds' do
+ allow(GraphQL::Analysis).to receive(:analyze_query).and_return([4, 2, [[], []]])
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+ allow(Gitlab::GraphqlLogger).to receive(:info)
+
+ expected_duration = monotonic_time_duration
+ memo = subject.initial_value(spy('query'))
+
+ subject.final_value(memo)
+
+ expect(memo).to have_key(:duration_s)
+ expect(memo[:duration_s]).to eq(expected_duration)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 4ad7ce70a44..dd816101f0c 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2584,14 +2584,14 @@ RSpec.describe Ci::Build do
end
shared_examples 'containing environment variables' do
- it { environment_variables.each { |v| is_expected.to include(v) } }
+ it { is_expected.to include(*environment_variables) }
end
context 'when no URL was set' do
it_behaves_like 'containing environment variables'
it 'does not have CI_ENVIRONMENT_URL' do
- keys = subject.map { |var| var[:key] }
+ keys = subject.pluck(:key)
expect(keys).not_to include('CI_ENVIRONMENT_URL')
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 7dc84577f40..bc9bd8edc6e 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -11,10 +11,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let_it_be(:namespace) { create_default(:namespace).freeze }
let_it_be(:project) { create_default(:project, :repository).freeze }
- let(:pipeline) do
- create(:ci_empty_pipeline, status: :created, project: project)
- end
-
it_behaves_like 'having unique enum values'
it { is_expected.to belong_to(:project) }
@@ -53,6 +49,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'associations' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
it 'has a bidirectional relationship with projects' do
expect(described_class.reflect_on_association(:project).has_inverse?).to eq(:all_pipelines)
expect(Project.reflect_on_association(:all_pipelines).has_inverse?).to eq(:project)
@@ -82,6 +80,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#set_status' do
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
where(:from_status, :to_status) do
from_status_names = described_class.state_machines[:status].states.map(&:name)
to_status_names = from_status_names - [:created] # we never want to transition into created
@@ -105,6 +105,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '.processables' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
before do
create(:ci_build, name: 'build', pipeline: pipeline)
create(:ci_bridge, name: 'bridge', pipeline: pipeline)
@@ -142,7 +144,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { described_class.for_sha(sha) }
let(:sha) { 'abc' }
- let!(:pipeline) { create(:ci_pipeline, sha: 'abc') }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, sha: 'abc') }
it 'returns the pipeline' do
is_expected.to contain_exactly(pipeline)
@@ -170,7 +173,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { described_class.for_source_sha(source_sha) }
let(:source_sha) { 'abc' }
- let!(:pipeline) { create(:ci_pipeline, source_sha: 'abc') }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, source_sha: 'abc') }
it 'returns the pipeline' do
is_expected.to contain_exactly(pipeline)
@@ -228,7 +232,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { described_class.for_branch(branch) }
let(:branch) { 'master' }
- let!(:pipeline) { create(:ci_pipeline, ref: 'master') }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, ref: 'master') }
it 'returns the pipeline' do
is_expected.to contain_exactly(pipeline)
@@ -247,13 +252,16 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '.ci_sources' do
subject { described_class.ci_sources }
- let!(:push_pipeline) { create(:ci_pipeline, source: :push) }
- let!(:web_pipeline) { create(:ci_pipeline, source: :web) }
- let!(:api_pipeline) { create(:ci_pipeline, source: :api) }
- let!(:webide_pipeline) { create(:ci_pipeline, source: :webide) }
- let!(:child_pipeline) { create(:ci_pipeline, source: :parent_pipeline) }
+ let(:push_pipeline) { build(:ci_pipeline, source: :push) }
+ let(:web_pipeline) { build(:ci_pipeline, source: :web) }
+ let(:api_pipeline) { build(:ci_pipeline, source: :api) }
+ let(:webide_pipeline) { build(:ci_pipeline, source: :webide) }
+ let(:child_pipeline) { build(:ci_pipeline, source: :parent_pipeline) }
+ let(:pipelines) { [push_pipeline, web_pipeline, api_pipeline, webide_pipeline, child_pipeline] }
it 'contains pipelines having CI only sources' do
+ pipelines.map(&:save!)
+
expect(subject).to contain_exactly(push_pipeline, web_pipeline, api_pipeline)
end
@@ -387,6 +395,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#merge_request_ref?' do
subject { pipeline.merge_request_ref? }
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
it 'calls MergeRequest#merge_request_ref?' do
expect(MergeRequest).to receive(:merge_request_ref?).with(pipeline.ref)
@@ -624,7 +634,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#source' do
context 'when creating new pipeline' do
let(:pipeline) do
- build(:ci_empty_pipeline, status: :created, project: project, source: nil)
+ build(:ci_empty_pipeline, :created, project: project, source: nil)
end
it "prevents from creating an object" do
@@ -633,17 +643,21 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when updating existing pipeline' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
before do
pipeline.update_attribute(:source, nil)
end
- it "object is valid" do
+ it 'object is valid' do
expect(pipeline).to be_valid
end
end
end
describe '#block' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
it 'changes pipeline status to manual' do
expect(pipeline.block).to be true
expect(pipeline.reload).to be_manual
@@ -654,7 +668,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#delay' do
subject { pipeline.delay }
- let(:pipeline) { build(:ci_pipeline, status: :created) }
+ let(:pipeline) { build(:ci_pipeline, :created) }
it 'changes pipeline status to schedule' do
subject
@@ -664,6 +678,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#valid_commit_sha' do
+ let(:pipeline) { build_stubbed(:ci_empty_pipeline, :created, project: project) }
+
context 'commit.sha can not start with 00000000' do
before do
pipeline.sha = '0' * 40
@@ -677,6 +693,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#short_sha' do
subject { pipeline.short_sha }
+ let(:pipeline) { build_stubbed(:ci_empty_pipeline, :created) }
+
it 'has 8 items' do
expect(subject.size).to eq(8)
end
@@ -686,49 +704,58 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#retried' do
subject { pipeline.retried }
+ let(:pipeline) { create(:ci_empty_pipeline, :created, project: project) }
+ let!(:build1) { create(:ci_build, pipeline: pipeline, name: 'deploy', retried: true) }
+
before do
- @build1 = create(:ci_build, pipeline: pipeline, name: 'deploy', retried: true)
- @build2 = create(:ci_build, pipeline: pipeline, name: 'deploy')
+ create(:ci_build, pipeline: pipeline, name: 'deploy')
end
it 'returns old builds' do
- is_expected.to contain_exactly(@build1)
+ is_expected.to contain_exactly(build1)
end
end
describe '#coverage' do
- let(:project) { create(:project, build_coverage_regex: "/.*/") }
- let(:pipeline) { create(:ci_empty_pipeline, project: project) }
+ let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline) }
- it "calculates average when there are two builds with coverage" do
- create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline)
- create(:ci_build, name: "rubocop", coverage: 40, pipeline: pipeline)
- expect(pipeline.coverage).to eq("35.00")
- end
+ context 'with multiple pipelines' do
+ before_all do
+ create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline)
+ create(:ci_build, name: "rubocop", coverage: 40, pipeline: pipeline)
+ end
- it "calculates average when there are two builds with coverage and one with nil" do
- create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline)
- create(:ci_build, name: "rubocop", coverage: 40, pipeline: pipeline)
- create(:ci_build, pipeline: pipeline)
- expect(pipeline.coverage).to eq("35.00")
- end
+ it "calculates average when there are two builds with coverage" do
+ expect(pipeline.coverage).to eq("35.00")
+ end
- it "calculates average when there are two builds with coverage and one is retried" do
- create(:ci_build, name: "rspec", coverage: 30, pipeline: pipeline)
- create(:ci_build, name: "rubocop", coverage: 30, pipeline: pipeline, retried: true)
- create(:ci_build, name: "rubocop", coverage: 40, pipeline: pipeline)
- expect(pipeline.coverage).to eq("35.00")
+ it "calculates average when there are two builds with coverage and one with nil" do
+ create(:ci_build, pipeline: pipeline)
+
+ expect(pipeline.coverage).to eq("35.00")
+ end
+
+ it "calculates average when there are two builds with coverage and one is retried" do
+ create(:ci_build, name: "rubocop", coverage: 30, pipeline: pipeline, retried: true)
+
+ expect(pipeline.coverage).to eq("35.00")
+ end
end
- it "calculates average when there is one build without coverage" do
- FactoryBot.create(:ci_build, pipeline: pipeline)
- expect(pipeline.coverage).to be_nil
+ context 'when there is one build without coverage' do
+ it "calculates average to nil" do
+ create(:ci_build, pipeline: pipeline)
+
+ expect(pipeline.coverage).to be_nil
+ end
end
end
describe '#retryable?' do
subject { pipeline.retryable? }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created, project: project) }
+
context 'no failed builds' do
before do
create_build('rspec', 'success')
@@ -790,6 +817,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#predefined_variables' do
subject { pipeline.predefined_variables }
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
it 'includes all predefined variables in a valid order' do
keys = subject.map { |variable| variable[:key] }
@@ -816,21 +845,18 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when merge request is present' do
+ let_it_be(:assignees) { create_list(:user, 2) }
+ let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:labels) { create_list(:label, 2) }
let(:merge_request) do
- create(:merge_request,
+ create(:merge_request, :simple,
source_project: project,
- source_branch: 'feature',
target_project: project,
- target_branch: 'master',
assignees: assignees,
milestone: milestone,
labels: labels)
end
- let(:assignees) { create_list(:user, 2) }
- let(:milestone) { create(:milestone, project: project) }
- let(:labels) { create_list(:label, 2) }
-
context 'when pipeline for merge request is created' do
let(:pipeline) do
create(:ci_pipeline, :detached_merge_request_pipeline,
@@ -1016,9 +1042,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#protected_ref?' do
- before do
- pipeline.project = create(:project, :repository)
- end
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
it 'delegates method to project' do
expect(pipeline).not_to be_protected_ref
@@ -1026,11 +1050,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#legacy_trigger' do
- let(:trigger_request) { create(:ci_trigger_request) }
-
- before do
- pipeline.trigger_requests << trigger_request
- end
+ let(:trigger_request) { build(:ci_trigger_request) }
+ let(:pipeline) { build(:ci_empty_pipeline, :created, trigger_requests: [trigger_request]) }
it 'returns first trigger request' do
expect(pipeline.legacy_trigger).to eq trigger_request
@@ -1040,6 +1061,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#auto_canceled?' do
subject { pipeline.auto_canceled? }
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
context 'when it is canceled' do
before do
pipeline.cancel
@@ -1047,7 +1070,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when there is auto_canceled_by' do
before do
- pipeline.update!(auto_canceled_by: create(:ci_empty_pipeline))
+ pipeline.auto_canceled_by = create(:ci_empty_pipeline)
end
it 'is auto canceled' do
@@ -1075,6 +1098,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'pipeline stages' do
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
describe 'legacy stages' do
before do
create(:commit_status, pipeline: pipeline,
@@ -1180,6 +1205,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#legacy_stage' do
subject { pipeline.legacy_stage('test') }
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
context 'with status in stage' do
before do
create(:commit_status, pipeline: pipeline, stage: 'test')
@@ -1202,6 +1229,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#stages' do
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
before do
create(:ci_stage_entity, project: project,
pipeline: pipeline,
@@ -1256,6 +1285,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'state machine' do
+ let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline, :created) }
let(:current) { Time.current.change(usec: 0) }
let(:build) { create_build('build1', queued_at: 0) }
let(:build_b) { create_build('build2', queued_at: 0) }
@@ -1459,24 +1489,25 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'auto merge' do
- let(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
-
- let(:pipeline) do
- create(:ci_pipeline, :running, project: merge_request.source_project,
- ref: merge_request.source_branch,
- sha: merge_request.diff_head_sha)
- end
+ context 'when auto merge is enabled' do
+ let_it_be_with_reload(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
+ let_it_be_with_reload(:pipeline) do
+ create(:ci_pipeline, :running, project: merge_request.source_project,
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha)
+ end
- before do
- merge_request.update_head_pipeline
- end
+ before_all do
+ merge_request.update_head_pipeline
+ end
- %w[succeed! drop! cancel! skip!].each do |action|
- context "when the pipeline recieved #{action} event" do
- it 'performs AutoMergeProcessWorker' do
- expect(AutoMergeProcessWorker).to receive(:perform_async).with(merge_request.id)
+ %w[succeed! drop! cancel! skip!].each do |action|
+ context "when the pipeline recieved #{action} event" do
+ it 'performs AutoMergeProcessWorker' do
+ expect(AutoMergeProcessWorker).to receive(:perform_async).with(merge_request.id)
- pipeline.public_send(action)
+ pipeline.public_send(action)
+ end
end
end
end
@@ -1628,15 +1659,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'multi-project pipelines' do
let!(:downstream_project) { create(:project, :repository) }
- let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:upstream_pipeline) { create(:ci_pipeline) }
let!(:downstream_pipeline) { create(:ci_pipeline, :with_job, project: downstream_project) }
it_behaves_like 'upstream downstream pipeline'
end
context 'parent-child pipelines' do
- let!(:upstream_pipeline) { create(:ci_pipeline, project: project) }
- let!(:downstream_pipeline) { create(:ci_pipeline, :with_job, project: project) }
+ let!(:upstream_pipeline) { create(:ci_pipeline) }
+ let!(:downstream_pipeline) { create(:ci_pipeline, :with_job) }
it_behaves_like 'upstream downstream pipeline'
end
@@ -1655,6 +1686,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#branch?' do
subject { pipeline.branch? }
+ let(:pipeline) { build(:ci_empty_pipeline, :created) }
+
context 'when ref is not a tag' do
before do
pipeline.tag = false
@@ -1665,16 +1698,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is merge request' do
- let(:pipeline) do
- create(:ci_pipeline, merge_request: merge_request)
- end
+ let(:pipeline) { build(:ci_pipeline, merge_request: merge_request) }
let(:merge_request) do
- create(:merge_request,
+ create(:merge_request, :simple,
source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
+ target_project: project)
end
it 'returns false' do
@@ -1738,6 +1767,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when repository exists' do
using RSpec::Parameterized::TableSyntax
+ let_it_be(:pipeline, refind: true) { create(:ci_empty_pipeline) }
+
where(:tag, :ref, :result) do
false | 'master' | true
false | 'non-existent-branch' | false
@@ -1746,8 +1777,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
with_them do
- let(:pipeline) do
- create(:ci_empty_pipeline, project: project, tag: tag, ref: ref)
+ before do
+ pipeline.update!(tag: tag, ref: ref)
end
it "correctly detects ref" do
@@ -1757,10 +1788,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when repository does not exist' do
- let(:project) { create(:project) }
- let(:pipeline) do
- create(:ci_empty_pipeline, project: project, ref: 'master')
- end
+ let(:pipeline) { build(:ci_empty_pipeline, ref: 'master', project: build(:project)) }
it 'always returns false' do
expect(pipeline.ref_exists?).to eq false
@@ -1771,7 +1799,6 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'with non-empty project' do
let(:pipeline) do
create(:ci_pipeline,
- project: project,
ref: project.default_branch,
sha: project.commit.sha)
end
@@ -1779,14 +1806,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#lazy_ref_commit' do
let(:another) do
create(:ci_pipeline,
- project: project,
ref: 'feature',
sha: project.commit('feature').sha)
end
let(:unicode) do
create(:ci_pipeline,
- project: project,
ref: 'ü/unicode/multi-byte')
end
@@ -1845,6 +1870,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#manual_actions' do
subject { pipeline.manual_actions }
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
it 'when none defined' do
is_expected.to be_empty
end
@@ -1871,9 +1898,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#branch_updated?' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
context 'when pipeline has before SHA' do
before do
- pipeline.update_column(:before_sha, 'a1b2c3d4')
+ pipeline.update!(before_sha: 'a1b2c3d4')
end
it 'runs on a branch update push' do
@@ -1884,7 +1913,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline does not have before SHA' do
before do
- pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ pipeline.update!(before_sha: Gitlab::Git::BLANK_SHA)
end
it 'does not run on a branch updating push' do
@@ -1894,6 +1923,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#modified_paths' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
context 'when old and new revisions are set' do
before do
pipeline.update!(before_sha: '1234abcd', sha: '2345bcde')
@@ -1910,7 +1941,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when either old or new revision is missing' do
before do
- pipeline.update_column(:before_sha, Gitlab::Git::BLANK_SHA)
+ pipeline.update!(before_sha: Gitlab::Git::BLANK_SHA)
end
it 'returns nil' do
@@ -1924,11 +1955,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
let(:merge_request) do
- create(:merge_request,
+ create(:merge_request, :simple,
source_project: project,
- source_branch: 'feature',
- target_project: project,
- target_branch: 'master')
+ target_project: project)
end
it 'returns merge request modified paths' do
@@ -1962,6 +1991,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#has_kubernetes_active?' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created, project: project) }
+
context 'when kubernetes is active' do
context 'when user configured kubernetes from CI/CD > Clusters' do
let!(:cluster) { create(:cluster, :project, :provided_by_gcp) }
@@ -1983,6 +2014,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#has_warnings?' do
subject { pipeline.has_warnings? }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
context 'build which is allowed to fail fails' do
before do
create :ci_build, :success, pipeline: pipeline, name: 'rspec'
@@ -2039,6 +2072,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#number_of_warnings' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
it 'returns the number of warnings' do
create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop')
create(:ci_bridge, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop')
@@ -2047,7 +2082,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'supports eager loading of the number of warnings' do
- pipeline2 = create(:ci_empty_pipeline, status: :created, project: project)
+ pipeline2 = create(:ci_empty_pipeline, :created)
create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop')
create(:ci_build, :allowed_to_fail, :failed, pipeline: pipeline2, name: 'rubocop')
@@ -2071,6 +2106,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { pipeline.needs_processing? }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
where(:processed, :result) do
nil | true
false | true
@@ -2090,122 +2127,107 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- shared_context 'with some outdated pipelines' do
- before do
- create_pipeline(:canceled, 'ref', 'A', project)
- create_pipeline(:success, 'ref', 'A', project)
- create_pipeline(:failed, 'ref', 'B', project)
- create_pipeline(:skipped, 'feature', 'C', project)
+ context 'with outdated pipelines' do
+ before_all do
+ create_pipeline(:canceled, 'ref', 'A')
+ create_pipeline(:success, 'ref', 'A')
+ create_pipeline(:failed, 'ref', 'B')
+ create_pipeline(:skipped, 'feature', 'C')
end
- def create_pipeline(status, ref, sha, project)
+ def create_pipeline(status, ref, sha)
create(
:ci_empty_pipeline,
status: status,
ref: ref,
- sha: sha,
- project: project
+ sha: sha
)
end
- end
-
- describe '.newest_first' do
- include_context 'with some outdated pipelines'
- it 'returns the pipelines from new to old' do
- expect(described_class.newest_first.pluck(:status))
- .to eq(%w[skipped failed success canceled])
- end
+ describe '.newest_first' do
+ it 'returns the pipelines from new to old' do
+ expect(described_class.newest_first.pluck(:status))
+ .to eq(%w[skipped failed success canceled])
+ end
- it 'searches limited backlog' do
- expect(described_class.newest_first(limit: 1).pluck(:status))
- .to eq(%w[skipped])
+ it 'searches limited backlog' do
+ expect(described_class.newest_first(limit: 1).pluck(:status))
+ .to eq(%w[skipped])
+ end
end
- end
-
- describe '.latest_status' do
- include_context 'with some outdated pipelines'
- context 'when no ref is specified' do
- it 'returns the status of the latest pipeline' do
- expect(described_class.latest_status).to eq('skipped')
+ describe '.latest_status' do
+ context 'when no ref is specified' do
+ it 'returns the status of the latest pipeline' do
+ expect(described_class.latest_status).to eq('skipped')
+ end
end
- end
- context 'when ref is specified' do
- it 'returns the status of the latest pipeline for the given ref' do
- expect(described_class.latest_status('ref')).to eq('failed')
+ context 'when ref is specified' do
+ it 'returns the status of the latest pipeline for the given ref' do
+ expect(described_class.latest_status('ref')).to eq('failed')
+ end
end
end
- end
-
- describe '.latest_successful_for_ref' do
- include_context 'with some outdated pipelines'
- let!(:latest_successful_pipeline) do
- create_pipeline(:success, 'ref', 'D', project)
- end
+ describe '.latest_successful_for_ref' do
+ let!(:latest_successful_pipeline) do
+ create_pipeline(:success, 'ref', 'D')
+ end
- it 'returns the latest successful pipeline' do
- expect(described_class.latest_successful_for_ref('ref'))
- .to eq(latest_successful_pipeline)
+ it 'returns the latest successful pipeline' do
+ expect(described_class.latest_successful_for_ref('ref'))
+ .to eq(latest_successful_pipeline)
+ end
end
- end
-
- describe '.latest_running_for_ref' do
- include_context 'with some outdated pipelines'
- let!(:latest_running_pipeline) do
- create_pipeline(:running, 'ref', 'D', project)
- end
+ describe '.latest_running_for_ref' do
+ let!(:latest_running_pipeline) do
+ create_pipeline(:running, 'ref', 'D')
+ end
- it 'returns the latest running pipeline' do
- expect(described_class.latest_running_for_ref('ref'))
- .to eq(latest_running_pipeline)
+ it 'returns the latest running pipeline' do
+ expect(described_class.latest_running_for_ref('ref'))
+ .to eq(latest_running_pipeline)
+ end
end
- end
- describe '.latest_failed_for_ref' do
- include_context 'with some outdated pipelines'
-
- let!(:latest_failed_pipeline) do
- create_pipeline(:failed, 'ref', 'D', project)
- end
+ describe '.latest_failed_for_ref' do
+ let!(:latest_failed_pipeline) do
+ create_pipeline(:failed, 'ref', 'D')
+ end
- it 'returns the latest failed pipeline' do
- expect(described_class.latest_failed_for_ref('ref'))
- .to eq(latest_failed_pipeline)
+ it 'returns the latest failed pipeline' do
+ expect(described_class.latest_failed_for_ref('ref'))
+ .to eq(latest_failed_pipeline)
+ end
end
- end
- describe '.latest_successful_for_sha' do
- include_context 'with some outdated pipelines'
-
- let!(:latest_successful_pipeline) do
- create_pipeline(:success, 'ref', 'awesomesha', project)
- end
+ describe '.latest_successful_for_sha' do
+ let!(:latest_successful_pipeline) do
+ create_pipeline(:success, 'ref', 'awesomesha')
+ end
- it 'returns the latest successful pipeline' do
- expect(described_class.latest_successful_for_sha('awesomesha'))
- .to eq(latest_successful_pipeline)
+ it 'returns the latest successful pipeline' do
+ expect(described_class.latest_successful_for_sha('awesomesha'))
+ .to eq(latest_successful_pipeline)
+ end
end
- end
-
- describe '.latest_successful_for_refs' do
- include_context 'with some outdated pipelines'
- let!(:latest_successful_pipeline1) do
- create_pipeline(:success, 'ref1', 'D', project)
- end
+ describe '.latest_successful_for_refs' do
+ let!(:latest_successful_pipeline1) do
+ create_pipeline(:success, 'ref1', 'D')
+ end
- let!(:latest_successful_pipeline2) do
- create_pipeline(:success, 'ref2', 'D', project)
- end
+ let!(:latest_successful_pipeline2) do
+ create_pipeline(:success, 'ref2', 'D')
+ end
- it 'returns the latest successful pipeline for both refs' do
- refs = %w(ref1 ref2 ref3)
+ it 'returns the latest successful pipeline for both refs' do
+ refs = %w(ref1 ref2 ref3)
- expect(described_class.latest_successful_for_refs(refs)).to eq({ 'ref1' => latest_successful_pipeline1, 'ref2' => latest_successful_pipeline2 })
+ expect(described_class.latest_successful_for_refs(refs)).to eq({ 'ref1' => latest_successful_pipeline1, 'ref2' => latest_successful_pipeline2 })
+ end
end
end
@@ -2215,8 +2237,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
:ci_empty_pipeline,
status: 'success',
ref: 'master',
- sha: '123',
- project: project
+ sha: '123'
)
end
@@ -2225,8 +2246,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
:ci_empty_pipeline,
status: 'success',
ref: 'develop',
- sha: '123',
- project: project
+ sha: '123'
)
end
@@ -2235,8 +2255,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
:ci_empty_pipeline,
status: 'success',
ref: 'test',
- sha: '456',
- project: project
+ sha: '456'
)
end
@@ -2333,12 +2352,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#status', :sidekiq_inline do
- let(:build) do
- create(:ci_build, :created, pipeline: pipeline, name: 'test')
- end
-
subject { pipeline.reload.status }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+ let(:build) { create(:ci_build, :created, pipeline: pipeline, name: 'test') }
+
context 'on waiting for resource' do
before do
allow(build).to receive(:with_resource_group?) { true }
@@ -2430,8 +2448,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#detailed_status' do
subject { pipeline.detailed_status(user) }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
context 'when pipeline is created' do
- let(:pipeline) { create(:ci_pipeline, status: :created) }
+ let(:pipeline) { create(:ci_pipeline, :created) }
it 'returns detailed status for created pipeline' do
expect(subject.text).to eq s_('CiStatusText|created')
@@ -2508,6 +2528,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#cancelable?' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
%i[created running pending].each do |status0|
context "when there is a build #{status0}" do
before do
@@ -2599,7 +2621,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#cancel_running' do
- let(:latest_status) { pipeline.statuses.pluck(:status) }
+ subject(:latest_status) { pipeline.statuses.pluck(:status) }
+
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
context 'when there is a running external job and a regular job' do
before do
@@ -2642,7 +2666,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#retry_failed' do
- let(:latest_status) { pipeline.latest_statuses.pluck(:status) }
+ subject(:latest_status) { pipeline.latest_statuses.pluck(:status) }
+
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
before do
stub_not_protect_default_branch
@@ -2691,11 +2717,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#execute_hooks' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
let!(:build_a) { create_build('a', 0) }
let!(:build_b) { create_build('b', 0) }
let!(:hook) do
- create(:project_hook, project: project, pipeline_events: enabled)
+ create(:project_hook, pipeline_events: enabled)
end
before do
@@ -2721,7 +2748,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'builds hook data once' do
- create(:pipelines_email_service, project: project)
+ create(:pipelines_email_service)
expect(Gitlab::DataBuilder::Pipeline).to receive(:build).once.and_call_original
@@ -2807,7 +2834,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe "#merge_requests_as_head_pipeline" do
- let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') }
+ let_it_be_with_reload(:pipeline) { create(:ci_empty_pipeline, status: 'created', ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') }
it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do
allow_next_instance_of(MergeRequest) do |instance|
@@ -2819,7 +2846,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it "doesn't return merge requests whose source branch doesn't match the pipeline's ref" do
- create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master')
+ create(:merge_request, :simple, source_project: project)
expect(pipeline.merge_requests_as_head_pipeline).to be_empty
end
@@ -2835,7 +2862,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#all_merge_requests' do
- let(:project) { create(:project) }
+ let_it_be_with_reload(:project) { create(:project) }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created, project: project) }
shared_examples 'a method that returns all merge requests for a given pipeline' do
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: pipeline_project, ref: 'master') }
@@ -2929,10 +2957,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#related_merge_requests' do
- let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
let(:other_merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'stable') }
- let(:branch_pipeline) { create(:ci_pipeline, project: project, ref: 'feature') }
+ let(:branch_pipeline) { create(:ci_pipeline, ref: 'feature') }
let(:merge_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, merge_request: merge_request) }
context 'for a branch pipeline' do
@@ -2969,9 +2996,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#open_merge_requests_refs' do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
- let!(:pipeline) { create(:ci_pipeline, user: user, project: project, ref: 'feature') }
+ let!(:pipeline) { create(:ci_pipeline, user: user, ref: 'feature') }
let!(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') }
subject { pipeline.open_merge_requests_refs }
@@ -3018,6 +3043,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#same_family_pipeline_ids' do
subject { pipeline.same_family_pipeline_ids.map(&:id) }
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, :created) }
+
context 'when pipeline is not child nor parent' do
it 'returns just the pipeline id' do
expect(subject).to contain_exactly(pipeline.id)
@@ -3025,7 +3052,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is child' do
- let(:parent) { create(:ci_pipeline, project: project) }
+ let(:parent) { create(:ci_pipeline) }
let!(:pipeline) { create(:ci_pipeline, child_of: parent) }
let!(:sibling) { create(:ci_pipeline, child_of: parent) }
@@ -3043,7 +3070,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is a child of a child pipeline' do
- let(:ancestor) { create(:ci_pipeline, project: project) }
+ let(:ancestor) { create(:ci_pipeline) }
let!(:parent) { create(:ci_pipeline, child_of: ancestor) }
let!(:pipeline) { create(:ci_pipeline, child_of: parent) }
let!(:cousin_parent) { create(:ci_pipeline, child_of: ancestor) }
@@ -3068,10 +3095,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#root_ancestor' do
subject { pipeline.root_ancestor }
- let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
context 'when pipeline is child of child pipeline' do
- let!(:root_ancestor) { create(:ci_pipeline, project: project) }
+ let!(:root_ancestor) { create(:ci_pipeline) }
let!(:parent_pipeline) { create(:ci_pipeline, child_of: root_ancestor) }
let!(:pipeline) { create(:ci_pipeline, child_of: parent_pipeline) }
@@ -3106,6 +3133,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#stuck?' do
+ let(:pipeline) { create(:ci_empty_pipeline, :created) }
+
before do
create(:ci_build, :pending, pipeline: pipeline)
end
@@ -3150,6 +3179,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#has_yaml_errors?' do
+ let(:pipeline) { build_stubbed(:ci_pipeline) }
+
context 'when yaml_errors is set' do
before do
pipeline.yaml_errors = 'File not found'
@@ -3219,7 +3250,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline is not the latest' do
before do
- create(:ci_pipeline, :success, project: project, ci_ref: pipeline.ci_ref)
+ create(:ci_pipeline, :success, ci_ref: pipeline.ci_ref)
end
it 'does not pass ref_status' do
@@ -3320,7 +3351,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#builds_in_self_and_descendants' do
subject(:builds) { pipeline.builds_in_self_and_descendants }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:pipeline) { create(:ci_pipeline) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
context 'when pipeline is standalone' do
@@ -3351,6 +3382,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#build_with_artifacts_in_self_and_descendants' do
+ let_it_be(:pipeline) { create(:ci_pipeline) }
let!(:build) { create(:ci_build, name: 'test', pipeline: pipeline) }
let(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) }
let!(:child_build) { create(:ci_build, :artifacts, name: 'test', pipeline: child_pipeline) }
@@ -3369,6 +3401,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#find_job_with_archive_artifacts' do
+ let(:pipeline) { create(:ci_pipeline) }
let!(:old_job) { create(:ci_build, name: 'rspec', retried: true, pipeline: pipeline) }
let!(:job_without_artifacts) { create(:ci_build, name: 'rspec', pipeline: pipeline) }
let!(:expected_job) { create(:ci_build, :artifacts, name: 'rspec', pipeline: pipeline ) }
@@ -3382,6 +3415,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#latest_builds_with_artifacts' do
+ let(:pipeline) { create(:ci_pipeline) }
let!(:fresh_build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
let!(:stale_build) { create(:ci_build, :success, :expired, :artifacts, pipeline: pipeline) }
@@ -3408,7 +3442,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#batch_lookup_report_artifact_for_file_type' do
context 'with code quality report artifact' do
- let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_reports) }
it "returns the code quality artifact" do
expect(pipeline.batch_lookup_report_artifact_for_file_type(:codequality)).to eq(pipeline.job_artifacts.sample)
@@ -3417,24 +3451,26 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#latest_report_builds' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
it 'returns build with test artifacts' do
- test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
- coverage_build = create(:ci_build, :coverage_reports, pipeline: pipeline, project: project)
+ test_build = create(:ci_build, :test_reports, pipeline: pipeline)
+ coverage_build = create(:ci_build, :coverage_reports, pipeline: pipeline)
create(:ci_build, :artifacts, pipeline: pipeline, project: project)
expect(pipeline.latest_report_builds).to contain_exactly(test_build, coverage_build)
end
it 'filters builds by scope' do
- test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
- create(:ci_build, :coverage_reports, pipeline: pipeline, project: project)
+ test_build = create(:ci_build, :test_reports, pipeline: pipeline)
+ create(:ci_build, :coverage_reports, pipeline: pipeline)
expect(pipeline.latest_report_builds(Ci::JobArtifact.test_reports)).to contain_exactly(test_build)
end
it 'only returns not retried builds' do
- test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
- create(:ci_build, :test_reports, :retried, pipeline: pipeline, project: project)
+ test_build = create(:ci_build, :test_reports, pipeline: pipeline)
+ create(:ci_build, :test_reports, :retried, pipeline: pipeline)
expect(pipeline.latest_report_builds).to contain_exactly(test_build)
end
@@ -3445,17 +3481,17 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline has builds with test reports' do
before do
- create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :test_reports, pipeline: pipeline)
end
context 'when pipeline status is running' do
- let(:pipeline) { create(:ci_pipeline, :running, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :running) }
it { is_expected.to be_falsey }
end
context 'when pipeline status is success' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { is_expected.to be_truthy }
end
@@ -3463,20 +3499,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline does not have builds with test reports' do
before do
- create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+ create(:ci_build, :artifacts, pipeline: pipeline)
end
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { is_expected.to be_falsey }
end
context 'when retried build has test reports' do
before do
- create(:ci_build, :retried, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :retried, :test_reports, pipeline: pipeline)
end
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { is_expected.to be_falsey }
end
@@ -3486,13 +3522,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { pipeline.has_coverage_reports? }
context 'when pipeline has a code coverage artifact' do
- let(:pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, :running, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, :running) }
it { expect(subject).to be_truthy }
end
context 'when pipeline does not have a code coverage artifact' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { expect(subject).to be_falsey }
end
@@ -3503,17 +3539,17 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline has builds with coverage reports' do
before do
- create(:ci_build, :coverage_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :coverage_reports, pipeline: pipeline)
end
context 'when pipeline status is running' do
- let(:pipeline) { create(:ci_pipeline, :running, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :running) }
it { expect(subject).to be_falsey }
end
context 'when pipeline status is success' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { expect(subject).to be_truthy }
end
@@ -3521,10 +3557,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline does not have builds with coverage reports' do
before do
- create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+ create(:ci_build, :artifacts, pipeline: pipeline)
end
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { expect(subject).to be_falsey }
end
@@ -3534,13 +3570,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
subject { pipeline.has_codequality_mr_diff_report? }
context 'when pipeline has a codequality mr diff report' do
- let(:pipeline) { create(:ci_pipeline, :with_codequality_mr_diff_report, :running, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :with_codequality_mr_diff_report, :running) }
it { expect(subject).to be_truthy }
end
context 'when pipeline does not have a codequality mr diff report' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { expect(subject).to be_falsey }
end
@@ -3551,17 +3587,17 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline has builds with codequality reports' do
before do
- create(:ci_build, :codequality_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :codequality_reports, pipeline: pipeline)
end
context 'when pipeline status is running' do
- let(:pipeline) { create(:ci_pipeline, :running, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :running) }
it { expect(subject).to be_falsey }
end
context 'when pipeline status is success' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it 'can generate a codequality report' do
expect(subject).to be_truthy
@@ -3581,10 +3617,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline does not have builds with codequality reports' do
before do
- create(:ci_build, :artifacts, pipeline: pipeline, project: project)
+ create(:ci_build, :artifacts, pipeline: pipeline)
end
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
it { expect(subject).to be_falsey }
end
@@ -3593,12 +3629,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#test_report_summary' do
subject { pipeline.test_report_summary }
- context 'when pipeline has multiple builds with report results' do
- let(:pipeline) { create(:ci_pipeline, :success, project: project) }
+ let(:pipeline) { create(:ci_pipeline, :success) }
+ context 'when pipeline has multiple builds with report results' do
before do
- create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline, project: project)
- create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline, project: project)
+ create(:ci_build, :success, :report_results, name: 'rspec', pipeline: pipeline)
+ create(:ci_build, :success, :report_results, name: 'java', pipeline: pipeline)
end
it 'returns test report summary with collected data' do
@@ -3616,13 +3652,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#test_reports' do
subject { pipeline.test_reports }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
context 'when pipeline has multiple builds with test reports' do
- let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let!(:build_java) { create(:ci_build, :success, name: 'java', pipeline: pipeline, project: project) }
+ let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) }
+ let!(:build_java) { create(:ci_build, :success, name: 'java', pipeline: pipeline) }
before do
- create(:ci_job_artifact, :junit, job: build_rspec, project: project)
- create(:ci_job_artifact, :junit_with_ant, job: build_java, project: project)
+ create(:ci_job_artifact, :junit, job: build_rspec)
+ create(:ci_job_artifact, :junit_with_ant, job: build_java)
end
it 'returns test reports with collected data' do
@@ -3632,8 +3670,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when builds are retried' do
- let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let!(:build_java) { create(:ci_build, :retried, :success, name: 'java', pipeline: pipeline, project: project) }
+ let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) }
+ let!(:build_java) { create(:ci_build, :retried, :success, name: 'java', pipeline: pipeline) }
it 'does not take retried builds into account' do
expect(subject.total_count).to be(0)
@@ -3653,13 +3691,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#accessibility_reports' do
subject { pipeline.accessibility_reports }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
context 'when pipeline has multiple builds with accessibility reports' do
- let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) }
+ let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) }
before do
- create(:ci_job_artifact, :accessibility, job: build_rspec, project: project)
- create(:ci_job_artifact, :accessibility_without_errors, job: build_golang, project: project)
+ create(:ci_job_artifact, :accessibility, job: build_rspec)
+ create(:ci_job_artifact, :accessibility_without_errors, job: build_golang)
end
it 'returns accessibility report with collected data' do
@@ -3670,8 +3710,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when builds are retried' do
- let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) }
+ let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) }
it 'returns empty urls for accessibility reports' do
expect(subject.urls).to be_empty
@@ -3689,13 +3729,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#coverage_reports' do
subject { pipeline.coverage_reports }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
context 'when pipeline has multiple builds with coverage reports' do
- let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let!(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let!(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) }
+ let!(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) }
before do
- create(:ci_job_artifact, :cobertura, job: build_rspec, project: project)
- create(:ci_job_artifact, :coverage_gocov_xml, job: build_golang, project: project)
+ create(:ci_job_artifact, :cobertura, job: build_rspec)
+ create(:ci_job_artifact, :coverage_gocov_xml, job: build_golang)
end
it 'returns coverage reports with collected data' do
@@ -3707,8 +3749,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'does not execute N+1 queries' do
- single_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
- single_rspec = create(:ci_build, :success, name: 'rspec', pipeline: single_build_pipeline, project: project)
+ single_build_pipeline = create(:ci_empty_pipeline, :created)
+ single_rspec = create(:ci_build, :success, name: 'rspec', pipeline: single_build_pipeline)
create(:ci_job_artifact, :cobertura, job: single_rspec, project: project)
control = ActiveRecord::QueryRecorder.new { single_build_pipeline.coverage_reports }
@@ -3717,8 +3759,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when builds are retried' do
- let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let!(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let!(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) }
+ let!(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) }
it 'does not take retried builds into account' do
expect(subject.files).to eql({})
@@ -3736,13 +3778,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#codequality_reports' do
subject(:codequality_reports) { pipeline.codequality_reports }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
context 'when pipeline has multiple builds with codequality reports' do
- let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let(:build_rspec) { create(:ci_build, :success, name: 'rspec', pipeline: pipeline) }
+ let(:build_golang) { create(:ci_build, :success, name: 'golang', pipeline: pipeline) }
before do
- create(:ci_job_artifact, :codequality, job: build_rspec, project: project)
- create(:ci_job_artifact, :codequality_without_errors, job: build_golang, project: project)
+ create(:ci_job_artifact, :codequality, job: build_rspec)
+ create(:ci_job_artifact, :codequality_without_errors, job: build_golang)
end
it 'returns codequality report with collected data' do
@@ -3750,8 +3794,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when builds are retried' do
- let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline, project: project) }
- let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline, project: project) }
+ let(:build_rspec) { create(:ci_build, :retried, :success, name: 'rspec', pipeline: pipeline) }
+ let(:build_golang) { create(:ci_build, :retried, :success, name: 'golang', pipeline: pipeline) }
it 'returns a codequality reports without degradations' do
expect(codequality_reports.degradations).to be_empty
@@ -3767,6 +3811,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#total_size' do
+ let(:pipeline) { create(:ci_pipeline) }
let!(:build_job1) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let!(:build_job2) { create(:ci_build, pipeline: pipeline, stage_idx: 0) }
let!(:test_job_failed_and_retried) { create(:ci_build, :failed, :retried, pipeline: pipeline, stage_idx: 1) }
@@ -3807,7 +3852,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline ref is the default branch of the project' do
let(:pipeline) do
- build(:ci_empty_pipeline, status: :created, project: project, ref: project.default_branch)
+ build(:ci_empty_pipeline, :created, project: project, ref: project.default_branch)
end
it "returns true" do
@@ -3817,7 +3862,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline ref is not the default branch of the project' do
let(:pipeline) do
- build(:ci_empty_pipeline, status: :created, project: project, ref: 'another_branch')
+ build(:ci_empty_pipeline, :created, project: project, ref: 'another_branch')
end
it "returns false" do
@@ -3827,7 +3872,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#find_stage_by_name' do
- let(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
let(:stage_name) { 'test' }
let(:stage) do
@@ -3907,10 +3952,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#parent_pipeline' do
- let_it_be(:project) { create(:project) }
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline) }
context 'when pipeline is triggered by a pipeline from the same project' do
- let_it_be(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:upstream_pipeline) { create(:ci_pipeline) }
let_it_be(:pipeline) { create(:ci_pipeline, child_of: upstream_pipeline) }
it 'returns the parent pipeline' do
@@ -3923,7 +3968,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is triggered by a pipeline from another project' do
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:pipeline) { create(:ci_pipeline) }
let!(:upstream_pipeline) { create(:ci_pipeline, project: create(:project), upstream_of: pipeline) }
it 'returns nil' do
@@ -3950,7 +3995,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#child_pipelines' do
let_it_be(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline triggered other pipelines on same project' do
let(:downstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
@@ -4004,6 +4049,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'upstream status interactions' do
+ let_it_be_with_reload(:pipeline) { create(:ci_pipeline, :created) }
+
context 'when a pipeline has an upstream status' do
context 'when an upstream status is a bridge' do
let(:bridge) { create(:ci_bridge, status: :pending) }
@@ -4062,6 +4109,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '#source_ref_path' do
subject { pipeline.source_ref_path }
+ let(:pipeline) { create(:ci_pipeline, :created) }
+
context 'when pipeline is for a branch' do
it { is_expected.to eq(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.source_ref.to_s) }
end
@@ -4074,13 +4123,15 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is for a tag' do
- let(:pipeline) { create(:ci_pipeline, project: project, tag: true) }
+ let(:pipeline) { create(:ci_pipeline, tag: true) }
it { is_expected.to eq(Gitlab::Git::TAG_REF_PREFIX + pipeline.source_ref.to_s) }
end
end
- describe "#builds_with_coverage" do
+ describe '#builds_with_coverage' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+
it 'returns builds with coverage only' do
rspec = create(:ci_build, name: 'rspec', coverage: 97.1, pipeline: pipeline)
jest = create(:ci_build, name: 'jest', coverage: 94.1, pipeline: pipeline)
@@ -4104,10 +4155,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#base_and_ancestors' do
- let(:same_project) { false }
-
subject { pipeline.base_and_ancestors(same_project: same_project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+ let(:same_project) { false }
+
context 'when pipeline is not child nor parent' do
it 'returns just the pipeline itself' do
expect(subject).to contain_exactly(pipeline)
@@ -4115,8 +4167,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is child' do
- let(:parent) { create(:ci_pipeline, project: pipeline.project) }
- let(:sibling) { create(:ci_pipeline, project: pipeline.project) }
+ let(:parent) { create(:ci_pipeline) }
+ let(:sibling) { create(:ci_pipeline) }
before do
create_source_pipeline(parent, pipeline)
@@ -4129,7 +4181,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is parent' do
- let(:child) { create(:ci_pipeline, project: pipeline.project) }
+ let(:child) { create(:ci_pipeline) }
before do
create_source_pipeline(pipeline, child)
@@ -4141,8 +4193,9 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is a child of a child pipeline' do
- let(:ancestor) { create(:ci_pipeline, project: pipeline.project) }
- let(:parent) { create(:ci_pipeline, project: pipeline.project) }
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+ let(:ancestor) { create(:ci_pipeline) }
+ let(:parent) { create(:ci_pipeline) }
before do
create_source_pipeline(ancestor, parent)
@@ -4155,6 +4208,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline is a triggered pipeline' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
let(:upstream) { create(:ci_pipeline, project: create(:project)) }
before do
@@ -4178,8 +4232,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'reset_ancestor_bridges!' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+
context 'when the pipeline is a child pipeline and the bridge is depended' do
- let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_pipeline) { create(:ci_pipeline) }
let!(:bridge) { create_bridge(parent_pipeline, pipeline, true) }
it 'marks source bridge as pending' do
@@ -4203,7 +4259,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when the pipeline is a child pipeline and the bridge is not depended' do
- let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
+ let!(:parent_pipeline) { create(:ci_pipeline) }
let!(:bridge) { create_bridge(parent_pipeline, pipeline, false) }
it 'does not touch source bridge' do
@@ -4239,6 +4295,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe 'test failure history processing' do
+ let(:pipeline) { build(:ci_pipeline, :created) }
+
it 'performs the service asynchronously when the pipeline is completed' do
service = double
@@ -4250,21 +4308,23 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#latest_test_report_builds' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+
it 'returns pipeline builds with test report artifacts' do
- test_build = create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ test_build = create(:ci_build, :test_reports, pipeline: pipeline)
create(:ci_build, :artifacts, pipeline: pipeline, project: project)
expect(pipeline.latest_test_report_builds).to contain_exactly(test_build)
end
it 'preloads project on each build to avoid N+1 queries' do
- create(:ci_build, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :test_reports, pipeline: pipeline)
control_count = ActiveRecord::QueryRecorder.new do
pipeline.latest_test_report_builds.map(&:project).map(&:full_path)
end
- multi_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
+ multi_build_pipeline = create(:ci_empty_pipeline, :created)
create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project)
create(:ci_build, :test_reports, pipeline: multi_build_pipeline, project: project)
@@ -4274,30 +4334,32 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
describe '#builds_with_failed_tests' do
+ let_it_be(:pipeline) { create(:ci_pipeline, :created) }
+
it 'returns pipeline builds with test report artifacts' do
- failed_build = create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
- create(:ci_build, :success, :test_reports, pipeline: pipeline, project: project)
+ failed_build = create(:ci_build, :failed, :test_reports, pipeline: pipeline)
+ create(:ci_build, :success, :test_reports, pipeline: pipeline)
expect(pipeline.builds_with_failed_tests).to contain_exactly(failed_build)
end
it 'supports limiting the number of builds to fetch' do
- create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
- create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline)
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline)
expect(pipeline.builds_with_failed_tests(limit: 1).count).to eq(1)
end
it 'preloads project on each build to avoid N+1 queries' do
- create(:ci_build, :failed, :test_reports, pipeline: pipeline, project: project)
+ create(:ci_build, :failed, :test_reports, pipeline: pipeline)
control_count = ActiveRecord::QueryRecorder.new do
pipeline.builds_with_failed_tests.map(&:project).map(&:full_path)
end
- multi_build_pipeline = create(:ci_empty_pipeline, status: :created, project: project)
- create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline, project: project)
- create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline, project: project)
+ multi_build_pipeline = create(:ci_empty_pipeline, :created)
+ create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline)
+ create(:ci_build, :failed, :test_reports, pipeline: multi_build_pipeline)
expect { multi_build_pipeline.builds_with_failed_tests.map(&:project).map(&:full_path) }
.not_to exceed_query_limit(control_count)
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index f78aa61529a..ff8bb820cc5 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -602,6 +602,38 @@ RSpec.describe ProjectPresenter do
end
end
+ describe 'experiment(:repo_integrations_link)' do
+ context 'when enabled' do
+ before do
+ stub_experiments(repo_integrations_link: :candidate)
+ end
+
+ it 'includes a button to configure integrations for maintainers' do
+ project.add_maintainer(user)
+
+ expect(empty_repo_statistics_buttons.map(&:label)).to include(
+ a_string_including('Configure Integration')
+ )
+ end
+
+ it 'does not include a button if not a maintainer' do
+ expect(empty_repo_statistics_buttons.map(&:label)).not_to include(
+ a_string_including('Configure Integration')
+ )
+ end
+ end
+
+ context 'when disabled' do
+ it 'does not include a button' do
+ project.add_maintainer(user)
+
+ expect(empty_repo_statistics_buttons.map(&:label)).not_to include(
+ a_string_including('Configure Integration')
+ )
+ end
+ end
+ end
+
context 'for a developer' do
before do
project.add_developer(user)
diff --git a/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb b/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb
index 405e86ee89f..e0aa2fc8d56 100644
--- a/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb
+++ b/spec/views/admin/application_settings/_package_registry.html.haml_spec.rb
@@ -48,18 +48,18 @@ RSpec.describe 'admin/application_settings/_package_registry' do
end
context 'with multiple plans' do
- let_it_be(:plan) { create(:plan, name: 'Gold') }
- let_it_be(:gold_plan_limits) { create(:plan_limits, :with_package_file_sizes, plan: plan) }
+ let_it_be(:plan) { create(:plan, name: 'Ultimate') }
+ let_it_be(:ultimate_plan_limits) { create(:plan_limits, :with_package_file_sizes, plan: plan) }
before do
- assign(:plans, [default_plan_limits.plan, gold_plan_limits.plan])
+ assign(:plans, [default_plan_limits.plan, ultimate_plan_limits.plan])
end
it 'displays the plan name when there is more than one plan' do
subject
expect(page).to have_content('Default')
- expect(page).to have_content('Gold')
+ expect(page).to have_content('Ultimate')
end
end
end
diff --git a/vendor/project_templates/learn_gitlab_gold_trial.tar.gz b/vendor/project_templates/learn_gitlab_ultimate_trial.tar.gz
index 2ec47956706..2ec47956706 100644
--- a/vendor/project_templates/learn_gitlab_gold_trial.tar.gz
+++ b/vendor/project_templates/learn_gitlab_ultimate_trial.tar.gz
Binary files differ