Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
9796aa22cd
commit
bf0d6692fc
|
@ -1,15 +0,0 @@
|
|||
test-reliability-report:
|
||||
extends:
|
||||
- .qa:rules:reliable-reports:schedule
|
||||
image:
|
||||
name: ${CI_REGISTRY_IMAGE}/gitlab-ee-qa:${CI_DEFAULT_BRANCH}
|
||||
entrypoint: [""]
|
||||
before_script:
|
||||
- cd /home/gitlab/qa
|
||||
script:
|
||||
- echo "Generate report for 'staging-full' runs"
|
||||
- bundle exec rake "reliable_spec_report[staging-full,30,true]"
|
||||
- bundle exec rake "unreliable_spec_report[staging-full,30,true]"
|
||||
- echo "Generate report for 'package-and-qa' runs"
|
||||
- bundle exec rake "reliable_spec_report[package-and-qa,30,true]"
|
||||
- bundle exec rake "unreliable_spec_report[package-and-qa,30,true]"
|
|
@ -766,11 +766,6 @@
|
|||
changes: *feature-flag-development-config-patterns
|
||||
allow_failure: true
|
||||
|
||||
.qa:rules:reliable-reports:schedule:
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $QA_RELIABLE_REPORT == "true"'
|
||||
allow_failure: true
|
||||
|
||||
###############
|
||||
# Rails rules #
|
||||
###############
|
||||
|
|
|
@ -94,10 +94,7 @@ _Consider adding links to check for Sentry errors, Production logs for 5xx, 302s
|
|||
|
||||
### Global rollout on production
|
||||
|
||||
For visibility, all `/chatops` commands that target production should be:
|
||||
|
||||
- Executed in the `#production` slack channel.
|
||||
- Cross-posted (with the command results) to the responsible team's slack channel (`#g_TEAM_NAME`).
|
||||
For visibility, all `/chatops` commands that target production should be executed in the `#production` slack channel and cross-posted (with the command results) to the responsible team's slack channel (`#g_TEAM_NAME`).
|
||||
|
||||
- [ ] [Incrementally roll out](https://docs.gitlab.com/ee/development/feature_flags/controls.html#process) the feature.
|
||||
- If the feature flag in code has [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform **actor-based** rollout.
|
||||
|
|
|
@ -2594,7 +2594,6 @@ Style/OpenStructUse:
|
|||
- 'spec/lib/gitlab/database/migrations/runner_spec.rb'
|
||||
- 'spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb'
|
||||
- 'spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb'
|
||||
- 'spec/lib/gitlab/grape_logging/loggers/exception_logger_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/pagination/keyset/connection_generic_keyset_spec.rb'
|
||||
- 'spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb'
|
||||
- 'spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
|
||||
|
@ -2604,7 +2603,6 @@ Style/OpenStructUse:
|
|||
- 'spec/models/design_management/design_at_version_spec.rb'
|
||||
- 'spec/models/user_spec.rb'
|
||||
- 'spec/presenters/packages/nuget/search_results_presenter_spec.rb'
|
||||
- 'spec/requests/api/import_github_spec.rb'
|
||||
- 'spec/services/packages/nuget/metadata_extraction_service_spec.rb'
|
||||
- 'spec/services/projects/import_service_spec.rb'
|
||||
- 'spec/services/system_note_service_spec.rb'
|
||||
|
|
|
@ -30,7 +30,7 @@ export function getAllExperimentContexts() {
|
|||
return Object.values(getExperimentsData()).map(createGitlabExperimentContext);
|
||||
}
|
||||
|
||||
export function isExperimentVariant(experimentName, variantName) {
|
||||
export function isExperimentVariant(experimentName, variantName = CANDIDATE_VARIANT) {
|
||||
return getExperimentData(experimentName)?.variant === variantName;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
MEMBER_AREAS_OF_FOCUS,
|
||||
INVITE_MEMBERS_FOR_TASK,
|
||||
MODAL_LABELS,
|
||||
LEARN_GITLAB,
|
||||
} from '../constants';
|
||||
import eventHub from '../event_hub';
|
||||
import {
|
||||
|
@ -200,7 +201,8 @@ export default {
|
|||
},
|
||||
tasksToBeDoneEnabled() {
|
||||
return (
|
||||
getParameterValues('open_modal')[0] === 'invite_members_for_task' &&
|
||||
(getParameterValues('open_modal')[0] === 'invite_members_for_task' ||
|
||||
this.isOnLearnGitlab) &&
|
||||
this.tasksToBeDoneOptions.length
|
||||
);
|
||||
},
|
||||
|
@ -221,11 +223,18 @@ export default {
|
|||
? this.selectedTaskProject.id
|
||||
: '';
|
||||
},
|
||||
isOnLearnGitlab() {
|
||||
return this.source === LEARN_GITLAB;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
eventHub.$on('openModal', (options) => {
|
||||
this.openModal(options);
|
||||
this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.view);
|
||||
if (this.isOnLearnGitlab) {
|
||||
this.trackEvent(INVITE_MEMBERS_FOR_TASK.name, this.source);
|
||||
} else {
|
||||
this.trackEvent(MEMBER_AREAS_OF_FOCUS.name, MEMBER_AREAS_OF_FOCUS.view);
|
||||
}
|
||||
});
|
||||
|
||||
if (this.tasksToBeDoneEnabled) {
|
||||
|
@ -303,7 +312,7 @@ export default {
|
|||
: Api.groupShareWithGroup.bind(Api);
|
||||
|
||||
apiShareWithGroup(this.id, this.shareWithGroupPostData(this.groupToBeSharedWith.id))
|
||||
.then(this.showToastMessageSuccess)
|
||||
.then(this.showSuccessMessage)
|
||||
.catch(this.showInvalidFeedbackMessage);
|
||||
},
|
||||
submitInviteMembers() {
|
||||
|
@ -332,7 +341,7 @@ export default {
|
|||
this.trackinviteMembersForTask();
|
||||
|
||||
Promise.all(promises)
|
||||
.then(this.conditionallyShowToastSuccess)
|
||||
.then(this.conditionallyShowSuccessMessage)
|
||||
.catch(this.showInvalidFeedbackMessage);
|
||||
},
|
||||
inviteByEmailPostData(usersToInviteByEmail) {
|
||||
|
@ -364,11 +373,11 @@ export default {
|
|||
group_access: this.selectedAccessLevel,
|
||||
};
|
||||
},
|
||||
conditionallyShowToastSuccess(response) {
|
||||
conditionallyShowSuccessMessage(response) {
|
||||
const message = this.unescapeMsg(responseMessageFromSuccess(response));
|
||||
|
||||
if (message === '') {
|
||||
this.showToastMessageSuccess();
|
||||
this.showSuccessMessage();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -376,8 +385,12 @@ export default {
|
|||
this.invalidFeedbackMessage = message;
|
||||
this.isLoading = false;
|
||||
},
|
||||
showToastMessageSuccess() {
|
||||
this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions);
|
||||
showSuccessMessage() {
|
||||
if (this.isOnLearnGitlab) {
|
||||
eventHub.$emit('showSuccessfulInvitationsAlert');
|
||||
} else {
|
||||
this.$toast.show(this.$options.labels.toastMessageSuccessful, this.toastOptions);
|
||||
}
|
||||
this.closeModal();
|
||||
},
|
||||
showInvalidFeedbackMessage(response) {
|
||||
|
|
|
@ -144,3 +144,5 @@ export const MODAL_LABELS = {
|
|||
headerCloseLabel: HEADER_CLOSE_LABEL,
|
||||
areasOfFocusLabel: AREAS_OF_FOCUS_LABEL,
|
||||
};
|
||||
|
||||
export const LEARN_GITLAB = 'learn_gitlab';
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
<script>
|
||||
import { GlProgressBar, GlSprintf } from '@gitlab/ui';
|
||||
import { GlProgressBar, GlSprintf, GlAlert } from '@gitlab/ui';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import { s__ } from '~/locale';
|
||||
import { ACTION_LABELS, ACTION_SECTIONS } from '../constants';
|
||||
import LearnGitlabSectionCard from './learn_gitlab_section_card.vue';
|
||||
|
||||
export default {
|
||||
components: { GlProgressBar, GlSprintf, LearnGitlabSectionCard },
|
||||
components: { GlProgressBar, GlSprintf, GlAlert, LearnGitlabSectionCard },
|
||||
i18n: {
|
||||
title: s__('LearnGitLab|Learn GitLab'),
|
||||
description: s__(
|
||||
'LearnGitLab|Ready to get started with GitLab? Follow these steps to set up your workspace, plan and commit changes, and deploy your project.',
|
||||
),
|
||||
percentageCompleted: s__(`LearnGitLab|%{percentage}%{percentSymbol} completed`),
|
||||
successfulInvitations: s__(
|
||||
"LearnGitLab|Your team is growing! You've successfully invited new team members to the %{projectName} project.",
|
||||
),
|
||||
},
|
||||
props: {
|
||||
actions: {
|
||||
|
@ -28,12 +31,22 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
project: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSuccessfulInvitationsAlert: false,
|
||||
actionsData: this.actions,
|
||||
};
|
||||
},
|
||||
maxValue: Object.keys(ACTION_LABELS).length,
|
||||
actionSections: Object.keys(ACTION_SECTIONS),
|
||||
computed: {
|
||||
progressValue() {
|
||||
return Object.values(this.actions).filter((a) => a.completed).length;
|
||||
return Object.values(this.actionsData).filter((a) => a.completed).length;
|
||||
},
|
||||
progressPercentage() {
|
||||
return Math.round((this.progressValue / this.$options.maxValue) * 100);
|
||||
|
@ -43,14 +56,23 @@ export default {
|
|||
if (this.inviteMembersOpen) {
|
||||
this.openInviteMembersModal('celebrate');
|
||||
}
|
||||
|
||||
eventHub.$on('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert);
|
||||
},
|
||||
beforeDestroy() {
|
||||
eventHub.$off('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert);
|
||||
},
|
||||
methods: {
|
||||
openInviteMembersModal(mode) {
|
||||
eventHub.$emit('openModal', { mode, inviteeType: 'members', source: 'learn-gitlab' });
|
||||
},
|
||||
handleShowSuccessfulInvitationsAlert() {
|
||||
this.showSuccessfulInvitationsAlert = true;
|
||||
this.markActionAsCompleted('userAdded');
|
||||
},
|
||||
actionsFor(section) {
|
||||
const actions = Object.fromEntries(
|
||||
Object.entries(this.actions).filter(
|
||||
Object.entries(this.actionsData).filter(
|
||||
([action]) => ACTION_LABELS[action].section === section,
|
||||
),
|
||||
);
|
||||
|
@ -59,11 +81,34 @@ export default {
|
|||
svgFor(section) {
|
||||
return this.sections[section].svg;
|
||||
},
|
||||
markActionAsCompleted(completedAction) {
|
||||
Object.keys(this.actionsData).forEach((action) => {
|
||||
if (action === completedAction) {
|
||||
this.actionsData[action].completed = true;
|
||||
this.modifySidebarPercentage();
|
||||
}
|
||||
});
|
||||
},
|
||||
modifySidebarPercentage() {
|
||||
const el = document.querySelector('.sidebar-top-level-items .active .count');
|
||||
el.textContent = `${this.progressPercentage}%`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<gl-alert
|
||||
v-if="showSuccessfulInvitationsAlert"
|
||||
class="gl-mt-5"
|
||||
@dismiss="showSuccessfulInvitationsAlert = false"
|
||||
>
|
||||
<gl-sprintf :message="$options.i18n.successfulInvitations">
|
||||
<template #projectName>
|
||||
<strong>{{ project.name }}</strong>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</gl-alert>
|
||||
<div class="row">
|
||||
<div class="gl-mb-7 gl-ml-5">
|
||||
<h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<script>
|
||||
import { GlLink, GlIcon } from '@gitlab/ui';
|
||||
import { isExperimentVariant } from '~/experimentation/utils';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import { s__ } from '~/locale';
|
||||
import { ACTION_LABELS } from '../constants';
|
||||
|
||||
|
@ -24,6 +26,20 @@ export default {
|
|||
trialOnly() {
|
||||
return ACTION_LABELS[this.action].trialRequired;
|
||||
},
|
||||
showInviteModalLink() {
|
||||
return (
|
||||
this.action === 'userAdded' && isExperimentVariant('invite_for_help_continuous_onboarding')
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
eventHub.$emit('openModal', {
|
||||
inviteeType: 'members',
|
||||
source: 'learn_gitlab',
|
||||
tasksToBeDoneEnabled: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -33,18 +49,27 @@ export default {
|
|||
<gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<gl-link
|
||||
target="_blank"
|
||||
:href="value.url"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
>
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</gl-link>
|
||||
</span>
|
||||
<gl-link
|
||||
v-else-if="showInviteModalLink"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
|
||||
data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
|
||||
data-testid="invite-for-help-continuous-onboarding-experiment-link"
|
||||
@click="openModal"
|
||||
>
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</gl-link>
|
||||
<gl-link
|
||||
v-else
|
||||
target="_blank"
|
||||
:href="value.url"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
>
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</gl-link>
|
||||
<span v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
|
||||
- {{ $options.i18n.trialOnly }}
|
||||
</span>
|
||||
|
|
|
@ -12,17 +12,18 @@ function initLearnGitlab() {
|
|||
|
||||
const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions));
|
||||
const sections = convertObjectPropsToCamelCase(JSON.parse(el.dataset.sections));
|
||||
const project = convertObjectPropsToCamelCase(JSON.parse(el.dataset.project));
|
||||
const { inviteMembersOpen } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(LearnGitlab, {
|
||||
props: { actions, sections, inviteMembersOpen },
|
||||
props: { actions, sections, project, inviteMembersOpen },
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
initInviteMembersModal();
|
||||
initLearnGitlab();
|
||||
initInviteMembersModal();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
class Projects::LearnGitlabController < Projects::ApplicationController
|
||||
before_action :authenticate_user!
|
||||
before_action :check_experiment_enabled?
|
||||
before_action :enable_invite_for_help_continuous_onboarding_experiment
|
||||
|
||||
feature_category :users
|
||||
|
||||
|
@ -14,4 +15,13 @@ class Projects::LearnGitlabController < Projects::ApplicationController
|
|||
def check_experiment_enabled?
|
||||
return access_denied! unless helpers.learn_gitlab_enabled?(project)
|
||||
end
|
||||
|
||||
def enable_invite_for_help_continuous_onboarding_experiment
|
||||
return unless current_user.can?(:admin_group_member, project.namespace)
|
||||
|
||||
experiment(:invite_for_help_continuous_onboarding, namespace: project.namespace) do |e|
|
||||
e.candidate {}
|
||||
e.record!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,7 +42,7 @@ module InviteMembersHelper
|
|||
e.candidate { dataset.merge!(areas_of_focus_options: member_areas_of_focus_options.to_json, no_selection_areas_of_focus: ['no_selection']) }
|
||||
end
|
||||
|
||||
if show_invite_members_for_task?
|
||||
if show_invite_members_for_task?(source)
|
||||
dataset.merge!(
|
||||
tasks_to_be_done_options: tasks_to_be_done_options.to_json,
|
||||
projects: projects_for_source(source).to_json,
|
||||
|
@ -80,10 +80,12 @@ module InviteMembersHelper
|
|||
{}
|
||||
end
|
||||
|
||||
def show_invite_members_for_task?
|
||||
return unless current_user && experiment(:invite_members_for_task).enabled?
|
||||
def show_invite_members_for_task?(source)
|
||||
return unless current_user
|
||||
|
||||
params[:open_modal] == 'invite_members_for_task'
|
||||
invite_members_for_task_experiment = experiment(:invite_members_for_task).enabled? && params[:open_modal] == 'invite_members_for_task'
|
||||
invite_for_help_continuous_onboarding = source.is_a?(Project) && experiment(:invite_for_help_continuous_onboarding, namespace: source.namespace).variant.name == 'candidate'
|
||||
invite_members_for_task_experiment || invite_for_help_continuous_onboarding
|
||||
end
|
||||
|
||||
def tasks_to_be_done_options
|
||||
|
|
|
@ -10,7 +10,8 @@ module LearnGitlabHelper
|
|||
def learn_gitlab_data(project)
|
||||
{
|
||||
actions: onboarding_actions_data(project).to_json,
|
||||
sections: onboarding_sections_data.to_json
|
||||
sections: onboarding_sections_data.to_json,
|
||||
project: onboarding_project_data(project).to_json
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -56,6 +57,10 @@ module LearnGitlabHelper
|
|||
}
|
||||
end
|
||||
|
||||
def onboarding_project_data(project)
|
||||
{ name: project.name }
|
||||
end
|
||||
|
||||
def action_urls
|
||||
LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
|
||||
.merge(LearnGitlab::Onboarding::ACTION_DOC_URLS)
|
||||
|
|
|
@ -12,11 +12,7 @@ class U2fRegistration < ApplicationRecord
|
|||
converter = Gitlab::Auth::U2fWebauthnConverter.new(self)
|
||||
WebauthnRegistration.create!(converter.convert)
|
||||
rescue StandardError => ex
|
||||
Gitlab::AppJsonLogger.error(
|
||||
event: 'u2f_migration',
|
||||
error: ex.class.name,
|
||||
backtrace: ::Gitlab::BacktraceCleaner.clean_backtrace(ex.backtrace),
|
||||
message: "U2F to WebAuthn conversion failed")
|
||||
Gitlab::ErrorTracking.track_exception(ex, u2f_registration_id: self.id)
|
||||
end
|
||||
|
||||
def update_webauthn_registration
|
||||
|
|
|
@ -1614,8 +1614,14 @@ class User < ApplicationRecord
|
|||
.joins(:runner)
|
||||
.select('ci_runners.*')
|
||||
|
||||
base_and_descendants = if Feature.enabled?(:linear_user_ci_owned_runners, self, default_enabled: :yaml)
|
||||
owned_groups.self_and_descendant_ids
|
||||
else
|
||||
Gitlab::ObjectHierarchy.new(owned_groups).base_and_descendants.select(:id)
|
||||
end
|
||||
|
||||
group_runners = Ci::RunnerNamespace
|
||||
.where(namespace_id: Gitlab::ObjectHierarchy.new(owned_groups).base_and_descendants.select(:id))
|
||||
.where(namespace_id: base_and_descendants)
|
||||
.joins(:runner)
|
||||
.select('ci_runners.*')
|
||||
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
- data = learn_gitlab_data(@project)
|
||||
- invite_members_open = session.delete(:confetti_post_signup)
|
||||
|
||||
= render 'projects/invite_members_modal', project: @project
|
||||
|
||||
- experiment(:confetti_post_signup, actor: current_user) do |e|
|
||||
- e.control do
|
||||
#js-learn-gitlab-app{ data: data }
|
||||
- e.candidate do
|
||||
= render 'projects/invite_members_modal', project: @project
|
||||
#js-learn-gitlab-app{ data: data.merge(invite_members_open: invite_members_open) }
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: linear_user_ci_owned_runners
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68848
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339435
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::access
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: invite_for_help_continuous_onboarding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73846
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345708
|
||||
milestone: '14.5'
|
||||
type: experiment
|
||||
group: group::activation
|
||||
default_enabled: false
|
|
@ -1,83 +1,9 @@
|
|||
---
|
||||
stage: Enablement
|
||||
group: Distribution
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
redirect_to: 'supported_os.md'
|
||||
remove_date: '2022-02-18'
|
||||
---
|
||||
|
||||
# OS Versions that are no longer supported **(FREE SELF)**
|
||||
This document was moved to [another location](supported_os.md).
|
||||
|
||||
GitLab provides omnibus packages for operating systems only until their
|
||||
EOL (End-Of-Life). After the EOL date of the OS, GitLab will stop releasing
|
||||
official packages. The list of deprecated operating systems and the final GitLab
|
||||
release for them can be found below:
|
||||
|
||||
| OS Version | End Of Life | Last supported GitLab version |
|
||||
| --------------- | ---------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Raspbian Wheezy | [May 2015](https://downloads.raspberrypi.org/raspbian/images/raspbian-2015-05-07/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_8.17&dist=debian%2Fwheezy) 8.17 |
|
||||
| OpenSUSE 13.2 | [January 2017](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-9.1&dist=opensuse%2F13.2) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-9.1&dist=opensuse%2F13.2) 9.1 |
|
||||
| Ubuntu 12.04 | [April 2017](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_9.1&dist=ubuntu%2Fprecise) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_9.1&dist=ubuntu%2Fprecise) 9.1 |
|
||||
| OpenSUSE 42.1 | [May 2017](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-9.3&dist=opensuse%2F42.1) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-9.3&dist=opensuse%2F42.1) 9.3 |
|
||||
| OpenSUSE 42.2 | [January 2018](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-10.4&dist=opensuse%2F42.2) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-10.4&dist=opensuse%2F42.2) 10.4 |
|
||||
| Debian Wheezy | [May 2018](https://www.debian.org/News/2018/20180601) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_11.6&dist=debian%2Fwheezy) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_11.6&dist=debian%2Fwheezy) 11.6 |
|
||||
| Raspbian Jessie | [May 2017](https://downloads.raspberrypi.org/raspbian/images/raspbian-2017-07-05/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_11.7&dist=debian%2Fjessie) 11.7 |
|
||||
| Ubuntu 14.04 | [April 2019](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_11.10&dist=ubuntu%2Ftrusty) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_11.10&dist=ubuntu%2Ftrusty) 11.10 |
|
||||
| OpenSUSE 42.3 | [July 2019](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-12.1&dist=opensuse%2F42.3) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-12.1&dist=opensuse%2F42.3) 12.1 |
|
||||
| OpenSUSE 15.0 | [December 2019](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-12.5&dist=opensuse%2F15.0) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-12.5&dist=opensuse%2F15.0) 12.5 |
|
||||
| Raspbian Stretch | [June 2020](https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-04-09/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_13.2&dist=raspbian%2Fstretch) 13.3 |
|
||||
| Debian Jessie | [June 2020](https://www.debian.org/News/2020/20200709) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_13.2&dist=debian%2Fjessie) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_13.2&dist=debian%2Fjessie) 13.3 |
|
||||
| CentOS 6 | [November 2020](https://wiki.centos.org/About/Product) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=13.6&filter=all&filter=all&dist=el%2F6) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=13.6&filter=all&filter=all&dist=el%2F6) 13.6 |
|
||||
| OpenSUSE 15.1 | [November 2020](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-13.12&dist=opensuse%2F15.1) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-13.12&dist=opensuse%2F15.2) 13.12 |
|
||||
| Ubuntu 16.04 | [April 2021](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_13.12&dist=ubuntu%2Fxenial) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_13.12&dist=ubuntu%2Fxenial) 13.12 |
|
||||
|
||||
NOTE:
|
||||
An exception to this deprecation policy is when we are unable to provide
|
||||
packages for the next version of the operating system. The most common reason
|
||||
for this our package repository provider, Packagecloud, not supporting newer
|
||||
versions and hence we can't upload packages to it.
|
||||
|
||||
## Update GitLab package sources after upgrading the OS
|
||||
|
||||
After upgrading the Operating System (OS) as per its own documentation,
|
||||
it may be necessary to also update the GitLab package source URL
|
||||
in your package manager configuration.
|
||||
If your package manager reports that no further updates are available,
|
||||
although [new versions have been released](https://about.gitlab.com/releases/categories/releases/), repeat the
|
||||
"Add the GitLab package repository" instructions
|
||||
of the [Linux package install guide](https://about.gitlab.com/install/#content).
|
||||
Future GitLab upgrades will now be fetched according to your upgraded OS.
|
||||
|
||||
## Supported Operating Systems
|
||||
|
||||
GitLab officially supports LTS versions of operating systems. While OSs like
|
||||
Ubuntu have a clear distinction between LTS and non-LTS versions, there are
|
||||
other OSs, openSUSE for example, that don't follow the LTS concept. Hence to
|
||||
avoid confusion, the official policy is that at any point of time, all the
|
||||
operating systems supported by GitLab are listed in the [installation
|
||||
page](https://about.gitlab.com/install/).
|
||||
|
||||
The following lists the currently supported OSs and their possible EOL dates.
|
||||
|
||||
| OS Version | First supported GitLab version | Arch | OS EOL | Details |
|
||||
| ---------------- | ------------------------------ | --------------- | ------------- | ------------------------------------------------------------ |
|
||||
| CentOS 7 | GitLab CE / GitLab EE 7.10.0 | x86_64 | June 2024 | <https://wiki.centos.org/About/Product> |
|
||||
| CentOS 8 | GitLab CE / GitLab EE 12.8.1 | x86_64, aarch64 | Dec 2021 | <https://wiki.centos.org/About/Product> |
|
||||
| Debian 9 | GitLab CE / GitLab EE 9.3.0 | amd64 | 2022 | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
| Debian 10 | GitLab CE / GitLab EE 12.2.0 | amd64, arm64 | TBD | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
| OpenSUSE 15.2 | GitLab CE / GitLab EE 13.11.0 | x86_64, aarch64 | Dec 2021 | <https://en.opensuse.org/Lifetime> |
|
||||
| OpenSUSE 15.3 | GitLab CE / GitLab EE 14.5.0 | x86_64, aarch64 | Nov 2022 | <https://en.opensuse.org/Lifetime> |
|
||||
| SLES 12 | GitLab EE 9.0.0 | x86_64 | Oct 2027 | <https://www.suse.com/lifecycle/> |
|
||||
| Ubuntu 18.04 | GitLab CE / GitLab EE 10.7.0 | amd64 | April 2023 | <https://wiki.ubuntu.com/Releases> |
|
||||
| Ubuntu 20.04 | GitLab CE / GitLab EE 13.2.0 | amd64, arm64 | April 2025 | <https://wiki.ubuntu.com/Releases> |
|
||||
| Raspbian Buster | GitLab CE 12.2.0 | armhf | 2022 | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
|
||||
### Packages for ARM64
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/issues/27) in GitLab 13.4.
|
||||
|
||||
GitLab provides arm64/aarch64 packages for some supported operating systems.
|
||||
You can see if your operating system architecture is supported in the table
|
||||
above.
|
||||
|
||||
WARNING:
|
||||
There are currently still some [known issues and limitation](https://gitlab.com/groups/gitlab-org/-/epics/4397)
|
||||
running GitLab on ARM.
|
||||
<!-- This redirect file can be deleted after <2022-02-18>. -->
|
||||
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
stage: Enablement
|
||||
group: Distribution
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Supported Operating Systems
|
||||
|
||||
GitLab officially supports LTS versions of operating systems. While OSs like
|
||||
Ubuntu have a clear distinction between LTS and non-LTS versions, there are
|
||||
other OSs, openSUSE for example, that don't follow the LTS concept. Hence to
|
||||
avoid confusion, the official policy is that at any point of time, all the
|
||||
operating systems supported by GitLab are listed in the [installation
|
||||
page](https://about.gitlab.com/install/).
|
||||
|
||||
The following lists the currently supported OSs and their possible EOL dates.
|
||||
|
||||
| OS Version | First supported GitLab version | Arch | OS EOL | Details |
|
||||
| ---------------- | ------------------------------ | --------------- | ------------- | ------------------------------------------------------------ |
|
||||
| CentOS 7 | GitLab CE / GitLab EE 7.10.0 | x86_64 | June 2024 | <https://wiki.centos.org/About/Product> |
|
||||
| CentOS 8 | GitLab CE / GitLab EE 12.8.1 | x86_64, aarch64 | Dec 2021 | <https://wiki.centos.org/About/Product> |
|
||||
| Debian 9 | GitLab CE / GitLab EE 9.3.0 | amd64 | 2022 | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
| Debian 10 | GitLab CE / GitLab EE 12.2.0 | amd64, arm64 | TBD | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
| OpenSUSE 15.2 | GitLab CE / GitLab EE 13.11.0 | x86_64, aarch64 | Dec 2021 | <https://en.opensuse.org/Lifetime> |
|
||||
| OpenSUSE 15.3 | GitLab CE / GitLab EE 14.5.0 | x86_64, aarch64 | Nov 2022 | <https://en.opensuse.org/Lifetime> |
|
||||
| SLES 12 | GitLab EE 9.0.0 | x86_64 | Oct 2027 | <https://www.suse.com/lifecycle/> |
|
||||
| Ubuntu 18.04 | GitLab CE / GitLab EE 10.7.0 | amd64 | April 2023 | <https://wiki.ubuntu.com/Releases> |
|
||||
| Ubuntu 20.04 | GitLab CE / GitLab EE 13.2.0 | amd64, arm64 | April 2025 | <https://wiki.ubuntu.com/Releases> |
|
||||
| Raspbian Buster | GitLab CE 12.2.0 | armhf | 2022 | <https://wiki.debian.org/DebianReleases#Production_Releases> |
|
||||
|
||||
NOTE:
|
||||
CentOS 8 will be EOL on December 31, 2021. In GitLab 14.5 and later,
|
||||
[CentOS builds work in AlmaLinux](https://gitlab.com/gitlab-org/distribution/team-tasks/-/issues/954#note_730198505).
|
||||
We will officially support all distributions that are binary compatible with Red Hat Enterprise Linux.
|
||||
This gives users a path forward for their CentOS 8 builds at its end of life.
|
||||
|
||||
## Update GitLab package sources after upgrading the OS
|
||||
|
||||
After upgrading the Operating System (OS) as per its own documentation,
|
||||
it may be necessary to also update the GitLab package source URL
|
||||
in your package manager configuration.
|
||||
If your package manager reports that no further updates are available,
|
||||
although [new versions have been released](https://about.gitlab.com/releases/categories/releases/), repeat the
|
||||
"Add the GitLab package repository" instructions
|
||||
of the [Linux package install guide](https://about.gitlab.com/install/#content).
|
||||
Future GitLab upgrades will now be fetched according to your upgraded OS.
|
||||
|
||||
## Packages for ARM64
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/issues/27) in GitLab 13.4.
|
||||
|
||||
GitLab provides arm64/aarch64 packages for some supported operating systems.
|
||||
You can see if your operating system architecture is supported in the table
|
||||
above.
|
||||
|
||||
WARNING:
|
||||
There are currently still some [known issues and limitation](https://gitlab.com/groups/gitlab-org/-/epics/4397)
|
||||
running GitLab on ARM.
|
||||
|
||||
## OS Versions that are no longer supported **(FREE SELF)**
|
||||
|
||||
GitLab provides omnibus packages for operating systems only until their
|
||||
EOL (End-Of-Life). After the EOL date of the OS, GitLab will stop releasing
|
||||
official packages. The list of deprecated operating systems and the final GitLab
|
||||
release for them can be found below:
|
||||
|
||||
| OS Version | End Of Life | Last supported GitLab version |
|
||||
| --------------- | ---------------------------------------------------------------------------------- | -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Raspbian Wheezy | [May 2015](https://downloads.raspberrypi.org/raspbian/images/raspbian-2015-05-07/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_8.17&dist=debian%2Fwheezy) 8.17 |
|
||||
| OpenSUSE 13.2 | [January 2017](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-9.1&dist=opensuse%2F13.2) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-9.1&dist=opensuse%2F13.2) 9.1 |
|
||||
| Ubuntu 12.04 | [April 2017](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_9.1&dist=ubuntu%2Fprecise) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_9.1&dist=ubuntu%2Fprecise) 9.1 |
|
||||
| OpenSUSE 42.1 | [May 2017](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-9.3&dist=opensuse%2F42.1) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-9.3&dist=opensuse%2F42.1) 9.3 |
|
||||
| OpenSUSE 42.2 | [January 2018](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-10.4&dist=opensuse%2F42.2) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-10.4&dist=opensuse%2F42.2) 10.4 |
|
||||
| Debian Wheezy | [May 2018](https://www.debian.org/News/2018/20180601) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_11.6&dist=debian%2Fwheezy) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_11.6&dist=debian%2Fwheezy) 11.6 |
|
||||
| Raspbian Jessie | [May 2017](https://downloads.raspberrypi.org/raspbian/images/raspbian-2017-07-05/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_11.7&dist=debian%2Fjessie) 11.7 |
|
||||
| Ubuntu 14.04 | [April 2019](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_11.10&dist=ubuntu%2Ftrusty) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_11.10&dist=ubuntu%2Ftrusty) 11.10 |
|
||||
| OpenSUSE 42.3 | [July 2019](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-12.1&dist=opensuse%2F42.3) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-12.1&dist=opensuse%2F42.3) 12.1 |
|
||||
| OpenSUSE 15.0 | [December 2019](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-12.5&dist=opensuse%2F15.0) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-12.5&dist=opensuse%2F15.0) 12.5 |
|
||||
| Raspbian Stretch | [June 2020](https://downloads.raspberrypi.org/raspbian/images/raspbian-2019-04-09/) | [GitLab CE](https://packages.gitlab.com/app/gitlab/raspberry-pi2/search?q=gitlab-ce_13.2&dist=raspbian%2Fstretch) 13.3 |
|
||||
| Debian Jessie | [June 2020](https://www.debian.org/News/2020/20200709) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_13.2&dist=debian%2Fjessie) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_13.2&dist=debian%2Fjessie) 13.3 |
|
||||
| CentOS 6 | [November 2020](https://wiki.centos.org/About/Product) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=13.6&filter=all&filter=all&dist=el%2F6) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=13.6&filter=all&filter=all&dist=el%2F6) 13.6 |
|
||||
| OpenSUSE 15.1 | [November 2020](https://en.opensuse.org/Lifetime#Discontinued_distributions) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce-13.12&dist=opensuse%2F15.1) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee-13.12&dist=opensuse%2F15.2) 13.12 |
|
||||
| Ubuntu 16.04 | [April 2021](https://ubuntu.com/info/release-end-of-life) | [GitLab CE](https://packages.gitlab.com/app/gitlab/gitlab-ce/search?q=gitlab-ce_13.12&dist=ubuntu%2Fxenial) / [GitLab EE](https://packages.gitlab.com/app/gitlab/gitlab-ee/search?q=gitlab-ee_13.12&dist=ubuntu%2Fxenial) 13.12 |
|
||||
|
||||
NOTE:
|
||||
An exception to this deprecation policy is when we are unable to provide
|
||||
packages for the next version of the operating system. The most common reason
|
||||
for this our package repository provider, PackageCloud, not supporting newer
|
||||
versions and hence we can't upload packages to it.
|
|
@ -1281,7 +1281,7 @@ in all of your GitLab Pages instances.
|
|||
|
||||
### 500 error with `securecookie: failed to generate random iv` and `Failed to save the session`
|
||||
|
||||
This problem most likely results from an [out-dated operating system](../package_information/deprecated_os.md).
|
||||
This problem most likely results from an [out-dated operating system](../package_information/supported_os.md#os-versions-that-are-no-longer-supported).
|
||||
The [Pages daemon uses the `securecookie` library](https://gitlab.com/search?group_id=9970&project_id=734943&repository_ref=master&scope=blobs&search=securecookie&snippets=false) to get random strings via [`crypto/rand` in Go](https://golang.org/pkg/crypto/rand/#pkg-variables).
|
||||
This requires the `getrandom` system call or `/dev/urandom` to be available on the host OS.
|
||||
Upgrading to an [officially supported operating system](https://about.gitlab.com/install/) is recommended.
|
||||
|
|
|
@ -618,7 +618,7 @@ users.count
|
|||
# If that count looks sane:
|
||||
|
||||
# You can either block the users:
|
||||
users.each { |user| user.block! }
|
||||
users.each { |user| user.blocked? ? nil : user.block! }
|
||||
|
||||
# Or you can delete them:
|
||||
# need 'current user' (your user) for auditing purposes
|
||||
|
|
|
@ -35,7 +35,7 @@ For the installation options, see [the main installation page](index.md).
|
|||
Installation of GitLab on these operating systems is possible, but not supported.
|
||||
Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information.
|
||||
|
||||
Please see [OS versions that are no longer supported](../administration/package_information/deprecated_os.md) for Omnibus installs page
|
||||
Please see [OS versions that are no longer supported](../administration/package_information/supported_os.md#os-versions-that-are-no-longer-supported) for Omnibus installs page
|
||||
for a list of supported and unsupported OS versions as well as the last support GitLab version for that OS.
|
||||
|
||||
### Microsoft Windows
|
||||
|
|
|
@ -20,15 +20,15 @@ NOTE:
|
|||
GitLab submits all issues to Akismet.
|
||||
|
||||
Akismet configuration is available to users on self-managed GitLab. Akismet is already enabled on
|
||||
GitLab SaaS (GitLab.com), where it's configuration and management are handled by GitLab Inc.
|
||||
GitLab SaaS (GitLab.com), where its configuration and management are handled by GitLab Inc.
|
||||
|
||||
## Configuration **(FREE SELF)**
|
||||
## Configure Akismet **(FREE SELF)**
|
||||
|
||||
To use Akismet:
|
||||
|
||||
1. Go to the [Akismet sign-in page](https://akismet.com/account/).
|
||||
1. Sign in or create a new account.
|
||||
1. Click **Show** to reveal the API key, and copy the API key's value.
|
||||
1. Select **Show** to reveal the API key, and copy the API key's value.
|
||||
1. Sign in to GitLab as an administrator.
|
||||
1. On the top bar, select **Menu > Admin**.
|
||||
1. On the left sidebar, select **Settings > Reporting** (`/admin/application_settings/reporting`).
|
||||
|
@ -38,7 +38,7 @@ To use Akismet:
|
|||
|
||||
![Screenshot of Akismet settings](img/akismet_settings.png)
|
||||
|
||||
## Training **(FREE SELF)**
|
||||
## Train the Akismet filter **(FREE SELF)**
|
||||
|
||||
To better differentiate between spam and ham, you can train the Akismet
|
||||
filter whenever there is a false positive or false negative.
|
||||
|
|
|
@ -317,39 +317,65 @@ main quota. You can find pricing for additional CI/CD minutes on the
|
|||
- Are only used after the shared quota included in your subscription runs out.
|
||||
- Roll over month to month.
|
||||
|
||||
To purchase additional minutes for your group on GitLab SaaS:
|
||||
|
||||
1. From your group, go to **Settings > Usage Quotas**.
|
||||
1. Select **Buy additional minutes** and GitLab directs you to the Customers Portal.
|
||||
1. Locate the subscription card that's linked to your group on GitLab SaaS, click **Buy more CI minutes**, and complete the details about the transaction.
|
||||
1. Once we have processed your payment, the extra CI minutes are synced to your group namespace.
|
||||
1. To confirm the available CI minutes, go to your group, then **Settings > Usage Quotas**.
|
||||
|
||||
The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month.
|
||||
|
||||
To purchase additional minutes for your personal namespace:
|
||||
|
||||
1. In the top-right corner, select your avatar.
|
||||
1. Select **Edit profile**.
|
||||
1. On the left sidebar, select **Usage Quotas**.
|
||||
1. Select **Buy additional minutes** and GitLab redirects you to the Customers Portal.
|
||||
1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes are synced to your personal namespace.
|
||||
1. To confirm the available CI minutes for your personal projects, go to the **Usage Quotas** settings again.
|
||||
1. Locate the subscription card that's linked to your personal namespace on GitLab SaaS, click **Buy more CI minutes**, and complete the details about the transaction.
|
||||
|
||||
The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month.
|
||||
After we process your payment, the extra CI minutes are synced to your group
|
||||
namespace.
|
||||
|
||||
To confirm the available CI minutes for your personal projects, go to the **Usage Quotas** settings again.
|
||||
|
||||
The **Additional minutes** displayed now includes the purchased additional CI
|
||||
minutes, plus any minutes rolled over from last month.
|
||||
|
||||
Be aware that:
|
||||
|
||||
- If you have purchased extra CI minutes before the purchase of a paid plan,
|
||||
we calculate a pro-rated charge for your paid plan. That means you may
|
||||
be charged for less than one year because your subscription was previously
|
||||
created with the extra CI minutes.
|
||||
- After the extra CI minutes have been assigned to a Group, they can't be transferred
|
||||
to a different Group by themselves, but they will transfer along with a subscription when
|
||||
changing the linked namespace for the subscription.
|
||||
- If you have used more minutes than your default quota, these minutes will
|
||||
be deducted from your Additional Minutes quota immediately after your purchase of additional
|
||||
minutes.
|
||||
- Extra CI minutes assigned to one group cannot be transferred to a different
|
||||
group.
|
||||
- If you have used more minutes than your default quota, those minutes are
|
||||
deducted from your Additional Minutes quota immediately after your purchase of
|
||||
additional minutes.
|
||||
|
||||
### Purchase additional CI minutes on GitLab SaaS
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6574) in GitLab 14.5.
|
||||
|
||||
If you're using GitLab SaaS, you can purchase additional CI minutes so your
|
||||
pipelines aren't blocked after you have used all your CI minutes from your
|
||||
main quota. You can find pricing for additional CI/CD minutes on the
|
||||
[GitLab Pricing page](https://about.gitlab.com/pricing/). Additional minutes:
|
||||
|
||||
- Are only used after the shared quota included in your subscription runs out.
|
||||
- Roll over month to month.
|
||||
|
||||
To purchase additional minutes for your group on GitLab SaaS:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Settings > Usage Quotas**.
|
||||
1. Select **Buy additional minutes**.
|
||||
1. Complete the details about the transaction.
|
||||
|
||||
After we process your payment, the extra CI minutes are synced to your group
|
||||
namespace.
|
||||
|
||||
To confirm the available CI minutes, go to your group, and then select
|
||||
**Settings > Usage Quotas**.
|
||||
|
||||
The **Additional minutes** displayed now includes the purchased additional CI
|
||||
minutes, plus any minutes rolled over from last month.
|
||||
|
||||
Be aware that:
|
||||
|
||||
- Extra CI minutes assigned to one group cannot be transferred to a different
|
||||
group.
|
||||
- If you have used more minutes than your default quota, those minutes are
|
||||
deducted from your Additional Minutes quota immediately after your purchase of
|
||||
additional minutes.
|
||||
|
||||
## Storage subscription
|
||||
|
||||
|
|
|
@ -82,13 +82,18 @@ See the guide to [plan your GitLab upgrade](plan_your_upgrade.md).
|
|||
## Checking for background migrations before upgrading
|
||||
|
||||
Certain releases may require different migrations to be
|
||||
finished before you update to the newer version. Additionally check
|
||||
[batched migrations](#batched-background-migrations) from GitLab 14.0.
|
||||
finished before you update to the newer version.
|
||||
|
||||
[Batched migrations](#batched-background-migrations) are a migration type available in GitLab 14.0 and later.
|
||||
Background migrations and batched migrations not the same, so you should check that both are
|
||||
complete before updating.
|
||||
|
||||
Decrease the time required to complete these migrations by increasing the number of
|
||||
[Sidekiq workers](../administration/operations/extra_sidekiq_processes.md)
|
||||
that can process jobs in the `background_migration` queue.
|
||||
|
||||
### Background migrations
|
||||
|
||||
**For Omnibus installations:**
|
||||
|
||||
```shell
|
||||
|
|
|
@ -188,7 +188,7 @@ For the GitLab Community Edition, replace `gitlab-ee` with
|
|||
|
||||
### GitLab 13.7 and later unavailable on Amazon Linux 2
|
||||
|
||||
Amazon Linux 2 is not an [officially supported operating system](../../administration/package_information/deprecated_os.md#supported-operating-systems).
|
||||
Amazon Linux 2 is not an [officially supported operating system](../../administration/package_information/supported_os.md).
|
||||
However, in past the [official package installation script](https://packages.gitlab.com/gitlab/gitlab-ee/install)
|
||||
installed the `el/6` package repository if run on Amazon Linux. From GitLab 13.7, we no longer
|
||||
provide `el/6` packages so administrators must run the [installation script](https://packages.gitlab.com/gitlab/gitlab-ee/install)
|
||||
|
|
|
@ -18,7 +18,7 @@ General notes:
|
|||
to create your plan, share details of your architecture, including:
|
||||
- How is GitLab installed?
|
||||
- What is the operating system of the node?
|
||||
(check [OS versions that are no longer supported](../administration/package_information/deprecated_os.md) to confirm that later updates are available).
|
||||
(check [OS versions that are no longer supported](../administration/package_information/supported_os.md#os-versions-that-are-no-longer-supported) to confirm that later updates are available).
|
||||
- Is it a single-node or a multi-node setup? If multi-node, share any architectural details about each node with us.
|
||||
- Are you using [GitLab Geo](../administration/geo/index.md)? If so, share any architectural details about each secondary node.
|
||||
- What else might be unique or interesting in your setup that might be important for us to understand?
|
||||
|
@ -112,7 +112,7 @@ to your instance and then upgrade it for any relevant features you're using.
|
|||
- [Determine what upgrade path](index.md#upgrade-paths) to follow.
|
||||
- Account for any [version-specific update instructions](index.md#version-specific-upgrading-instructions).
|
||||
- Account for any [version-specific changes](package/index.md#version-specific-changes).
|
||||
- Check the [OS compatibility with the target GitLab version](../administration/package_information/deprecated_os.md).
|
||||
- Check the [OS compatibility with the target GitLab version](../administration/package_information/supported_os.md).
|
||||
- Due to background migrations, plan to pause any further upgrades after upgrading
|
||||
to a new major version.
|
||||
[All migrations must finish running](index.md#checking-for-background-migrations-before-upgrading)
|
||||
|
|
|
@ -29,15 +29,15 @@ Prerequisites:
|
|||
- You need to [authenticate with the API](../../../api/index.md#authentication). If authenticating with a deploy token, it must be configured with the `write_package_registry` scope.
|
||||
|
||||
```plaintext
|
||||
PUT /projects/:id/packages/terraform/modules/:module_name/:module_system/:module_version/file
|
||||
PUT /projects/:id/packages/terraform/modules/:module-name/:module-system/:module-version/file
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| -------------------| --------------- | ---------| -------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../../../api/index.md#namespaced-path-encoding). |
|
||||
| `module_name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), or hyphens (`-`) and cannot exceed 64 characters.
|
||||
| `module_system` | string | yes | The package system. It can contain only lowercase letters (`a-z`) and numbers (`0-9`), and cannot exceed 64 characters.
|
||||
| `module_version` | string | yes | The package version. It must be valid according to the [Semantic Versioning Specification](https://semver.org/).
|
||||
| `module-name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), or hyphens (`-`) and cannot exceed 64 characters.
|
||||
| `module-system` | string | yes | The package system. It can contain only lowercase letters (`a-z`) and numbers (`0-9`), and cannot exceed 64 characters.
|
||||
| `module-version` | string | yes | The package version. It must be valid according to the [Semantic Versioning Specification](https://semver.org/).
|
||||
|
||||
Provide the file content in the request body.
|
||||
|
||||
|
@ -97,7 +97,7 @@ You can then reference your Terraform Module from a downstream Terraform project
|
|||
|
||||
```plaintext
|
||||
module "<module>" {
|
||||
source = "gitlab.com/<namespace>/<module_name>/<module_system>"
|
||||
source = "gitlab.com/<namespace>/<module-name>/<module-system>"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ a self-managed instance from an old server to a new server.
|
|||
|
||||
The backups produced don't depend on the operating system running GitLab. You can therefore use
|
||||
the restore method to switch between different operating system distributions or versions, as long
|
||||
as the same GitLab version [is available for installation](../../../administration/package_information/deprecated_os.md).
|
||||
as the same GitLab version [is available for installation](../../../administration/package_information/supported_os.md).
|
||||
|
||||
To instead merge two self-managed GitLab instances together, use the instructions in
|
||||
[Migrate from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
|
||||
|
|
|
@ -20583,6 +20583,9 @@ msgstr ""
|
|||
msgid "LearnGitLab|Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:"
|
||||
msgstr ""
|
||||
|
||||
msgid "LearnGitLab|Your team is growing! You've successfully invited new team members to the %{projectName} project."
|
||||
msgstr ""
|
||||
|
||||
msgid "LearnGitlab|Creating your onboarding experience..."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -55,9 +55,9 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "1.220.0",
|
||||
"@gitlab/svgs": "1.221.0",
|
||||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "32.36.0",
|
||||
"@gitlab/ui": "32.38.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.4-1",
|
||||
"@rails/ujs": "6.1.4-1",
|
||||
|
|
|
@ -199,7 +199,7 @@ module QA
|
|||
Page::Project::Artifact::Show.perform do |artifacts|
|
||||
artifacts.go_to_directory('node_modules')
|
||||
artifacts.go_to_directory("@#{registry_scope}")
|
||||
expect(artifacts).to have_content( "#{project.name}")
|
||||
expect(artifacts).to have_content("#{project.name}")
|
||||
end
|
||||
|
||||
project.visit!
|
||||
|
|
|
@ -91,7 +91,7 @@ module QA
|
|||
file_path: 'package.json',
|
||||
content: <<~JSON
|
||||
{
|
||||
"name": "@#{registry_scope}/mypackage",
|
||||
"name": "#{package.name}",
|
||||
"version": "1.0.0",
|
||||
"description": "Example package for GitLab npm registry",
|
||||
"publishConfig": {
|
||||
|
@ -133,7 +133,7 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
it "push and pull a npm package via CI using a #{params[:token_name]}", quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/344537', type: :investigating } do
|
||||
it "push and pull a npm package via CI using a #{params[:token_name]}" do
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
commit.project = project
|
||||
commit.commit_message = 'Add .gitlab-ci.yml'
|
||||
|
@ -168,7 +168,7 @@ module QA
|
|||
Page::Project::Artifact::Show.perform do |artifacts|
|
||||
artifacts.go_to_directory('node_modules')
|
||||
artifacts.go_to_directory("@#{registry_scope}")
|
||||
expect(artifacts).to have_content("mypackage")
|
||||
expect(artifacts).to have_content('mypackage')
|
||||
end
|
||||
|
||||
project.visit!
|
||||
|
|
|
@ -5,14 +5,15 @@ require 'spec_helper'
|
|||
RSpec.describe Projects::LearnGitlabController do
|
||||
describe 'GET #index' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:project) { create(:project, namespace: user.namespace) }
|
||||
let_it_be(:project) { create(:project, namespace: create(:group)) }
|
||||
|
||||
let(:learn_gitlab_enabled) { true }
|
||||
let(:params) { { namespace_id: project.namespace.to_param, project_id: project } }
|
||||
|
||||
subject { get :index, params: params }
|
||||
subject(:action) { get :index, params: params }
|
||||
|
||||
before do
|
||||
project.namespace.add_owner(user)
|
||||
allow(controller.helpers).to receive(:learn_gitlab_enabled?).and_return(learn_gitlab_enabled)
|
||||
end
|
||||
|
||||
|
@ -32,6 +33,10 @@ RSpec.describe Projects::LearnGitlabController do
|
|||
|
||||
it { is_expected.to have_gitlab_http_status(:not_found) }
|
||||
end
|
||||
|
||||
it_behaves_like 'tracks assignment and records the subject', :invite_for_help_continuous_onboarding, :namespace do
|
||||
subject { project.namespace }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,12 +99,13 @@ describe('experiment Utilities', () => {
|
|||
|
||||
describe('isExperimentVariant', () => {
|
||||
describe.each`
|
||||
experiment | variant | input | output
|
||||
${ABC_KEY} | ${DEFAULT_VARIANT} | ${[ABC_KEY, DEFAULT_VARIANT]} | ${true}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_variant_name']} | ${true}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_bogus_name']} | ${false}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${['boguskey', '_variant_name']} | ${false}
|
||||
${undefined} | ${undefined} | ${[ABC_KEY, '_variant_name']} | ${false}
|
||||
experiment | variant | input | output
|
||||
${ABC_KEY} | ${CANDIDATE_VARIANT} | ${[ABC_KEY]} | ${true}
|
||||
${ABC_KEY} | ${DEFAULT_VARIANT} | ${[ABC_KEY, DEFAULT_VARIANT]} | ${true}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_variant_name']} | ${true}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${[ABC_KEY, '_bogus_name']} | ${false}
|
||||
${ABC_KEY} | ${'_variant_name'} | ${['boguskey', '_variant_name']} | ${false}
|
||||
${undefined} | ${undefined} | ${[ABC_KEY, '_variant_name']} | ${false}
|
||||
`(
|
||||
'with input=$input, experiment=$experiment, variant=$variant',
|
||||
({ experiment, variant, input, output }) => {
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
MEMBERS_MODAL_DEFAULT_TITLE,
|
||||
MEMBERS_PLACEHOLDER,
|
||||
MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT,
|
||||
LEARN_GITLAB,
|
||||
} from '~/invite_members/constants';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
@ -268,6 +269,14 @@ describe('InviteMembersModal', () => {
|
|||
|
||||
expect(findTasksToBeDone().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when opened from the Learn GitLab page', () => {
|
||||
it('does render the tasks to be done', () => {
|
||||
setupComponent({ source: LEARN_GITLAB }, {}, []);
|
||||
|
||||
expect(findTasksToBeDone().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rendering the tasks', () => {
|
||||
|
@ -465,7 +474,6 @@ describe('InviteMembersModal', () => {
|
|||
|
||||
wrapper.vm.$toast = { show: jest.fn() };
|
||||
jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
|
||||
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
|
||||
});
|
||||
|
||||
it('includes the non-default selected areas of focus', () => {
|
||||
|
@ -492,7 +500,23 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the successful toastMessage', () => {
|
||||
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added', {
|
||||
onComplete: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when opened from a Learn GitLab page', () => {
|
||||
it('emits the `showSuccessfulInvitationsAlert` event', async () => {
|
||||
eventHub.$emit('openModal', { inviteeType: 'members', source: LEARN_GITLAB });
|
||||
|
||||
jest.spyOn(eventHub, '$emit').mockImplementation();
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('showSuccessfulInvitationsAlert');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -649,7 +673,6 @@ describe('InviteMembersModal', () => {
|
|||
|
||||
wrapper.vm.$toast = { show: jest.fn() };
|
||||
jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
|
||||
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
|
||||
});
|
||||
|
||||
it('includes the non-default selected areas of focus', () => {
|
||||
|
@ -672,7 +695,9 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the successful toastMessage', () => {
|
||||
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added', {
|
||||
onComplete: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -711,13 +736,14 @@ describe('InviteMembersModal', () => {
|
|||
it('displays the successful toast message when email has already been invited', async () => {
|
||||
mockInvitationsApi(httpStatus.CREATED, invitationsApiResponse.EMAIL_TAKEN);
|
||||
wrapper.vm.$toast = { show: jest.fn() };
|
||||
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
|
||||
|
||||
clickInviteButton();
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added', {
|
||||
onComplete: expect.any(Function),
|
||||
});
|
||||
expect(findMembersSelect().props('validationState')).toBe(null);
|
||||
});
|
||||
|
||||
|
@ -782,7 +808,6 @@ describe('InviteMembersModal', () => {
|
|||
wrapper.vm.$toast = { show: jest.fn() };
|
||||
jest.spyOn(Api, 'inviteGroupMembersByEmail').mockResolvedValue({ data: postData });
|
||||
jest.spyOn(Api, 'addGroupMembersByUserId').mockResolvedValue({ data: postData });
|
||||
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
|
||||
jest.spyOn(wrapper.vm, 'trackInvite');
|
||||
});
|
||||
|
||||
|
@ -800,7 +825,9 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the successful toastMessage', () => {
|
||||
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added', {
|
||||
onComplete: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -855,7 +882,6 @@ describe('InviteMembersModal', () => {
|
|||
wrapper.setData({ inviteeType: 'group' });
|
||||
wrapper.vm.$toast = { show: jest.fn() };
|
||||
jest.spyOn(Api, 'groupShareWithGroup').mockResolvedValue({ data: groupPostData });
|
||||
jest.spyOn(wrapper.vm, 'showToastMessageSuccess');
|
||||
|
||||
clickInviteButton();
|
||||
});
|
||||
|
@ -865,7 +891,9 @@ describe('InviteMembersModal', () => {
|
|||
});
|
||||
|
||||
it('displays the successful toastMessage', () => {
|
||||
expect(wrapper.vm.showToastMessageSuccess).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith('Members were successfully added', {
|
||||
onComplete: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -930,6 +958,13 @@ describe('InviteMembersModal', () => {
|
|||
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(MEMBER_AREAS_OF_FOCUS.view);
|
||||
});
|
||||
|
||||
it('tracks the view for learn_gitlab source', () => {
|
||||
eventHub.$emit('openModal', { inviteeType: 'members', source: LEARN_GITLAB });
|
||||
|
||||
expect(ExperimentTracking).toHaveBeenCalledWith(INVITE_MEMBERS_FOR_TASK.name);
|
||||
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(LEARN_GITLAB);
|
||||
});
|
||||
|
||||
it('tracks the invite for areas_of_focus', () => {
|
||||
eventHub.$emit('openModal', { inviteeType: 'members' });
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
exports[`Learn GitLab renders correctly 1`] = `
|
||||
<div>
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="row"
|
||||
>
|
||||
|
@ -131,66 +133,60 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Set up CI/CD"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Set up CI/CD
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Set up CI/CD"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Set up CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Start a free Ultimate trial"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Start a free Ultimate trial
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Start a free Ultimate trial"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Start a free Ultimate trial
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Add code owners"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Add code owners
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Add code owners"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Add code owners
|
||||
|
||||
</a>
|
||||
|
||||
<span
|
||||
class="gl-font-style-italic gl-text-gray-500"
|
||||
|
@ -204,22 +200,20 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Add merge request approval"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Add merge request approval
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Add merge request approval"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Add merge request approval
|
||||
|
||||
</a>
|
||||
|
||||
<span
|
||||
class="gl-font-style-italic gl-text-gray-500"
|
||||
|
@ -269,44 +263,40 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Create an issue"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Create an issue
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Create an issue"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Create an issue
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Submit a merge request"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Submit a merge request
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Submit a merge request"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Submit a merge request
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
|
@ -349,22 +339,20 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Run a Security scan using CI/CD"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Run a Security scan using CI/CD
|
||||
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-track-action="click_link"
|
||||
data-track-experiment="change_continuous_onboarding_link_urls"
|
||||
data-track-label="Run a Security scan using CI/CD"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
href="http://example.com/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Run a Security scan using CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { stubExperiments } from 'helpers/experimentation_helper';
|
||||
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import LearnGitlabSectionLink from '~/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue';
|
||||
|
||||
const defaultAction = 'gitWrite';
|
||||
|
@ -23,6 +26,9 @@ describe('Learn GitLab Section Link', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const openInviteMembesrModalLink = () =>
|
||||
wrapper.find('[data-testid="invite-for-help-continuous-onboarding-experiment-link"]');
|
||||
|
||||
it('renders no icon when not completed', () => {
|
||||
createWrapper(undefined, { completed: false });
|
||||
|
||||
|
@ -46,4 +52,54 @@ describe('Learn GitLab Section Link', () => {
|
|||
|
||||
expect(wrapper.find('[data-testid="trial-only"]').exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('rendering a link to open the invite_members modal instead of a regular link', () => {
|
||||
it.each`
|
||||
action | experimentVariant | showModal
|
||||
${'userAdded'} | ${'candidate'} | ${true}
|
||||
${'userAdded'} | ${'control'} | ${false}
|
||||
${defaultAction} | ${'candidate'} | ${false}
|
||||
${defaultAction} | ${'control'} | ${false}
|
||||
`(
|
||||
'when the invite_for_help_continuous_onboarding experiment has variant: $experimentVariant and action is $action, the modal link is shown: $showModal',
|
||||
({ action, experimentVariant, showModal }) => {
|
||||
stubExperiments({ invite_for_help_continuous_onboarding: experimentVariant });
|
||||
createWrapper(action);
|
||||
|
||||
expect(openInviteMembesrModalLink().exists()).toBe(showModal);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('clicking the link to open the invite_members modal', () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(eventHub, '$emit').mockImplementation();
|
||||
|
||||
stubExperiments({ invite_for_help_continuous_onboarding: 'candidate' });
|
||||
createWrapper('userAdded');
|
||||
});
|
||||
|
||||
it('calls the eventHub', () => {
|
||||
openInviteMembesrModalLink().vm.$emit('click');
|
||||
|
||||
expect(eventHub.$emit).toHaveBeenCalledWith('openModal', {
|
||||
inviteeType: 'members',
|
||||
source: 'learn_gitlab',
|
||||
tasksToBeDoneEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('tracks the click', async () => {
|
||||
const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
|
||||
triggerEvent(openInviteMembesrModalLink().element);
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_link', {
|
||||
label: 'Invite your colleagues',
|
||||
property: 'Growth::Activation::Experiment::InviteForHelpContinuousOnboarding',
|
||||
});
|
||||
|
||||
unmockTracking();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,20 +1,35 @@
|
|||
import { GlProgressBar } from '@gitlab/ui';
|
||||
import { GlProgressBar, GlAlert } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import LearnGitlab from '~/pages/projects/learn_gitlab/components/learn_gitlab.vue';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import { testActions, testSections } from './mock_data';
|
||||
import { testActions, testSections, testProject } from './mock_data';
|
||||
|
||||
describe('Learn GitLab', () => {
|
||||
let wrapper;
|
||||
let sidebar;
|
||||
let inviteMembersOpen = false;
|
||||
|
||||
const createWrapper = () => {
|
||||
wrapper = mount(LearnGitlab, {
|
||||
propsData: { actions: testActions, sections: testSections, inviteMembersOpen },
|
||||
propsData: {
|
||||
actions: testActions,
|
||||
sections: testSections,
|
||||
project: testProject,
|
||||
inviteMembersOpen,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
sidebar = document.createElement('div');
|
||||
sidebar.innerHTML = `
|
||||
<div class="sidebar-top-level-items">
|
||||
<div class="active">
|
||||
<div class="count"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(sidebar);
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
|
@ -22,6 +37,7 @@ describe('Learn GitLab', () => {
|
|||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
inviteMembersOpen = false;
|
||||
sidebar.remove();
|
||||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
|
@ -66,4 +82,26 @@ describe('Learn GitLab', () => {
|
|||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the showSuccessfulInvitationsAlert event is fired', () => {
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
beforeEach(() => {
|
||||
eventHub.$emit('showSuccessfulInvitationsAlert');
|
||||
});
|
||||
|
||||
it('displays the successful invitations alert', () => {
|
||||
expect(findAlert().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays a message with the project name', () => {
|
||||
expect(findAlert().text()).toBe(
|
||||
"Your team is growing! You've successfully invited new team members to the test-project project.",
|
||||
);
|
||||
});
|
||||
|
||||
it('modifies the sidebar percentage', () => {
|
||||
expect(sidebar.textContent.trim()).toBe('22%');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -57,3 +57,7 @@ export const testSections = {
|
|||
svg: 'plan.svg',
|
||||
},
|
||||
};
|
||||
|
||||
export const testProject = {
|
||||
name: 'test-project',
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ RSpec.describe InviteMembersHelper do
|
|||
include Devise::Test::ControllerHelpers
|
||||
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:group) { create(:group, projects: [project]) }
|
||||
let_it_be(:developer) { create(:user, developer_projects: [project]) }
|
||||
|
||||
let(:owner) { project.owner }
|
||||
|
@ -64,48 +65,13 @@ RSpec.describe InviteMembersHelper do
|
|||
end
|
||||
|
||||
context 'tasks_to_be_done' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
subject(:output) { helper.common_invite_modal_dataset(source) }
|
||||
|
||||
let_it_be(:source) { project }
|
||||
|
||||
before do
|
||||
stub_experiments(invite_members_for_task: true)
|
||||
end
|
||||
|
||||
context 'when not logged in' do
|
||||
before do
|
||||
allow(helper).to receive(:params).and_return({ open_modal: 'invite_members_for_task' })
|
||||
end
|
||||
|
||||
it "doesn't have the tasks to be done attributes" do
|
||||
expect(output[:tasks_to_be_done_options]).to be_nil
|
||||
expect(output[:projects]).to be_nil
|
||||
expect(output[:new_project_path]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when logged in but the open_modal param is not present' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(developer)
|
||||
end
|
||||
|
||||
it "doesn't have the tasks to be done attributes" do
|
||||
expect(output[:tasks_to_be_done_options]).to be_nil
|
||||
expect(output[:projects]).to be_nil
|
||||
expect(output[:new_project_path]).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when logged in and the open_modal param is present' do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(developer)
|
||||
allow(helper).to receive(:params).and_return({ open_modal: 'invite_members_for_task' })
|
||||
end
|
||||
|
||||
context 'for a group' do
|
||||
let_it_be(:source) { create(:group, projects: [project]) }
|
||||
|
||||
it 'has the expected attributes', :aggregate_failures do
|
||||
shared_examples_for 'including the tasks to be done attributes' do
|
||||
it 'includes the tasks to be done attributes when expected' do
|
||||
if expected?
|
||||
expect(output[:tasks_to_be_done_options]).to eq(
|
||||
[
|
||||
{ value: :code, text: 'Create/import code into a project (repository)' },
|
||||
|
@ -117,24 +83,75 @@ RSpec.describe InviteMembersHelper do
|
|||
[{ id: project.id, title: project.title }].to_json
|
||||
)
|
||||
expect(output[:new_project_path]).to eq(
|
||||
new_project_path(namespace_id: source.id)
|
||||
source.is_a?(Project) ? '' : new_project_path(namespace_id: group.id)
|
||||
)
|
||||
else
|
||||
expect(output[:tasks_to_be_done_options]).to be_nil
|
||||
expect(output[:projects]).to be_nil
|
||||
expect(output[:new_project_path]).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a project' do
|
||||
it 'has the expected attributes', :aggregate_failures do
|
||||
expect(output[:tasks_to_be_done_options]).to eq(
|
||||
[
|
||||
{ value: :code, text: 'Create/import code into a project (repository)' },
|
||||
{ value: :ci, text: 'Set up CI/CD pipelines to build, test, deploy, and monitor code' },
|
||||
{ value: :issues, text: 'Create/import issues (tickets) to collaborate on ideas and plan work' }
|
||||
].to_json
|
||||
)
|
||||
expect(output[:projects]).to eq(
|
||||
[{ id: project.id, title: project.title }].to_json
|
||||
)
|
||||
expect(output[:new_project_path]).to eq('')
|
||||
context 'the invite_members_for_task experiment' do
|
||||
where(:invite_members_for_task_enabled?, :open_modal_param_present?, :logged_in?, :expected?) do
|
||||
true | true | true | true
|
||||
true | true | false | false
|
||||
true | false | true | false
|
||||
true | false | false | false
|
||||
false | true | true | false
|
||||
false | true | false | false
|
||||
false | false | true | false
|
||||
false | false | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(developer) if logged_in?
|
||||
stub_experiments(invite_members_for_task: true) if invite_members_for_task_enabled?
|
||||
allow(helper).to receive(:params).and_return({ open_modal: 'invite_members_for_task' }) if open_modal_param_present?
|
||||
end
|
||||
|
||||
context 'when the source is a project' do
|
||||
let_it_be(:source) { project }
|
||||
|
||||
it_behaves_like 'including the tasks to be done attributes'
|
||||
end
|
||||
|
||||
context 'when the source is a group' do
|
||||
let_it_be(:source) { group }
|
||||
|
||||
it_behaves_like 'including the tasks to be done attributes'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'the invite_for_help_continuous_onboarding experiment' do
|
||||
where(:invite_for_help_continuous_onboarding?, :logged_in?, :expected?) do
|
||||
true | true | true
|
||||
true | false | false
|
||||
false | true | false
|
||||
false | false | false
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(developer) if logged_in?
|
||||
stub_experiments(invite_for_help_continuous_onboarding: :candidate) if invite_for_help_continuous_onboarding?
|
||||
end
|
||||
|
||||
context 'when the source is a project' do
|
||||
let_it_be(:source) { project }
|
||||
|
||||
it_behaves_like 'including the tasks to be done attributes'
|
||||
end
|
||||
|
||||
context 'when the source is a group' do
|
||||
let_it_be(:source) { group }
|
||||
|
||||
let(:expected?) { false }
|
||||
|
||||
it_behaves_like 'including the tasks to be done attributes'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -60,6 +60,7 @@ RSpec.describe LearnGitlabHelper do
|
|||
|
||||
let(:onboarding_actions_data) { Gitlab::Json.parse(learn_gitlab_data[:actions]).deep_symbolize_keys }
|
||||
let(:onboarding_sections_data) { Gitlab::Json.parse(learn_gitlab_data[:sections]).deep_symbolize_keys }
|
||||
let(:onboarding_project_data) { Gitlab::Json.parse(learn_gitlab_data[:project]).deep_symbolize_keys }
|
||||
|
||||
shared_examples 'has all data' do
|
||||
it 'has all actions' do
|
||||
|
@ -82,6 +83,11 @@ RSpec.describe LearnGitlabHelper do
|
|||
expect(onboarding_sections_data.keys).to contain_exactly(:deploy, :plan, :workspace)
|
||||
expect(onboarding_sections_data.values.map { |section| section.keys }).to match_array([[:svg]] * 3)
|
||||
end
|
||||
|
||||
it 'has all project data', :aggregate_failures do
|
||||
expect(onboarding_project_data.keys).to contain_exactly(:name)
|
||||
expect(onboarding_project_data.values).to match_array([project.name])
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'has all data'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GrapeLogging::Loggers::ExceptionLogger do
|
||||
let(:mock_request) { OpenStruct.new(env: {}) }
|
||||
let(:mock_request) { double('env', env: {}) }
|
||||
let(:response_body) { nil }
|
||||
|
||||
describe ".parameters" do
|
||||
|
@ -76,7 +76,7 @@ RSpec.describe Gitlab::GrapeLogging::Loggers::ExceptionLogger do
|
|||
describe 'when an exception is available' do
|
||||
let(:exception) { RuntimeError.new('This is a test') }
|
||||
let(:mock_request) do
|
||||
OpenStruct.new(
|
||||
double('env',
|
||||
env: {
|
||||
::API::Helpers::API_EXCEPTION_ENV => exception
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ RSpec.describe U2fRegistration do
|
|||
describe '#create_webauthn_registration' do
|
||||
shared_examples_for 'creates webauthn registration' do
|
||||
it 'creates webauthn registration' do
|
||||
u2f_registration.save!
|
||||
created_record = u2f_registration
|
||||
|
||||
webauthn_registration = WebauthnRegistration.where(u2f_registration_id: u2f_registration.id)
|
||||
webauthn_registration = WebauthnRegistration.where(u2f_registration_id: created_record.id)
|
||||
expect(webauthn_registration).to exist
|
||||
end
|
||||
end
|
||||
|
@ -43,13 +43,16 @@ RSpec.describe U2fRegistration do
|
|||
|
||||
it 'logs error' do
|
||||
allow(Gitlab::Auth::U2fWebauthnConverter).to receive(:new).and_raise('boom!')
|
||||
expect(Gitlab::AppJsonLogger).to(
|
||||
receive(:error).with(a_hash_including(event: 'u2f_migration',
|
||||
error: 'RuntimeError',
|
||||
message: 'U2F to WebAuthn conversion failed'))
|
||||
)
|
||||
|
||||
u2f_registration.save!
|
||||
allow_next_instance_of(U2fRegistration) do |u2f_registration|
|
||||
allow(u2f_registration).to receive(:id).and_return(123)
|
||||
end
|
||||
|
||||
expect(Gitlab::ErrorTracking).to(
|
||||
receive(:track_exception).with(kind_of(StandardError),
|
||||
u2f_registration_id: 123))
|
||||
|
||||
u2f_registration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3673,309 +3673,321 @@ RSpec.describe User do
|
|||
end
|
||||
|
||||
describe '#ci_owned_runners' do
|
||||
let(:user) { create(:user) }
|
||||
shared_examples 'ci_owned_runners examples' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
shared_examples :nested_groups_owner do
|
||||
context 'when the user is the owner of a multi-level group' do
|
||||
before do
|
||||
set_permissions_for_users
|
||||
end
|
||||
shared_examples :nested_groups_owner do
|
||||
context 'when the user is the owner of a multi-level group' do
|
||||
before do
|
||||
set_permissions_for_users
|
||||
end
|
||||
|
||||
it 'loads all the runners in the tree of groups' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
|
||||
end
|
||||
it 'loads all the runners in the tree of groups' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(runner, group_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(true)
|
||||
expect(user.owns_runner?(group_runner)).to eq(true)
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(true)
|
||||
expect(user.owns_runner?(group_runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :group_owner do
|
||||
context 'when the user is the owner of a one level group' do
|
||||
before do
|
||||
shared_examples :group_owner do
|
||||
context 'when the user is the owner of a one level group' do
|
||||
before do
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it 'loads the runners in the group' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(group_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(group_runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :project_owner do
|
||||
context 'when the user is the owner of a project' do
|
||||
it 'loads the runner belonging to the project' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :project_member do
|
||||
context 'when the user is a maintainer' do
|
||||
before do
|
||||
add_user(:maintainer)
|
||||
end
|
||||
|
||||
it 'loads the runners of the project' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(project_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a developer' do
|
||||
before do
|
||||
add_user(:developer)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a reporter' do
|
||||
before do
|
||||
add_user(:reporter)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a guest' do
|
||||
before do
|
||||
add_user(:guest)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :group_member do
|
||||
context 'when the user is a maintainer' do
|
||||
before do
|
||||
add_user(:maintainer)
|
||||
end
|
||||
|
||||
it 'does not load the runners of the group' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a developer' do
|
||||
before do
|
||||
add_user(:developer)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a reporter' do
|
||||
before do
|
||||
add_user(:reporter)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a guest' do
|
||||
before do
|
||||
add_user(:guest)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without any projects nor groups' do
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(create(:ci_runner))).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with runner in a personal project' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:project) { create(:project, namespace: namespace) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it_behaves_like :project_owner
|
||||
end
|
||||
|
||||
context 'with group runner in a non owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
def add_user(access)
|
||||
group.add_user(user, access)
|
||||
end
|
||||
|
||||
it_behaves_like :group_member
|
||||
end
|
||||
|
||||
context 'with group runner in an owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
it_behaves_like :group_owner
|
||||
end
|
||||
|
||||
context 'with group runner in an owned group and group runner in a different owner subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:another_user) { create(:user) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
subgroup.add_owner(another_user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an an owned group and a group runner in that same group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it 'loads the runners in the group' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(group_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(group_runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :project_owner do
|
||||
context 'when the user is the owner of a project' do
|
||||
it 'loads the runner belonging to the project' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :project_member do
|
||||
context 'when the user is a maintainer' do
|
||||
before do
|
||||
add_user(:maintainer)
|
||||
end
|
||||
|
||||
it 'loads the runners of the project' do
|
||||
expect(user.ci_owned_runners).to contain_exactly(project_runner)
|
||||
end
|
||||
|
||||
it 'returns true for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(true)
|
||||
end
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'when the user is a developer' do
|
||||
before do
|
||||
add_user(:developer)
|
||||
context 'with personal project runner in an owned group and a group runner in a subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an owned group in an owned namespace and a group runner in that group' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:project) { create(:project, namespace: namespace, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an owned namespace, an owned group, a subgroup and a group runner in that subgroup' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:project) { create(:project, namespace: namespace, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with a project runner that belong to projects that belong to a not owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def add_user(access)
|
||||
project.add_user(user, access)
|
||||
end
|
||||
|
||||
it_behaves_like :project_member
|
||||
end
|
||||
|
||||
context 'with project runners that belong to projects that do not belong to any group' do
|
||||
let!(:project) { create(:project) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a reporter' do
|
||||
before do
|
||||
add_user(:reporter)
|
||||
context 'with a group runner that belongs to a subgroup of a group owned by another user' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:another_user) { create(:user) }
|
||||
|
||||
def add_user(access)
|
||||
subgroup.add_user(user, access)
|
||||
group.add_user(another_user, :owner)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a guest' do
|
||||
before do
|
||||
add_user(:guest)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(project_runner)).to eq(false)
|
||||
end
|
||||
it_behaves_like :group_member
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples :group_member do
|
||||
context 'when the user is a maintainer' do
|
||||
before do
|
||||
add_user(:maintainer)
|
||||
end
|
||||
it_behaves_like 'ci_owned_runners examples'
|
||||
|
||||
it 'does not load the runners of the group' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
context 'when feature flag :linear_user_ci_owned_runners is disabled' do
|
||||
before do
|
||||
stub_feature_flags(linear_user_ci_owned_runners: false)
|
||||
end
|
||||
|
||||
context 'when the user is a developer' do
|
||||
before do
|
||||
add_user(:developer)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a reporter' do
|
||||
before do
|
||||
add_user(:reporter)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the user is a guest' do
|
||||
before do
|
||||
add_user(:guest)
|
||||
end
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(runner)).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without any projects nor groups' do
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
|
||||
it 'returns false for owns_runner?' do
|
||||
expect(user.owns_runner?(create(:ci_runner))).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with runner in a personal project' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:project) { create(:project, namespace: namespace) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it_behaves_like :project_owner
|
||||
end
|
||||
|
||||
context 'with group runner in a non owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
def add_user(access)
|
||||
group.add_user(user, access)
|
||||
end
|
||||
|
||||
it_behaves_like :group_member
|
||||
end
|
||||
|
||||
context 'with group runner in an owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
|
||||
it_behaves_like :group_owner
|
||||
end
|
||||
|
||||
context 'with group runner in an owned group and group runner in a different owner subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:another_user) { create(:user) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
subgroup.add_owner(another_user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an an owned group and a group runner in that same group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an owned group and a group runner in a subgroup' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an owned group in an owned namespace and a group runner in that group' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:group) { create(:group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
|
||||
let!(:project) { create(:project, namespace: namespace, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with personal project runner in an owned namespace, an owned group, a subgroup and a group runner in that subgroup' do
|
||||
let!(:namespace) { create(:user_namespace, owner: user) }
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:group_runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:project) { create(:project, namespace: namespace, group: group) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def set_permissions_for_users
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like :nested_groups_owner
|
||||
end
|
||||
|
||||
context 'with a project runner that belong to projects that belong to a not owned group' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:project) { create(:project, group: group) }
|
||||
let!(:project_runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
def add_user(access)
|
||||
project.add_user(user, access)
|
||||
end
|
||||
|
||||
it_behaves_like :project_member
|
||||
end
|
||||
|
||||
context 'with project runners that belong to projects that do not belong to any group' do
|
||||
let!(:project) { create(:project) }
|
||||
let!(:runner) { create(:ci_runner, :project, projects: [project]) }
|
||||
|
||||
it 'does not load any runner' do
|
||||
expect(user.ci_owned_runners).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a group runner that belongs to a subgroup of a group owned by another user' do
|
||||
let!(:group) { create(:group) }
|
||||
let!(:subgroup) { create(:group, parent: group) }
|
||||
let!(:runner) { create(:ci_runner, :group, groups: [subgroup]) }
|
||||
let!(:another_user) { create(:user) }
|
||||
|
||||
def add_user(access)
|
||||
subgroup.add_user(user, access)
|
||||
group.add_user(another_user, :owner)
|
||||
end
|
||||
|
||||
it_behaves_like :group_member
|
||||
it_behaves_like 'ci_owned_runners examples'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ RSpec.describe API::ImportGithub do
|
|||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:provider_username) { user.username }
|
||||
let(:provider_user) { OpenStruct.new(login: provider_username) }
|
||||
let(:provider_user) { double('provider', login: provider_username) }
|
||||
let(:provider_repo) do
|
||||
OpenStruct.new(
|
||||
double('provider',
|
||||
name: 'vim',
|
||||
full_name: "#{provider_username}/vim",
|
||||
owner: OpenStruct.new(login: provider_username)
|
||||
owner: double('provider', login: provider_username)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -24,6 +24,16 @@ RSpec.describe Groups::CreateService, '#execute' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when `setup_for_company:true` is passed' do
|
||||
let(:params) { group_params.merge(setup_for_company: true) }
|
||||
let(:service) { described_class.new(user, params) }
|
||||
let(:created_group) { service.execute }
|
||||
|
||||
it 'creates group with the specified setup_for_company' do
|
||||
expect(created_group.setup_for_company).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'creating a group with `default_branch_protection` attribute' do
|
||||
let(:params) { group_params.merge(default_branch_protection: Gitlab::Access::PROTECTION_NONE) }
|
||||
let(:service) { described_class.new(user, params) }
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -914,20 +914,20 @@
|
|||
stylelint-declaration-strict-value "1.7.7"
|
||||
stylelint-scss "3.18.0"
|
||||
|
||||
"@gitlab/svgs@1.220.0":
|
||||
version "1.220.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.220.0.tgz#188bdefe86cdbf8be1faa7a92dbac31c728066c7"
|
||||
integrity sha512-9QRXQG6IrQoviU86g2Y4l19yE81UyEg/iMoGetMfUdQ64NW6unLN7uNbUaO1ws1J0p7uG0dKwR6ohD7tEUPLFA==
|
||||
"@gitlab/svgs@1.221.0":
|
||||
version "1.221.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.221.0.tgz#1804d181b09672d13005d49818eb2da040d67488"
|
||||
integrity sha512-Xn29eer39uVqeuefL/3hVuxo2tazHoEFIXC0F7DnESmBggrcjueNM2tuBUk40oaX6kCzM2Bn4Qn9ESIR+V0PgQ==
|
||||
|
||||
"@gitlab/tributejs@1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
|
||||
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
|
||||
|
||||
"@gitlab/ui@32.36.0":
|
||||
version "32.36.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.36.0.tgz#f3fb6f86dc51a6941bd230c047f0014364732efd"
|
||||
integrity sha512-cX/+P011FD6TrD9/tKuG5xX/tSx9oRwb/bRA45RG1zbkmRh/ohr5zQMuNK2utrHFl4OqrcPirqXMyFl+kKnCdg==
|
||||
"@gitlab/ui@32.38.0":
|
||||
version "32.38.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.38.0.tgz#580bda8daacdf23c1f6a75d77f6df44b1dbe1726"
|
||||
integrity sha512-BS0+4JicfuiCbaWTTok0dQUzUCI8m8t5T7//DQUQqqwCZLYeJlb1AxMatd6IjwcdE0m+AhST3iOZi2x+hDrkbQ==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
bootstrap-vue "2.20.1"
|
||||
|
|
Loading…
Reference in New Issue