diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-15 06:09:32 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-15 06:09:32 +0000 |
commit | cc74c1d821edef69a8b32b2660336a44a14e3f3b (patch) | |
tree | 7f2a09dd8d217c372a865ac2810aaf6b37d3b2ff | |
parent | 5fe82ba3d39345ce6c6df14a10b13998b13c68b2 (diff) | |
download | gitlab-ce-cc74c1d821edef69a8b32b2660336a44a14e3f3b.tar.gz |
Add latest changes from gitlab-org/gitlab@master
52 files changed, 1145 insertions, 932 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index 02396a4ba1b..c8b7168dce1 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -1,26 +1,32 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; import { escape } from 'lodash'; -import { GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { __, sprintf } from '~/locale'; +import { sprintf } from '~/locale'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { hasDiff } from '~/helpers/diffs_helper'; import eventHub from '../../notes/event_hub'; import DiffFileHeader from './diff_file_header.vue'; import DiffContent from './diff_content.vue'; import { diffViewerErrors } from '~/ide/constants'; +import { GENERIC_ERROR, DIFF_FILE } from '../i18n'; export default { components: { DiffFileHeader, DiffContent, + GlButton, GlLoadingIcon, }, directives: { SafeHtml, }, mixins: [glFeatureFlagsMixin()], + i18n: { + genericError: GENERIC_ERROR, + ...DIFF_FILE, + }, props: { file: { type: Object, @@ -53,7 +59,7 @@ export default { ...mapGetters('diffs', ['getDiffFileDiscussions']), viewBlobLink() { return sprintf( - __('You can %{linkStart}view the blob%{linkEnd} instead.'), + this.i18n.blobView, { linkStart: `<a href="${escape(this.file.view_path)}">`, linkEnd: '</a>', @@ -75,9 +81,7 @@ export default { }, forkMessage() { return sprintf( - __( - "You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.", - ), + this.i18n.editInFork, { tag_start: '<span class="js-file-fork-suggestion-section-action">', tag_end: '</span>', @@ -148,7 +152,7 @@ export default { }) .catch(() => { this.isLoadingCollapsedDiff = false; - createFlash(__('Something went wrong on our end. Please try again!')); + createFlash(this.i18n.genericError); }); }, showForkMessage() { @@ -188,14 +192,14 @@ export default { <a :href="file.fork_path" class="js-fork-suggestion-button btn btn-grouped btn-inverted btn-success" - >{{ __('Fork') }}</a + >{{ $options.i18n.fork }}</a > <button class="js-cancel-fork-suggestion-button btn btn-grouped" type="button" @click="hideForkMessage" > - {{ __('Cancel') }} + {{ $options.i18n.cancel }} </button> </div> <gl-loading-icon v-if="showLoadingIcon" class="diff-content loading" /> @@ -205,11 +209,17 @@ export default { <div v-safe-html="errorMessage" class="nothing-here-block"></div> </div> <template v-else> - <div v-show="isCollapsed" class="nothing-here-block diff-collapsed"> - {{ __('This diff is collapsed.') }} - <a class="click-to-expand js-click-to-expand" href="#" @click.prevent="handleToggle">{{ - __('Click to expand it.') - }}</a> + <div v-show="isCollapsed" class="gl-p-7 gl-text-center collapsed-file-warning"> + <p class="gl-mb-8 gl-mt-5"> + {{ $options.i18n.collapsed }} + </p> + <gl-button + class="gl-alert-action gl-mb-5" + data-testid="expandButton" + @click="handleToggle" + > + {{ $options.i18n.expand }} + </gl-button> </div> <diff-content v-show="!isCollapsed && !isFileTooLarge" diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js new file mode 100644 index 00000000000..8b91543587c --- /dev/null +++ b/app/assets/javascripts/diffs/i18n.js @@ -0,0 +1,14 @@ +import { __ } from '~/locale'; + +export const GENERIC_ERROR = __('Something went wrong on our end. Please try again!'); + +export const DIFF_FILE = { + blobView: __('You can %{linkStart}view the blob%{linkEnd} instead.'), + editInFork: __( + "You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request.", + ), + fork: __('Fork'), + cancel: __('Cancel'), + collapsed: __('This file is collapsed.'), + expand: __('Expand file'), +}; diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue index 407fbbb0871..96100e4ac0c 100644 --- a/app/assets/javascripts/import_projects/components/import_projects_table.vue +++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue @@ -1,23 +1,17 @@ <script> -import { throttle } from 'lodash'; import { mapActions, mapState, mapGetters } from 'vuex'; -import { GlButton, GlLoadingIcon, GlModal } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon, GlIntersectionObserver, GlModal } from '@gitlab/ui'; import { n__, __, sprintf } from '~/locale'; -import PaginationLinks from '~/vue_shared/components/pagination_links.vue'; import ProviderRepoTableRow from './provider_repo_table_row.vue'; -import PageQueryParamSync from './page_query_param_sync.vue'; - -const reposFetchThrottleDelay = 1000; export default { name: 'ImportProjectsTable', components: { ProviderRepoTableRow, - PageQueryParamSync, GlLoadingIcon, GlButton, GlModal, - PaginationLinks, + GlIntersectionObserver, }, props: { providerTitle: { @@ -37,7 +31,7 @@ export default { }, computed: { - ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace', 'pageInfo']), + ...mapState(['filter', 'repositories', 'namespaces', 'defaultTargetNamespace']), ...mapGetters([ 'isLoading', 'isImportingAnyRepo', @@ -46,6 +40,10 @@ export default { 'importAllCount', ]), + pagePaginationStateKey() { + return `${this.filter}-${this.repositories.length}`; + }, + availableNamespaces() { const serializedNamespaces = this.namespaces.map(({ fullPath }) => ({ id: fullPath, @@ -84,7 +82,11 @@ export default { mounted() { this.fetchNamespaces(); - this.fetchRepos(); + this.fetchJobs(); + + if (!this.paginatable) { + this.fetchRepos(); + } }, beforeDestroy() { @@ -95,111 +97,95 @@ export default { methods: { ...mapActions([ 'fetchRepos', + 'fetchJobs', 'fetchNamespaces', 'stopJobsPolling', 'clearJobsEtagPoll', 'setFilter', 'importAll', - 'setPage', ]), - - handleFilterInput({ target }) { - this.setFilter(target.value); - }, - - throttledFetchRepos: throttle(function fetch() { - this.fetchRepos(); - }, reposFetchThrottleDelay), }, }; </script> <template> <div> - <page-query-param-sync :page="pageInfo.page" @popstate="setPage" /> <p class="light text-nowrap mt-2"> {{ s__('ImportProjects|Select the repositories you want to import') }} </p> <template v-if="hasIncompatibleRepos"> <slot name="incompatible-repos-warning"></slot> </template> + <div class="d-flex justify-content-between align-items-end flex-wrap mb-3"> + <gl-button + variant="success" + :loading="isImportingAnyRepo" + :disabled="!hasImportableRepos" + type="button" + @click="$refs.importAllModal.show()" + >{{ importAllButtonText }}</gl-button + > + <gl-modal + ref="importAllModal" + modal-id="import-all-modal" + :title="s__('ImportProjects|Import repositories')" + :ok-title="__('Import')" + @ok="importAll" + > + {{ + n__( + 'Are you sure you want to import %d repository?', + 'Are you sure you want to import %d repositories?', + importAllCount, + ) + }} + </gl-modal> + + <slot name="actions"></slot> + <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> + <input + data-qa-selector="githubish_import_filter_field" + class="form-control" + name="filter" + :placeholder="__('Filter your repositories by name')" + autofocus + size="40" + @keyup.enter="setFilter($event.target.value)" + /> + </form> + </div> + <div v-if="repositories.length" class="table-responsive"> + <table class="table import-table"> + <thead> + <th class="import-jobs-from-col">{{ fromHeaderText }}</th> + <th class="import-jobs-to-col">{{ __('To GitLab') }}</th> + <th class="import-jobs-status-col">{{ __('Status') }}</th> + <th class="import-jobs-cta-col"></th> + </thead> + <tbody> + <template v-for="repo in repositories"> + <provider-repo-table-row + :key="repo.importSource.providerLink" + :repo="repo" + :available-namespaces="availableNamespaces" + /> + </template> + </tbody> + </table> + </div> + <gl-intersection-observer + v-if="paginatable" + :key="pagePaginationStateKey" + @appear="fetchRepos" + /> <gl-loading-icon v-if="isLoading" class="js-loading-button-icon import-projects-loading-icon" size="md" /> - <template v-if="!isLoading"> - <div class="d-flex justify-content-between align-items-end flex-wrap mb-3"> - <gl-button - variant="success" - :loading="isImportingAnyRepo" - :disabled="!hasImportableRepos" - type="button" - @click="$refs.importAllModal.show()" - >{{ importAllButtonText }}</gl-button - > - <gl-modal - ref="importAllModal" - modal-id="import-all-modal" - :title="s__('ImportProjects|Import repositories')" - :ok-title="__('Import')" - @ok="importAll" - > - {{ - n__( - 'Are you sure you want to import %d repository?', - 'Are you sure you want to import %d repositories?', - importAllCount, - ) - }} - </gl-modal> - <slot name="actions"></slot> - <form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent> - <input - :value="filter" - data-qa-selector="githubish_import_filter_field" - class="form-control" - name="filter" - :placeholder="__('Filter your repositories by name')" - autofocus - size="40" - @input="handleFilterInput($event)" - @keyup.enter="throttledFetchRepos" - /> - </form> - </div> - <div v-if="repositories.length" class="table-responsive"> - <table class="table import-table"> - <thead> - <th class="import-jobs-from-col">{{ fromHeaderText }}</th> - <th class="import-jobs-to-col">{{ __('To GitLab') }}</th> - <th class="import-jobs-status-col">{{ __('Status') }}</th> - <th class="import-jobs-cta-col"></th> - </thead> - <tbody> - <template v-for="repo in repositories"> - <provider-repo-table-row - :key="repo.importSource.providerLink" - :repo="repo" - :available-namespaces="availableNamespaces" - /> - </template> - </tbody> - </table> - </div> - <div v-else class="text-center"> - <strong>{{ emptyStateText }}</strong> - </div> - <pagination-links - v-if="paginatable" - align="center" - class="gl-mt-3" - :page-info="pageInfo" - :prev-page="pageInfo.page - 1" - :next-page="repositories.length && pageInfo.page + 1" - :change="setPage" - /> - </template> + <div v-if="!isLoading && repositories.length === 0" class="text-center"> + <strong>{{ emptyStateText }}</strong> + </div> </div> </template> diff --git a/app/assets/javascripts/import_projects/components/page_query_param_sync.vue b/app/assets/javascripts/import_projects/components/page_query_param_sync.vue deleted file mode 100644 index 5ba3d70f5d0..00000000000 --- a/app/assets/javascripts/import_projects/components/page_query_param_sync.vue +++ /dev/null @@ -1,39 +0,0 @@ -<script> -import { queryToObject, setUrlParams, updateHistory } from '~/lib/utils/url_utility'; - -export default { - props: { - page: { - type: Number, - required: true, - }, - }, - - watch: { - page(newPage) { - updateHistory({ - url: setUrlParams({ - page: newPage === 1 ? null : newPage, - }), - }); - }, - }, - - created() { - window.addEventListener('popstate', this.updatePage); - }, - - beforeDestroy() { - window.removeEventListener('popstate', this.updatePage); - }, - - methods: { - updatePage() { - const page = parseInt(queryToObject(window.location.search).page, 10) || 1; - this.$emit('popstate', page); - }, - }, - - render: () => null, -}; -</script> diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js index af410f411d8..7b70d290278 100644 --- a/app/assets/javascripts/import_projects/store/actions.js +++ b/app/assets/javascripts/import_projects/store/actions.js @@ -1,11 +1,7 @@ import Visibility from 'visibilityjs'; import * as types from './mutation_types'; import { isProjectImportable } from '../utils'; -import { - convertObjectPropsToCamelCase, - normalizeHeaders, - parseIntPagination, -} from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import Poll from '~/lib/utils/poll'; import { visitUrl, objectToQuery } from '~/lib/utils/url_utility'; import { deprecatedCreateFlash as createFlash } from '~/flash'; @@ -54,12 +50,9 @@ const importAll = ({ state, dispatch }) => { ); }; -const fetchReposFactory = ({ reposPath = isRequired(), hasPagination }) => ({ - state, - dispatch, - commit, -}) => { - dispatch('stopJobsPolling'); +const fetchReposFactory = ({ reposPath = isRequired() }) => ({ state, commit }) => { + const nextPage = state.pageInfo.page + 1; + commit(types.SET_PAGE, nextPage); commit(types.REQUEST_REPOS); const { provider, filter } = state; @@ -68,21 +61,16 @@ const fetchReposFactory = ({ reposPath = isRequired(), hasPagination }) => ({ .get( pathWithParams({ path: reposPath, - filter, - page: hasPagination ? state.pageInfo.page.toString() : '', + filter: filter ?? '', + page: nextPage === 1 ? '' : nextPage.toString(), }), ) - .then(({ data, headers }) => { - const normalizedHeaders = normalizeHeaders(headers); - - if ('X-PAGE' in normalizedHeaders) { - commit(types.SET_PAGE_INFO, parseIntPagination(normalizedHeaders)); - } - + .then(({ data }) => { commit(types.RECEIVE_REPOS_SUCCESS, convertObjectPropsToCamelCase(data, { deep: true })); }) - .then(() => dispatch('fetchJobs')) .catch(e => { + commit(types.SET_PAGE, nextPage - 1); + if (hasRedirectInError(e)) { redirectToUrlInError(e); } else { @@ -136,8 +124,6 @@ const fetchImportFactory = (importPath = isRequired()) => ({ state, commit, gett }; export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, dispatch }) => { - const { filter } = state; - if (eTagPoll) { stopJobsPolling(); clearJobsEtagPoll(); @@ -145,7 +131,7 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d eTagPoll = new Poll({ resource: { - fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter })), + fetchJobs: () => axios.get(pathWithParams({ path: jobsPath, filter: state.filter })), }, method: 'fetchJobs', successCallback: ({ data }) => @@ -157,7 +143,6 @@ export const fetchJobsFactory = (jobsPath = isRequired()) => ({ state, commit, d createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')); } }, - data: { filter }, }); if (!Visibility.hidden()) { @@ -196,7 +181,7 @@ const setPage = ({ state, commit, dispatch }, page) => { return dispatch('fetchRepos'); }; -export default ({ endpoints = isRequired(), hasPagination }) => ({ +export default ({ endpoints = isRequired() }) => ({ clearJobsEtagPoll, stopJobsPolling, restartJobsPolling, @@ -204,7 +189,7 @@ export default ({ endpoints = isRequired(), hasPagination }) => ({ setImportTarget, importAll, setPage, - fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath, hasPagination }), + fetchRepos: fetchReposFactory({ reposPath: endpoints.reposPath }), fetchImport: fetchImportFactory(endpoints.importPath), fetchJobs: fetchJobsFactory(endpoints.jobsPath), fetchNamespaces: fetchNamespacesFactory(endpoints.namespacesPath), diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js index ea719e11d86..6999253d4b2 100644 --- a/app/assets/javascripts/import_projects/store/mutations.js +++ b/app/assets/javascripts/import_projects/store/mutations.js @@ -2,9 +2,41 @@ import Vue from 'vue'; import * as types from './mutation_types'; import { STATUSES } from '../constants'; +const makeNewImportedProject = importedProject => ({ + importSource: { + id: importedProject.id, + fullName: importedProject.importSource, + sanitizedName: importedProject.name, + providerLink: importedProject.providerLink, + }, + importedProject, +}); + +const makeNewIncompatibleProject = project => ({ + importSource: { ...project, incompatible: true }, + importedProject: null, +}); + +const processLegacyEntries = ({ newRepositories, existingRepositories, factory }) => { + const newEntries = []; + newRepositories.forEach(project => { + const existingProject = existingRepositories.find(p => p.importSource.id === project.id); + const importedProjectShape = factory(project); + + if (existingProject) { + Object.assign(existingProject, importedProjectShape); + } else { + newEntries.push(importedProjectShape); + } + }); + return newEntries; +}; + export default { [types.SET_FILTER](state, filter) { state.filter = filter; + state.repositories = []; + state.pageInfo.page = 0; }, [types.REQUEST_REPOS](state) { @@ -17,30 +49,41 @@ export default { if (!Array.isArray(repositories)) { // Legacy code path, will be removed when all importers will be switched to new pagination format // https://gitlab.com/gitlab-org/gitlab/-/issues/27370#note_379034091 + + const newImportedProjects = processLegacyEntries({ + newRepositories: repositories.importedProjects, + existingRepositories: state.repositories, + factory: makeNewImportedProject, + }); + + const incompatibleRepos = repositories.incompatibleRepos ?? []; + const newIncompatibleProjects = processLegacyEntries({ + newRepositories: incompatibleRepos, + existingRepositories: state.repositories, + factory: makeNewIncompatibleProject, + }); + state.repositories = [ - ...repositories.importedProjects.map(importedProject => ({ - importSource: { - id: importedProject.id, - fullName: importedProject.importSource, - sanitizedName: importedProject.name, - providerLink: importedProject.providerLink, - }, - importedProject, - })), + ...newImportedProjects, + ...state.repositories, ...repositories.providerRepos.map(project => ({ importSource: project, importedProject: null, })), - ...(repositories.incompatibleRepos ?? []).map(project => ({ - importSource: { ...project, incompatible: true }, - importedProject: null, - })), + ...newIncompatibleProjects, ]; + if (incompatibleRepos.length === 0 && repositories.providerRepos.length === 0) { + state.pageInfo.page -= 1; + } + return; } - state.repositories = repositories; + state.repositories = [...state.repositories, ...repositories]; + if (repositories.length === 0) { + state.pageInfo.page -= 1; + } }, [types.RECEIVE_REPOS_ERROR](state) { @@ -100,10 +143,6 @@ export default { } }, - [types.SET_PAGE_INFO](state, pageInfo) { - state.pageInfo = pageInfo; - }, - [types.SET_PAGE](state, page) { state.pageInfo.page = page; }, diff --git a/app/assets/javascripts/import_projects/store/state.js b/app/assets/javascripts/import_projects/store/state.js index 3318181e4af..ecd93561d52 100644 --- a/app/assets/javascripts/import_projects/store/state.js +++ b/app/assets/javascripts/import_projects/store/state.js @@ -8,6 +8,6 @@ export default () => ({ ciCdOnly: false, filter: '', pageInfo: { - page: 1, + page: 0, }, }); diff --git a/app/models/ci_platform_metric.rb b/app/models/ci_platform_metric.rb index ce0cfda336c..5e6e3eddce9 100644 --- a/app/models/ci_platform_metric.rb +++ b/app/models/ci_platform_metric.rb @@ -14,19 +14,25 @@ class CiPlatformMetric < ApplicationRecord numericality: { only_integer: true, greater_than: 0 } CI_VARIABLE_KEY = 'AUTO_DEVOPS_PLATFORM_TARGET' + ALLOWED_TARGETS = %w[ECS FARGATE].freeze def self.insert_auto_devops_platform_targets! + recorded_at = Time.zone.now + # This work can NOT be done in-database because value is encrypted. # However, for 'AUTO_DEVOPS_PLATFORM_TARGET', these values are only # encrypted as a matter of course, rather than as a need for secrecy. # So this is not a security risk, but exposing other keys possibly could be. variables = Ci::Variable.by_key(CI_VARIABLE_KEY) - recorded_at = Time.zone.now + counts = variables.group_by(&:value).map do |value, variables| - target = value.truncate(PLATFORM_TARGET_MAX_LENGTH, separator: '', omission: '') + # While this value is, in theory, not secret. A user could accidentally + # put a secret in here so we need to make sure we filter invalid values. + next unless ALLOWED_TARGETS.include?(value) + count = variables.count - self.new(recorded_at: recorded_at, platform_target: target, count: count) - end + self.new(recorded_at: recorded_at, platform_target: value, count: count) + end.compact bulk_insert!(counts, validate: true) end diff --git a/changelogs/unreleased/ci-platform-metrics-invalid-bucket.yml b/changelogs/unreleased/ci-platform-metrics-invalid-bucket.yml new file mode 100644 index 00000000000..e51d0c02c7d --- /dev/null +++ b/changelogs/unreleased/ci-platform-metrics-invalid-bucket.yml @@ -0,0 +1,5 @@ +--- +title: Filter the values for deployment platform metrics +merge_request: 42116 +author: +type: changed diff --git a/changelogs/unreleased/feature-highlight-collapsed-diff-files.yml b/changelogs/unreleased/feature-highlight-collapsed-diff-files.yml new file mode 100644 index 00000000000..9c066e1ffa3 --- /dev/null +++ b/changelogs/unreleased/feature-highlight-collapsed-diff-files.yml @@ -0,0 +1,5 @@ +--- +title: Expand the visible highlight for collapsed diffs +merge_request: 41393 +author: +type: other diff --git a/changelogs/unreleased/xanf-add-infinite-scroll-to-importers.yml b/changelogs/unreleased/xanf-add-infinite-scroll-to-importers.yml new file mode 100644 index 00000000000..42a8130f8c6 --- /dev/null +++ b/changelogs/unreleased/xanf-add-infinite-scroll-to-importers.yml @@ -0,0 +1,5 @@ +--- +title: Introduce infinite scrolling to importers +merge_request: 41789 +author: +type: changed diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md index 4f1037f4f2d..1dac098ec0c 100644 --- a/doc/administration/auth/ldap/index.md +++ b/doc/administration/auth/ldap/index.md @@ -16,7 +16,7 @@ This integration works with most LDAP-compliant directory servers, including: - Open LDAP - 389 Server -Users added through LDAP take a [licensed seat](../../../subscriptions/index.md#choosing-the-number-of-users). +Users added through LDAP take a [licensed seat](../../../subscriptions/self_managed/index.md#choose-the-number-of-users). GitLab Enterprise Editions (EE) include enhanced integration, including group membership syncing as well as multiple LDAP servers support. diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md index 5d9a533f6d5..1b526cabde2 100644 --- a/doc/ci/pipelines/index.md +++ b/doc/ci/pipelines/index.md @@ -199,7 +199,7 @@ such as builds, logs, artifacts, and triggers. **This action cannot be undone.** ### Pipeline quotas Each user has a personal pipeline quota that tracks the usage of shared runners in all personal projects. -Each group has a [usage quota](../../subscriptions/index.md#ci-pipeline-minutes) that tracks the usage of shared runners for all projects created within the group. +Each group has a [usage quota](../../subscriptions/gitlab_com/index.md#ci-pipeline-minutes) that tracks the usage of shared runners for all projects created within the group. When a pipeline is triggered, regardless of who triggered it, the pipeline quota for the project owner's [namespace](../../user/group/index.md#namespaces) is used. In this case, the namespace can be the user or group that owns the project. diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index f1476f8022f..32561e6b98c 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -50,7 +50,7 @@ If you are using a self-managed instance of GitLab: If you are using GitLab.com: - You can select from a list of [shared runners that GitLab maintains](../../user/gitlab_com/index.md#shared-runners). -- The shared runners consume the [pipelines minutes](../../subscriptions/index.md#ci-pipeline-minutes) +- The shared runners consume the [pipelines minutes](../../subscriptions/gitlab_com/index.md#ci-pipeline-minutes) included with your account. #### How shared runners pick jobs diff --git a/doc/development/architecture.md b/doc/development/architecture.md index eba9b7099f3..e0e800044d2 100644 --- a/doc/development/architecture.md +++ b/doc/development/architecture.md @@ -173,7 +173,7 @@ Table description links: | [Gitaly](#gitaly) | Git RPC service for handling all Git calls made by GitLab | ✅ | ✅ | ✅ | ✅ | ⚙ | ✅ | CE & EE | | [GitLab Exporter](#gitlab-exporter) | Generates a variety of GitLab metrics | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | CE & EE | | [GitLab Geo Node](#gitlab-geo) | Geographically distributed GitLab nodes | ⚙ | ⚙ | ❌ | ✅ | ❌ | ⚙ | EE Only | -| [GitLab Managed Apps](#gitlab-managed-apps) | Deploy Helm, Ingress, Cert-Manager, Prometheus, a Runner, JupyterHub, or Knative to a cluster | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE | +| [GitLab Managed Apps](#gitlab-managed-apps) | Deploy Helm, Ingress, Cert-Manager, Prometheus, GitLab Runner, JupyterHub, or Knative to a cluster | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | ⤓ | CE & EE | | [GitLab Pages](#gitlab-pages) | Hosts static websites | ⚙ | ❌ | ❌ | ✅ | ⚙ | ⚙ | CE & EE | | [GitLab self-monitoring: Alertmanager](#alertmanager) | Deduplicates, groups, and routes alerts from Prometheus | ⚙ | ✅ | ⚙ | ✅ | ❌ | ❌ | CE & EE | | [GitLab self-monitoring: Grafana](#grafana) | Metrics dashboard | ✅ | ⚙ | ⤓ | ✅ | ❌ | ❌ | CE & EE | @@ -665,7 +665,7 @@ You can install them after you create a cluster. This includes: - [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) - [Cert-Manager](https://cert-manager.io/docs/) - [Prometheus](https://prometheus.io/docs/introduction/overview/) -- a [Runner](https://docs.gitlab.com/runner/) +- [GitLab Runner](https://docs.gitlab.com/runner/) - [JupyterHub](https://jupyter.org) - [Knative](https://cloud.google.com/knative/) diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md index 585133ae9a2..30ccc52ec5e 100644 --- a/doc/development/cicd/index.md +++ b/doc/development/cicd/index.md @@ -53,25 +53,25 @@ The component that processes a pipeline is [`ProcessPipelineService`](https://gi which is responsible for moving all the pipeline's jobs to a completed state. When a pipeline is created, all its jobs are initially in `created` state. This services looks at what jobs in `created` stage are eligible to be processed based on the pipeline structure. Then it moves them into the `pending` state, which means -they can now [be picked up by a Runner](#job-scheduling). After a job has been executed it can complete +they can now [be picked up by a runner](#job-scheduling). After a job has been executed it can complete successfully or fail. Each status transition for job within a pipeline triggers this service again, which looks for the next jobs to be transitioned towards completion. While doing that, `ProcessPipelineService` updates the status of jobs, stages and the overall pipeline. -On the right side of the diagram we have a list of [Runners](../../ci/runners/README.md) -connected to the GitLab instance. These can be Shared Runners, Group Runners or Project-specific Runners. -The communication between Runners and the Rails server occurs through a set of API endpoints, grouped as +On the right side of the diagram we have a list of [runners](../../ci/runners/README.md) +connected to the GitLab instance. These can be shared runners, group runners, or project-specific runners. +The communication between runners and the Rails server occurs through a set of API endpoints, grouped as the `Runner API Gateway`. -We can register, delete and verify Runners, which also causes read/write queries to the database. After a Runner is connected, +We can register, delete, and verify runners, which also causes read/write queries to the database. After a runner is connected, it keeps asking for the next job to execute. This invokes the [`RegisterJobService`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/services/ci/register_job_service.rb) -which will pick the next job and assign it to the Runner. At this point the job will transition to a +which will pick the next job and assign it to the runner. At this point the job will transition to a `running` state, which again triggers `ProcessPipelineService` due to the status change. For more details read [Job scheduling](#job-scheduling)). -While a job is being executed, the Runner sends logs back to the server as well any possible artifacts +While a job is being executed, the runner sends logs back to the server as well any possible artifacts that need to be stored. Also, a job may depend on artifacts from previous jobs in order to run. In this -case the Runner will download them using a dedicated API endpoint. +case the runner will download them using a dedicated API endpoint. Artifacts are stored in object storage, while metadata is kept in the database. An important example of artifacts are reports (JUnit, SAST, DAST, etc.) which are parsed and rendered in the merge request. @@ -90,25 +90,25 @@ from the `CreatePipelineService` every time a downstream pipeline is triggered. When a Pipeline is created all its jobs are created at once for all stages, with an initial state of `created`. This makes it possible to visualize the full content of a pipeline. -A job with the `created` state won't be seen by the Runner yet. To make it possible to assign a job to a Runner, the job must transition first into the `pending` state, which can happen if: +A job with the `created` state won't be seen by the runner yet. To make it possible to assign a job to a runner, the job must transition first into the `pending` state, which can happen if: 1. The job is created in the very first stage of the pipeline. 1. The job required a manual start and it has been triggered. 1. All jobs from the previous stage have completed successfully. In this case we transition all jobs from the next stage to `pending`. 1. The job specifies DAG dependencies using `needs:` and all the dependent jobs are completed. -When the Runner is connected, it requests the next `pending` job to run by polling the server continuously. +When the runner is connected, it requests the next `pending` job to run by polling the server continuously. NOTE: **Note:** -API endpoints used by the Runner to interact with GitLab are defined in [`lib/api/ci/runner.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/ci/runner.rb) +API endpoints used by the runner to interact with GitLab are defined in [`lib/api/ci/runner.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/api/ci/runner.rb) -After the server receives the request it selects a `pending` job based on the [`Ci::RegisterJobService` algorithm](#ciregisterjobservice), then assigns and sends the job to the Runner. +After the server receives the request it selects a `pending` job based on the [`Ci::RegisterJobService` algorithm](#ciregisterjobservice), then assigns and sends the job to the runner. -Once all jobs are completed for the current stage, the server "unlocks" all the jobs from the next stage by changing their state to `pending`. These can now be picked by the scheduling algorithm when the Runner requests new jobs, and continues like this until all stages are completed. +Once all jobs are completed for the current stage, the server "unlocks" all the jobs from the next stage by changing their state to `pending`. These can now be picked by the scheduling algorithm when the runner requests new jobs, and continues like this until all stages are completed. -### Communication between Runner and GitLab server +### Communication between runner and GitLab server -Once the Runner is [registered](https://docs.gitlab.com/runner/register/) using the registration token, the server knows what type of jobs it can execute. This depends on: +Once the runner is [registered](https://docs.gitlab.com/runner/register/) using the registration token, the server knows what type of jobs it can execute. This depends on: - The type of runner it is registered as: - a shared runner @@ -116,30 +116,30 @@ Once the Runner is [registered](https://docs.gitlab.com/runner/register/) using - a project specific runner - Any associated tags. -The Runner initiates the communication by requesting jobs to execute with `POST /api/v4/jobs/request`. Although this polling generally happens every few seconds we leverage caching via HTTP headers to reduce the server-side work load if the job queue doesn't change. +The runner initiates the communication by requesting jobs to execute with `POST /api/v4/jobs/request`. Although this polling generally happens every few seconds we leverage caching via HTTP headers to reduce the server-side work load if the job queue doesn't change. This API endpoint runs [`Ci::RegisterJobService`](https://gitlab.com/gitlab-org/gitlab/blob/master/app/services/ci/register_job_service.rb), which: 1. Picks the next job to run from the pool of `pending` jobs -1. Assigns it to the Runner -1. Presents it to the Runner via the API response +1. Assigns it to the runner +1. Presents it to the runner via the API response ### `Ci::RegisterJobService` -There are 3 top level queries that this service uses to gather the majority of the jobs and they are selected based on the level where the Runner is registered to: +There are 3 top level queries that this service uses to gather the majority of the jobs and they are selected based on the level where the runner is registered to: -- Select jobs for shared Runner (instance level) -- Select jobs for group level Runner -- Select jobs for project Runner +- Select jobs for shared runner (instance level) +- Select jobs for group runner +- Select jobs for project runner -This list of jobs is then filtered further by matching tags between job and Runner tags. +This list of jobs is then filtered further by matching tags between job and runner tags. NOTE: **Note:** -If a job contains tags, the Runner will not pick the job if it does not match **all** the tags. -The Runner may have more tags than defined for the job, but not vice-versa. +If a job contains tags, the runner will not pick the job if it does not match **all** the tags. +The runner may have more tags than defined for the job, but not vice-versa. -Finally if the Runner can only pick jobs that are tagged, all untagged jobs are filtered out. +Finally if the runner can only pick jobs that are tagged, all untagged jobs are filtered out. -At this point we loop through remaining `pending` jobs and we try to assign the first job that the Runner "can pick" based on additional policies. For example, Runners marked as `protected` can only pick jobs that run against protected branches (such as production deployments). +At this point we loop through remaining `pending` jobs and we try to assign the first job that the runner "can pick" based on additional policies. For example, runners marked as `protected` can only pick jobs that run against protected branches (such as production deployments). -As we increase the number of Runners in the pool we also increase the chances of conflicts which would arise if assigning the same job to different Runners. To prevent that we gracefully rescue conflict errors and assign the next job in the list. +As we increase the number of runners in the pool we also increase the chances of conflicts which would arise if assigning the same job to different runners. To prevent that we gracefully rescue conflict errors and assign the next job in the list. diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index 543195c8bb5..d6f24d6374d 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -455,7 +455,7 @@ If you want to know the in-depth details, here's what's really happening: [skips the test jobs](https://gitlab.com/gitlab-org/gitlab-docs/blob/8d5d5c750c602a835614b02f9db42ead1c4b2f5e/.gitlab-ci.yml#L50-55) to lower the build time. 1. Once the docs site is built, the HTML files are uploaded as artifacts. -1. A specific Runner tied only to the docs project, runs the Review App job +1. A specific runner tied only to the docs project, runs the Review App job that downloads the artifacts and uses `rsync` to transfer the files over to a location where NGINX serves them. @@ -465,7 +465,7 @@ The following GitLab features are used among others: - [Multi project pipelines](../../ci/multi_project_pipeline_graphs.md) - [Review Apps](../../ci/review_apps/index.md) - [Artifacts](../../ci/yaml/README.md#artifacts) -- [Specific Runner](../../ci/runners/README.md#prevent-a-specific-runner-from-being-enabled-for-other-projects) +- [Specific runner](../../ci/runners/README.md#prevent-a-specific-runner-from-being-enabled-for-other-projects) - [Pipelines for merge requests](../../ci/merge_request_pipelines/index.md) ## Testing diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index e9a3d363237..d79054d197f 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -1391,14 +1391,14 @@ interface: ```markdown | Section | Description | |:-------------------------|:----------------------------------------------------------------------------------------------------------------------------| -| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, Runners, and Gitaly servers. | +| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, runners, and Gitaly servers. | | **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit logs. | | **{messages}** Messages | Send and manage broadcast messages for your users. | ``` | Section | Description | |:-------------------------|:----------------------------------------------------------------------------------------------------------------------------| -| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, Runners, and Gitaly servers. | +| **{overview}** Overview | View your GitLab Dashboard, and administer projects, users, groups, jobs, runners, and Gitaly servers. | | **{monitor}** Monitoring | View GitLab system information, and information on background jobs, logs, health checks, requests profiles, and audit logs. | | **{messages}** Messages | Send and manage broadcast messages for your users. | @@ -1974,8 +1974,8 @@ You can use the following fake tokens as examples: | Application ID | `2fcb195768c39e9a94cec2c2e32c59c0aad7a3365c10892e8116b5d83d4096b6` | | Application secret | `04f294d1eaca42b8692017b426d53bbc8fe75f827734f0260710b83a556082df` | | CI/CD variable | `Li8j-mLUVA3eZYjPfd_H` | -| Specific Runner token | `yrnZW46BrtBFqM7xDzE7dddd` | -| Shared Runner token | `6Vk7ZsosqQyfreAxXTZr` | +| Specific runner token | `yrnZW46BrtBFqM7xDzE7dddd` | +| Shared runner token | `6Vk7ZsosqQyfreAxXTZr` | | Trigger token | `be20d8dcc028677c931e04f3871a9b` | | Webhook secret token | `6XhDroRcYPM5by_h-HLY` | | Health check token | `Tu7BgjR9qeZTEyRzGG2P` | diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md index 6616c350e2b..01f9d9b16fb 100644 --- a/doc/development/ee_features.md +++ b/doc/development/ee_features.md @@ -26,6 +26,16 @@ setting the [`FOSS_ONLY` environment variable](https://gitlab.com/gitlab-org/git to something that evaluates as `true`. The same works for running tests (for example `FOSS_ONLY=1 yarn jest`). +## CI pipelines in a FOSS context + +By default, merge request pipelines for development run in an EE-context only. If you are +developing features that differ between FOSS and EE, you may wish to run pipelines in a +FOSS context as well. + +To run pipelines in both contexts, include `RUN AS-IF-FOSS` in the merge request title. + +See the [As-if-FOSS jobs](pipelines.md#as-if-foss-jobs) pipelines documentation for more information. + ## Separation of EE code All EE code should be put inside the `ee/` top-level directory. The diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md index a6afef668bd..00a8c0bf6e9 100644 --- a/doc/development/pipelines.md +++ b/doc/development/pipelines.md @@ -392,7 +392,7 @@ graph LR subgraph "post-test stage"; Z["fail-pipeline-early"]; end - + A --"artifact: list of test files"--> G G --"on failure"--> Z ``` @@ -460,8 +460,8 @@ of the `gitlab-org/gitlab-foss` project. These jobs are only created in the foll - Merge requests which include `RUN AS-IF-FOSS` in their title. - Merge requests that changes the CI configuration. -The `* as-if-foss` jobs have the `FOSS_ONLY='1'` variable set and gets their EE-specific -folders removed before the tests start running. +The `* as-if-foss` jobs are run in addition to the regular EE-context jobs. They have the `FOSS_ONLY='1'` variable +set and get their EE-specific folders removed before the tests start running. The intent is to ensure that a change won't introduce a failure once the `gitlab-org/gitlab` project will be synced to the `gitlab-org/gitlab-foss` project. diff --git a/doc/development/windows.md b/doc/development/windows.md index c92a468fad3..3301e4f7c8f 100644 --- a/doc/development/windows.md +++ b/doc/development/windows.md @@ -87,7 +87,7 @@ You should now be remoted into a Windows machine with a command prompt. - Start the runner: `gitlab-runner.exe start`. For more information, see [Install GitLab Runner on Windows](https://docs.gitlab.com/runner/install/windows.html) -and [Registering Runners](https://docs.gitlab.com/runner/register/index.html). +and [Registering runners](https://docs.gitlab.com/runner/register/index.html). ## Developer tips diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md new file mode 100644 index 00000000000..bce61cdad66 --- /dev/null +++ b/doc/subscriptions/gitlab_com/index.md @@ -0,0 +1,315 @@ +--- +type: index, reference +--- + +# GitLab.com subscription **(BRONZE ONLY)** + +GitLab.com is GitLab Inc.'s software-as-a-service offering. You don't need to +install anything to use GitLab.com, you only need to +[sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away. + +In this page we'll go through the details of your GitLab.com subscription. + +## Choose a GitLab.com group or personal subscription + +On GitLab.com you can apply a subscription to either a group or a personal namespace. + +When applied to: + +- A **group**, the group, all subgroups, and all projects under the selected + group on GitLab.com will have the features of the associated tier. GitLab recommends + choosing a group plan when managing an organization's projects and users. +- A **personal userspace**, all projects will have features with the + subscription applied, but as it's not a group, group features won't be available. + +## Choose a GitLab.com tier + +Pricing is [tier-based](https://about.gitlab.com/pricing/), allowing you to choose +the features which fit your budget. For information on what features are available +at each tier, see the +[GitLab.com feature comparison](https://about.gitlab.com/pricing/gitlab-com/feature-comparison/). + +## Choose the number of users + +NOTE: **Note:** +Applied only to groups. + +A GitLab.com subscription uses a concurrent (_seat_) model. You pay for a +subscription according to the maximum number of users enabled at once. You can +add and remove users during the subscription period, as long as the total users +at any given time is within your subscription count. + +Every occupied seat, whether by person, job, or bot is counted in the subscription, +with the following exception: + +- Members with Guest permissions on a Gold subscription. + +TIP: **Tip:** +To support the open source community and encourage the development of open +source projects, GitLab grants access to **Gold** features for all GitLab.com +**public** projects, regardless of the subscription. + +## Obtain a GitLab.com subscription + +To subscribe to GitLab.com: + +- **For individuals**: + 1. Create a user account for yourself using our + [sign up page](https://gitlab.com/users/sign_in#register-pane). + 1. Visit the [billing page](https://gitlab.com/profile/billings) + under your profile. + 1. Select the **Bronze**, **Silver**, or **Gold** GitLab.com plan through the + [Customers Portal](https://customers.gitlab.com/). + 1. Link your GitLab.com account with your Customers Portal account. + Once a plan has been selected, if your account is not + already linked, you will be prompted to link your account with a + **Sign in to GitLab.com** button. + 1. Select the namespace from the drop-down list to associate the subscription. + 1. Proceed to checkout. +- **For groups**: + 1. Create a user account for yourself using our + [sign up page](https://gitlab.com/users/sign_in#register-pane). + 1. Create a [group](../../user/group/index.md). GitLab groups help assemble related + projects together allowing you to grant members access to several projects + at once. A group is not required if you plan on having projects inside a personal + namespace. + 1. Create additional users and + [add them to the group](../../user/group/index.md#add-users-to-a-group). + 1. Select the **Bronze**, **Silver**, or **Gold** GitLab.com plan through the + [Customers Portal](https://customers.gitlab.com/). + 1. Link your GitLab.com account with your Customers Portal account. + Once a plan has been selected, if your account is not + already linked, you will be prompted to link your account with a + **Sign in to GitLab.com** button. + 1. Select the namespace from the drop-down list to associate the subscription. + 1. Proceed to checkout. + +TIP: **Tip:** +You can also go to the [**My Account**](https://customers.gitlab.com/customers/edit) +page to add or change the GitLab.com account link. + +## View your GitLab.com subscription + +To see the status of your GitLab.com subscription, log in to GitLab.com and go +to the **Billing** section of the relevant namespace: + +- **For individuals**: Visit the [billing page](https://gitlab.com/profile/billings) + under your profile. +- **For groups**: From the group page (*not* from a project within the group), go to **Settings > Billing**. + + NOTE: **Note:** + You must have Owner level [permissions](../../user/permissions.md) to view a group's billing page. + + The following table describes details of your subscription for groups: + + | Field | Description | + |-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| + | **Seats in subscription** | If this is a paid plan, represents the number of seats you've paid to support in your group. | + | **Seats currently in use** | Number of active seats currently in use. | + | **Max seats used** | Highest number of seats you've used. If this exceeds the seats in subscription, you may owe an additional fee for the additional users. | + | **Seats owed** | If your maximum seats used exceeds the seats in your subscription, you'll owe an additional fee for the users you've added. | + | **Subscription start date** | Date your subscription started. If this is for a Free plan, is the date you transitioned off your group's paid plan. | + | **Subscription end date** | Date your current subscription will end. Does not apply to Free plans. | + +## Renew your GitLab.com subscription + +To renew your subscription: + +1. [Prepare for renewal by reviewing your account](#prepare-for-renewal-by-reviewing-your-account) +1. [Renew your GitLab.com subscription](#renew-or-change-a-gitlabcom-subscription) + +### Prepare for renewal by reviewing your account + +The [Customers Portal](https://customers.gitlab.com/customers/sign_in) is your +tool for renewing and modifying your subscription. Before going ahead with renewal, +log in and verify or update: + +- The invoice contact details on the **Account details** page. +- The credit card on file on the **Payment Methods** page. + +TIP: **Tip:** +Contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) +if you need assistance accessing the Customers Portal or if you need to change +the contact person who manages your subscription. + +It's important to regularly review your user accounts, because: + +- A GitLab subscription is based on the number of users. You will pay more than + you should if you renew for too many users, while the renewal will fail if you + attempt to renew a subscription for too few users. +- Stale user accounts can be a security risk. A regular review helps reduce this risk. + +#### Users over License + +A GitLab subscription is valid for a specific number of users. For details, see +[Choose the number of users](#choose-the-number-of-users). + +If the active user count exceeds the number included in the subscription, known +as the number of _users over license_, you must pay for the excess number of +users either before renewal, or at the time of renewal. This is also known the +_true up_ process. + +There is no self-service option for purchasing additional seats. You must +request a quotation from GitLab Sales. To do so, contact GitLab via our +[support form](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). + +The amount charged per seat is calculated by one of the following methods: + +- If paid before renewal, the amount per seat is calculated on a prorated basis. + For example, if the user was added 3 months before the end of the subscription + period, the amount owing is calculated as: (3 / 12) x annual fee. +- If paid on renewal, the amount per seat is the standard annual fee. + +### Renew or change a GitLab.com subscription + +NOTE: **Note:** +To renew for more users than are currently active in your GitLab.com plan, +contact our sales team via `renewals@gitlab.com` for assistance as this can't be +done in the Customers Portal. + +For details on upgrading your subscription tier, see +[Upgrade your GitLab.com subscription tier](#upgrade-your-gitlabcom-subscription-tier). + +#### Automatic renewal + +To view or change automatic subscription renewal (at the same tier as the +previous period), log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in), and: + +- If you see a **Resume subscription** button, your subscription was canceled + previously. Click it to resume automatic renewal. +- If you see **Cancel subscription**, your subscription is set to automatically + renew at the end of the subscription period. Click it to cancel automatic renewal. + +With automatic renewal enabled, the subscription will automatically renew on the +expiration date and there will be no gap in available service. An invoice will be +generated for the renewal and available for viewing or download in the +[View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty +during the renewal process, contact our +[support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance. + +## Upgrade your GitLab.com subscription tier + +To upgrade your [GitLab tier](https://about.gitlab.com/pricing/): + +1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in). +1. Select the **Upgrade** button on the relevant subscription card on the [Manage purchases](https://customers.gitlab.com/subscriptions) page. +1. Select the desired upgrade. +1. Confirm the active form of payment, or add a new form of payment. +1. Check the **I accept the Privacy Policy and Terms of Service** checkbox. +1. Select **Confirm purchase**. + +When the purchase has been processed, you receive confirmation of your new subscription tier. + +## Subscription expiry + +When your subscription or trial expires, GitLab does not delete your data, but +it may become inaccessible, depending on the tier at expiry. Some features may not +behave as expected if you're not prepared for the expiry. For example, +[environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab/-/issues/24759). + +If you renew or upgrade, your data will again be accessible. + +## CI pipeline minutes + +CI pipeline minutes are the execution time for your +[pipelines](../../ci/pipelines/index.md) on GitLab's shared runners. Each +[GitLab.com tier](https://about.gitlab.com/pricing/) includes a monthly quota +of CI pipeline minutes: + +- Free: 400 minutes +- Bronze: 2,000 minutes +- Silver: 10,000 minutes +- Gold: 50,000 minutes + +Quotas apply to: + +- Groups, where the minutes are shared across all members of the group, its + subgroups, and nested projects. To view the group's usage, navigate to the group, + then **Settings > Usage Quotas**. +- Your personal account, where the minutes are available for your personal projects. + To view and buy personal minutes, click your avatar, then + **Settings > [Usage Quotas](https://gitlab.com/profile/usage_quotas#pipelines-quota-tab)**. + +Only pipeline minutes for GitLab shared runners are restricted. If you have a +specific runner set up for your projects, there is no limit to your build time on GitLab.com. + +The available quota is reset on the first of each calendar month at midnight UTC. + +When the CI minutes are depleted, an email is sent automatically to notify the owner(s) +of the namespace. You can [purchase additional CI minutes](#purchase-additional-ci-minutes), +or upgrade your account to [Silver or Gold](https://about.gitlab.com/pricing/). +Your own runners can still be used even if you reach your limits. + +### Purchase additional CI minutes + +If you're using GitLab.com, you can purchase additional CI minutes so your +pipelines won't be blocked after you have used all your CI minutes from your +main quota. You can find pricing for additional CI/CD minutes in the +[GitLab Customers Portal](https://customers.gitlab.com/plans). Additional minutes: + +- Are only used once the shared quota included in your subscription runs out. +- Roll over month to month. + +To purchase additional minutes for your group on GitLab.com: + +1. From your group, go to **Settings > Usage Quotas**. +1. Select **Buy additional minutes** and you will be directed to the Customers Portal. +1. Locate the subscription card that's linked to your group on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. +1. Once we have processed your payment, the extra CI minutes will be 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. Click your avatar, then go to **Settings > Usage Quotas**. +1. Select **Buy additional minutes** and you will be directed to the Customers Portal. +1. Locate the subscription card that's linked to your personal namespace on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes will be synced to your personal namespace. +1. To confirm the available CI minutes for your personal projects, click your avatar, then go to **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: + +- If you have purchased extra CI minutes before the purchase of a paid plan, + we will calculate a pro-rated charge for your paid plan. That means you may + be charged for less than one year since your subscription was previously + created with the extra CI minutes. +- Once the extra CI minutes have been assigned to a Group, they can't be transferred + to a different Group. +- 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. + +## Customers portal + +GitLab provides a [customer portal](../index.md#customers-portal) where you can +manage your subscriptions and your account details. + +## Contact Support + +Learn more about: + +- The tiers of [GitLab Support](https://about.gitlab.com/support/). +- [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new). + +We also encourage all users to search our project trackers for known issues and +existing feature requests in the [GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/) project. + +These issues are the best avenue for getting updates on specific product plans +and for communicating directly with the relevant GitLab team members. + +## Troubleshooting + +### Credit card declined + +If your credit card is declined when purchasing a GitLab subscription, possible reasons include: + +- The credit card details provided are incorrect. +- The credit card account has insufficient funds. +- You are using a virtual credit card and it has insufficient funds, or has expired. +- The transaction exceeds the credit limit. +- The transaction exceeds the credit card's maximum transaction amount. + +Check with your financial institution to confirm if any of these reasons apply. If they don't +apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md index 35559c9b970..bc58c9e899d 100644 --- a/doc/subscriptions/index.md +++ b/doc/subscriptions/index.md @@ -2,144 +2,70 @@ type: index, reference --- -# GitLab subscription +# GitLab subscription **(STARTER)** -GitLab offers tiers of features. Your subscription determines which tier you have access to. Subscriptions are valid for 12 months. +GitLab offers tiers of features. Your subscription determines which tier you +have access to. Subscriptions are valid for 12 months. -GitLab provides special subscriptions to participants in the [GitLab Education Program](https://about.gitlab.com/solutions/education/) and [GitLab Open Source Program](https://about.gitlab.com/solutions/open-source/). For details on obtaining and renewing these subscriptions, see: +GitLab provides special subscriptions to participants in: -- [GitLab for Education subscriptions](#gitlab-for-education-subscriptions) -- [GitLab for Open Source subscriptions](#gitlab-for-open-source-subscriptions) +- [Education](#gitlab-for-education-subscriptions) +- [Open Source](#gitlab-for-open-source-subscriptions) -## Choosing a GitLab subscription +## Choose a GitLab subscription -When choosing a subscription, consider the following factors: +When choosing a subscription, there are two factors to consider: -- [GitLab tier](#choosing-a-gitlab-tier) -- [GitLab.com or self-managed](#choosing-between-gitlabcom-or-self-managed) -- [Group or personal subscription (GitLab.com only)](#choosing-a-gitlabcom-group-or-personal-subscription) -- [Number of users](#choosing-the-number-of-users) +- [GitLab.com or self-managed](#choose-between-gitlabcom-or-self-managed) +- [GitLab tier](#choose-a-gitlab-tier) -### Choosing a GitLab tier +### Choose between GitLab.com or self-managed -Pricing is [tier-based](https://about.gitlab.com/pricing/), allowing you to choose the features which fit your budget. See the [GitLab.com feature comparison](https://about.gitlab.com/pricing/gitlab-com/feature-comparison/) and the [self-managed feature comparison](https://about.gitlab.com/pricing/self-managed/feature-comparison/) for information on what features are available at each tier for each product. +There are some differences in how a subscription applies, depending if you use +GitLab.com or a self-managed instance: -### Choosing between GitLab.com or self-managed +- [GitLab.com](gitlab_com/index.md): GitLab's software-as-a-service offering. + You don't need to install anything to use GitLab.com, you only need to + [sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away. +- [GitLab self-managed](self_managed/index.md): Install, administer, and maintain + your own GitLab instance. -There are some differences in how a subscription applies, depending if you use GitLab.com or a self-managed instance. - -- [GitLab.com](#gitlabcom): GitLab's software-as-a-service offering. You don't need to install anything to use GitLab.com, you only need to [sign up](https://gitlab.com/users/sign_up) and start using GitLab straight away. -- [GitLab self-managed](#self-managed): Install, administer, and maintain your own GitLab instance. - -On a self-managed instance, a GitLab subscription provides the same set of features for all users. On GitLab.com you can apply a subscription to either a group or a personal namespace. - -NOTE: **Note:** -Subscriptions cannot be transferred between GitLab.com and GitLab self-managed. A new subscription must be purchased and applied as needed. - -### Choosing a GitLab.com group or personal subscription - -On GitLab.com you can apply a subscription to either a group or a personal namespace. - -When applied to: - -- A **group**, the group, all subgroups, and all projects under the selected - group on GitLab.com will have the features of the associated tier. GitLab recommends - choosing a group plan when managing an organization's projects and users. -- A **personal userspace** instead, all projects will have features with the - subscription applied, but as it's not a group, group features won't be available. - -### Choosing the number of users - -There are some differences between who is counted in a subscription, depending if you use GitLab.com or a self-managed instance. - -#### GitLab.com - -A GitLab.com subscription uses a concurrent (_seat_) model. You pay for a subscription according to the maximum number of users enabled at once. You can add and remove users during the subscription period, as long as the total users at any given time is within your subscription count. - -Every occupied seat, whether by person, job, or bot is counted in the subscription, with the following exception: - -- Members with Guest permissions on a Gold subscription. - -TIP: **Tip:** -To support the open source community and encourage the development of open -source projects, GitLab grants access to **Gold** features for all GitLab.com -**public** projects, regardless of the subscription. - -#### Self-managed - -A self-managed subscription uses a hybrid model. You pay for a subscription according to the maximum number of users enabled during the subscription period. For instances that aren't offline or on a closed network, the maximum number of simultaneous users in the self-managed installation is checked each quarter, using [Seat Link](#seat-link). - -Every occupied seat, whether by person, job, or bot is counted in the subscription, with the following exceptions: - -- [Deactivated](../user/admin_area/activating_deactivating_users.md#deactivating-a-user) and -[blocked](../user/admin_area/blocking_unblocking_users.md) users who are restricted prior to the -renewal of a subscription won't be counted as active users for the renewal subscription. They may -count as active users in the subscription period in which they were originally added. -- Members with Guest permissions on an Ultimate subscription. -- GitLab-created service accounts: `Ghost User`, `Support Bot` and [`Project bot users`](../user/project/settings/project_access_tokens.md#project-bot-users). - -##### Users statistics - -To view a breakdown of the users within your instance, including active, billable, and blocked, go to **Admin Area > Overview > Dashboard** and select **Users statistics** in the **Users** section. -For more details, see [Users statistics](../user/admin_area/index.md#users-statistics). +On a self-managed instance, a GitLab subscription provides the same set of +features for _all_ users. On GitLab.com, you can apply a subscription to either +a group or a personal namespace. NOTE: **Note:** -If you have LDAP integration enabled, anyone in the configured domain can sign up for a GitLab account. This can result in an unexpected bill at time of renewal. Consider [disabling new signups](../user/admin_area/settings/sign_up_restrictions.md) and managing new users manually instead. - -## Obtain a GitLab subscription - -### Subscribe to GitLab.com - -To subscribe to GitLab.com: +Subscriptions cannot be transferred between GitLab.com and GitLab self-managed. +A new subscription must be purchased and applied as needed. -1. Create a user account for yourself using our - [sign up page](https://gitlab.com/users/sign_in#register-pane). -1. Create a [group](../user/group/index.md). GitLab groups help assemble related - projects together allowing you to grant members access to several projects - at once. A group is not required if you plan on having projects inside a personal - namespace. -1. Create additional users and - [add them to the group](../user/group/index.md#add-users-to-a-group). -1. Select the **Bronze**, **Silver**, or **Gold** GitLab.com plan through the - [Customers Portal](https://customers.gitlab.com/). -1. Link your GitLab.com account with your Customers Portal account. - Once a plan has been selected, if your account is not - already linked, you will be prompted to link your account with a - **Sign in to GitLab.com** button. -1. Select the namespace from the drop-down list to associate the subscription. -1. Proceed to checkout. +### Choose a GitLab tier -TIP: **Tip:** -You can also go to the [**My Account**](https://customers.gitlab.com/customers/edit) -page to add or change the GitLab.com account link. +Pricing is [tier-based](https://about.gitlab.com/pricing/), allowing you to choose +the features which fit your budget. For information on what features are available +at each tier for each product, see: -### Subscribe through GitLab self-managed +- [GitLab.com feature comparison](https://about.gitlab.com/pricing/gitlab-com/feature-comparison/) +- [Self-managed feature comparison](https://about.gitlab.com/pricing/self-managed/feature-comparison/) -To subscribe to GitLab through a self-managed installation: +## Find your subscription -1. Go to the [Customers Portal](https://customers.gitlab.com/) and purchase a **Starter**, **Premium**, or **Ultimate** self-managed plan. -1. After purchase, a license file is sent to the email address associated to the Customers Portal account, - which must be [uploaded to your GitLab instance](../user/admin_area/license.md#uploading-your-license). +The following chart should help you determine your subscription model. Click +on the list item to go to the respective help page. -TIP: **Tip:** -If you're purchasing a subscription for an existing **Core** self-managed -instance, ensure you're purchasing enough seats to -[cover your users](../user/admin_area/index.md#administering-users). +```mermaid +graph TD -### Credit card declined +A(Is your user account on GitLab.com?) +A --> B(Yes) +A --> C(No) +B --> D(fa:fa-link View your subscription on GitLab.com) +C --> E(fa:fa-link View your self-hosted subscription) -If your credit card is declined when purchasing a GitLab subscription, possible reasons include: - -- The credit card details provided are incorrect. -- The credit card account has insufficient funds. -- You are using a virtual credit card and it has insufficient funds, or has expired. -- The transaction exceeds the credit limit. -- The transaction exceeds the credit card's maximum transaction amount. - -Check with your financial institution to confirm if any of these reasons apply. If they don't -apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). +click D "./gitlab_com/index.html#view-your-gitlabcom-subscription" +click E "./self_managed/index.html#view-your-subscription" +``` -## Manage your GitLab account +## Customers portal With the [Customers Portal](https://customers.gitlab.com/) you can: @@ -234,379 +160,33 @@ To change the password for this customers portal account: 1. Make the required changes to the **Your password** section. 1. Click **Save changes**. -## View your subscription - -You can view details of your subscription in either GitLab.com or your self-managed instance: - -- [View your GitLab.com subscription](#view-your-gitlabcom-subscription) -- [View your self-managed subscription](#view-your-self-managed-subscription) - -### View your GitLab.com subscription - -To see the status of your GitLab.com subscription, log in to GitLab.com and go to the **Billing** section of the relevant namespace: - -- For individuals: - 1. Go to **User Avatar > Settings**. - 1. Click **Billing**. -- For groups: - 1. From the group page (*not* from a project within the group), go to **Settings > Billing**. - You must have Owner level permission to view a group's billing page. - -The following table describes details of your subscription for groups: - -| Field | Description | -|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| **Seats in subscription** | If this is a paid plan, represents the number of seats you've paid to support in your group. | -| **Seats currently in use** | Number of active seats currently in use. | -| **Max seats used** | Highest number of seats you've used. If this exceeds the seats in subscription, you may owe an additional fee for the additional users. | -| **Seats owed** | If your maximum seats used exceeds the seats in your subscription, you'll owe an additional fee for the users you've added. | -| **Subscription start date** | Date your subscription started. If this is for a Free plan, is the date you transitioned off your group's paid plan. | -| **Subscription end date** | Date your current subscription will end. Does not apply to Free plans. | - -### View your self-managed subscription - -To view the status of your self-managed subscription, log in to the self-managed instance and go to the **License** page. - - 1. Go to **Admin Area**. - 1. From the left-hand menu, select **License**. - -## Renew your subscription - -To renew your subscription, [prepare for renewal by reviewing your account](#prepare-for-renewal-by-reviewing-your-account), then do one of the following: - -- [Renew a GitLab.com subscription](#renew-or-change-a-gitlabcom-subscription). -- [Renew a self-managed subscription](#renew-a-self-managed-subscription). - -### Prepare for renewal by reviewing your account - -The [Customers Portal](https://customers.gitlab.com/customers/sign_in) is your tool for renewing and modifying your subscription. Before going ahead with renewal, log in and verify or update: - -- The invoice contact details on the **Account details** page. -- The credit card on file on the **Payment Methods** page. - -TIP: **Tip:** -Contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) if you need assistance accessing the Customers Portal or if you need to change the contact person who manages your subscription. - -It's important to regularly review your user accounts, because: - -- A GitLab subscription is based on the number of users. You will pay more than you should if you renew for too many users, while the renewal will fail if you attempt to renew a subscription for too few users. -- Stale user accounts can be a security risk. A regular review helps reduce this risk. - -#### Users over License - -A GitLab subscription is valid for a specific number of users. For details, see [Choose the number of users](#choosing-the-number-of-users). If the active user count exceeds the number included in the subscription, known as the number of _users over license_, you must pay for the excess number of users either before renewal, or at the time of renewal. This is also known the _true up_ process. - -##### Purchase additional seats for GitLab.com - -There is no self-service option for purchasing additional seats. You must request a quotation from GitLab Sales. To do so, contact GitLab via our [support form](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). - -The amount charged per seat is calculated by one of the following methods: - -- If paid before renewal, the amount per seat is calculated on a prorated basis. For example, if the user was added 3 months before the end of the subscription period, the amount owing is calculated as: (3 / 12) x annual fee. -- If paid on renewal, the amount per seat is the standard annual fee. - -##### Purchase additional users for self-managed - -Self-managed instances can add users to a subscription any time during the subscription period. The cost of additional users added during the subscription period is prorated from the date of purchase through the end of the subscription period. - -To add users to a subscription: - -1. Log in to the [Customers Portal](https://customers.gitlab.com/). -1. Navigate to the **Manage Purchases** page. -1. Select **Add more seats** on the relevant subscription card. -1. Enter the number of additional users. -1. Select **Proceed to checkout**. -1. Review the **Subscription Upgrade Detail**. The system lists the total price for all users on the system and a credit for what you've already paid. You will only be charged for the net change. -1. Select **Confirm Upgrade**. - -The following will be emailed to you: - -- A payment receipt. You can also access this information in the Customers Portal under [**View invoices**](https://customers.gitlab.com/receipts). -- A new license. [Upload this license](../user/admin_area/license.md#uploading-your-license) to your instance to use it. - -### Seat Link - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208832) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.9. - -Seat Link allows us to provide our self-managed customers with prorated charges for user growth throughout the year using a quarterly reconciliation process. - -Seat Link daily sends a count of all users in connected self-managed instances to GitLab. That information is used to automate prorated reconciliations. The data is sent securely through an encrypted HTTPS connection. - -Seat Link provides **only** the following information to GitLab: - -- Date -- License key -- Historical maximum user count -- Active users count - -For offline or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data. - -<details> -<summary>Click here to view example content of a Seat Link POST request.</summary> - -<pre><code> -{ - date: '2020-01-29', - license_key: 'ZXlKa1lYUmhJam9pWm5WNmVsTjVZekZ2YTJoV2NucDBh -RXRxTTA5amQxcG1VMVZqDQpXR3RwZEc5SGIyMVhibmxuZDJ0NWFrNXJTVzVH -UzFCT1hHNVRiVFIyT0ZaUFlVSm1OV1ZGV0VObE1uVk4NCk4xY3ZkM1F4Y2to -MFFuVklXSFJvUWpSM01VdE9SVE5rYkVjclZrdDJORkpOTlhka01qaE5aalpj -YmxSMg0KWVd3MFNFTldTRmRtV1ZGSGRDOUhPR05oUVZvNUsxVnRXRUZIZFU1 -U1VqUm5aVFZGZUdwTWIxbDFZV1EyDQphV1JTY1V4c1ZYSjNPVGhrYVZ4dVlu -TkpWMHRJZUU5dmF6ZEJRVVkxTlVWdFUwMTNSMGRHWm5SNlJFcFYNClQyVkJl -VXc0UzA0NWFFb3ZlSFJrZW0xbVRqUlZabkZ4U1hWcWNXRnZYRzVaTm5GSmVW -UnJVR1JQYTJKdA0KU0ZZclRHTmFPRTVhZEVKMUt6UjRkSE15WkRCT1UyNWlS -MGRJZDFCdmRFWk5Za2h4Tm5sT1VsSktlVlYyDQpXRmhjYmxSeU4wRnRNMU5q -THpCVWFGTmpTMnh3UWpOWVkyc3pkbXBST1dnelZHY3hUV3hxVDIwdlZYRlQN -Ck9EWTJSVWx4WlVOT01EQXhVRlZ3ZGs1Rk0xeHVSVEJTTDFkMWJUQTVhV1ZK -WjBORFdWUktaRXNyVnpsTw0KTldkWWQwWTNZa05VWlZBMmRUVk9kVUpxT1hV -Mk5VdDFTUzk0TUU5V05XbFJhWGh0WEc1cVkyWnhaeTlXDQpTMEpyZWt0cmVY -bzBOVGhFVG1oU1oxSm5WRFprY0Uwck0wZEdhVUpEV1d4a1RXZFRjVU5tYTB0 -a2RteEQNCmNWTlFSbFpuWlZWY2JpdFVVbXhIV0d4MFRuUnRWbkJKTkhwSFJt -TnRaMGsyV0U1MFFUUXJWMUJVTWtOSA0KTVhKUWVGTkxPVTkzV1VsMlVUUldk -R3hNTWswNU1USlNjRnh1U1UxTGJTdHRRM1l5YTFWaWJtSlBTMkUxDQplRkpL -SzJSckszaG1hVXB1ZVRWT1UwdHZXV0ZOVG1WamMyVjRPV0pSUlZkUU9UUnpU -VWh2Wlc5cFhHNUgNClNtRkdVMDUyY1RGMWNGTnhVbU5JUkZkeGVWcHVRMnBh -VTBSUGR6VnRNVGhvWTFBM00zVkZlVzFOU0djMA0KY1ZFM1FWSlplSFZ5UzFS -aGIxTmNia3BSUFQxY2JpSXNJbxRsZVNJNkltZFhiVzFGVkRZNWNFWndiV2Rt -DQpNWEIyY21SbFFrdFNZamxaYURCdVVHcHhiRlV3Tm1WQ2JGSlFaSFJ3Y0Rs -cFMybGhSMnRPTkZOMWNVNU0NClVGeHVTa3N6TUUxcldVOTVWREl6WVVWdk5U -ZGhWM1ZvVjJkSFRtZFBZVXRJTkVGcE55dE1NRE5dWnpWeQ0KWlV0aWJsVk9T -RmRzVVROUGRHVXdWR3hEWEc1MWjWaEtRMGQ2YTAxWFpUZHJURTVET0doV00w -ODRWM0V2DQphV2M1YWs5cWFFWk9aR3BYTm1aVmJXNUNaazlXVUVRMWRrMXpj -bTFDV0V4dldtRmNibFpTTWpWU05VeFMNClEwTjRNMWxWCUtSVGEzTTJaV2xE -V0hKTFRGQmpURXRsZFVaQlNtRnJTbkpPZGtKdlUyUmlNVWxNWWpKaQ0KT0dw -c05YbE1kVnh1YzFWbk5VZDFhbU56ZUM5Tk16TXZUakZOVW05cVpsVTNObEo0 -TjJ4eVlVUkdkWEJtDQpkSHByYWpreVJrcG9UVlo0Y0hKSU9URndiV2RzVFdO -VlhHNXRhVmszTkV0SVEzcEpNMWRyZEVoRU4ydHINCmRIRnFRVTlCVUVVM1pV -SlRORE4xUjFaYVJGb3JlWGM5UFZ4dUlpd2lhWFlpt2lKV00yRnNVbk5RTjJk -Sg0KU1hNMGExaE9SVGR2V2pKQlBUMWNiaUo5DQo=', - max_historical_user_count: 10, - active_users: 6 -} -</code></pre> - -</details> - -You can view the exact JSON payload in the administration panel. To view the payload: - -1. Navigate to **Admin Area > Settings > Metrics and profiling** and expand **Seat Link**. -1. Click **Preview payload**. - -#### Disable Seat Link - -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212375) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.10. - -Seat Link is enabled by default. - -To disable this feature, go to **Admin Area > Settings > Metrics and profiling**, uncheck the **Enable Seat Link** checkbox > **Save changes**. - -To disable Seat Link in an Omnibus GitLab installation, and prevent it from -being configured in the future through the administration panel, set the following in -[`gitlab.rb`](https://docs.gitlab.com/omnibus/settings/configuration.html#configuration-options): - -```ruby -gitlab_rails['seat_link_enabled'] = false -``` - -To disable Seat Link in a GitLab source installation, and prevent it from -being configured in the future through the administration panel, -set the following in `gitlab.yml`: - -```yaml -production: &base - # ... - gitlab: - # ... - seat_link_enabled: false -``` - -### Renew or change a GitLab.com subscription - -To renew for more users than are currently active in your GitLab.com system, contact our sales team via `renewals@gitlab.com` for assistance as this can't be done in the Customers Portal. - -For details on upgrading your subscription tier, see [Upgrade your GitLab.com subscription tier](#upgrade-your-gitlabcom-subscription-tier). - -#### Automatic renewal - -To view or change automatic subscription renewal (at the same tier as the previous period), log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in), and: - -- If you see a **Resume subscription** button, your subscription was canceled previously. Click it to resume automatic renewal. -- If you see **Cancel subscription**, your subscription is set to automatically renew at the end of the subscription period. Click it to cancel automatic renewal. - -With automatic renewal enabled, the subscription will automatically renew on the expiration date and there will be no gap in available service. -An invoice will be generated for the renewal and available for viewing or download in the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance. - -### Renew a self-managed subscription - -Starting 30 days before a subscription expires, GitLab notifies administrators of the date of expiry with a banner in the GitLab user interface. - -We recommend following these steps during renewal: - -1. Prune any inactive or unwanted users by [blocking them](../user/admin_area/blocking_unblocking_users.md#blocking-a-user). -1. Determine if you have a need for user growth in the upcoming subscription. -1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription. - - TIP: **Tip:** - If you need to change your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team via `renewals@gitlab.com` for assistance as this can't be done in the Customers Portal. - -1. In the first box, enter the total number of user licenses you’ll need for the upcoming year. Be sure this number is at least **equal to, or greater than** the number of active users in the system at the time of performing the renewal. -1. Enter the number of [users over license](#users-over-license) in the second box for the user overage incurred in your previous subscription term. - - TIP: **Tip:** - You can find the _users over license_ in your instance's **Admin** dashboard by clicking on the **Admin Area** in the top bar, or going to `/admin`. - - The following table describes details of your admin dashboard and renewal terms: - - | Field | Description | - |:------|:------------| - | Users in License | The number of users you've paid for in the current license loaded on the system. This does not include the amount you've paid for `Users over license` during renewal. | - | Active users | The number of current active users on your system. | - | Maximum users | The highest number of active users on your system during the term of the loaded license. If this number exceeds your users in license count at any point, you incur users over license. | - | Users over license | The number of users that exceed the `Users in License` for the current license term. Charges for this number of users will be incurred at the next renewal. | - -1. Review your renewal details and complete the payment process. -1. A license for the renewal term will be available for download on the [Manage Purchases](https://customers.gitlab.com/subscriptions) page on the relevant subscription card. Select **Copy license to clipboard** or **Download license** to get a copy. -1. [Upload](../user/admin_area/license.md#uploading-your-license) your new license to your instance. - -An invoice will be generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance. - -## Upgrade your subscription tier - -The process for upgrading differs depending on whether you're a GitLab.com or self-managed customer. - -### Upgrade your GitLab.com subscription tier - -To upgrade your [GitLab tier](https://about.gitlab.com/pricing/): - -1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in). -1. Select the **Upgrade** button on the relevant subscription card on the [Manage purchases](https://customers.gitlab.com/subscriptions) page. -1. Select the desired upgrade. -1. Confirm the active form of payment, or add a new form of payment. -1. Check the **I accept the Privacy Policy and Terms of Service** checkbox. -1. Select **Confirm purchase**. - -When the purchase has been processed, you receive confirmation of your new subscription tier. - -### Upgrade your self-managed subscription tier - -To upgrade your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team as this -can't be done in the Customers Portal. You can either send an email to `renewals@gitlab.com`, or -complete the [**Contact Sales**](https://about.gitlab.com/sales/) form. Include details of which subscription you want to upgrade and the desired tier in your message. - -After messaging the sales team, the workflow is as follows: - -1. Receive a reply from the sales team, asking for confirmation of the upgrade. -1. Reply to the sales team, confirming details of the upgrade. -1. Receive a quote from the sales team. -1. Sign and return the quote. -1. Receive the new license. -1. Upload the new license. For details, see [Uploading your license](../user/admin_area/license.md#uploading-your-license). - -The new subscription tier is active when the license file is uploaded. - -## Subscription expiry - -When your subscription or trial expires, GitLab does not delete your data, but it may become inaccessible, depending on the tier at expiry. Some features may not behave as expected if you're not prepared for the expiry. For example, [environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab/-/issues/24759). - -If you renew or upgrade, your data will again be accessible. - -### Self-managed GitLab data - -For self-managed customers, there is a 14-day grace period when your features -will continue to work as-is, after which the entire instance will become read -only. - -However, if you remove the license, you will immediately revert to Core -features, and the instance will be read / write again. - -## CI pipeline minutes - -CI pipeline minutes are the execution time for your [pipelines](../ci/pipelines/index.md) on GitLab's shared runners. Each [GitLab.com tier](https://about.gitlab.com/pricing/) includes a monthly quota of CI pipeline minutes: - -- Free: 2,000 minutes -- Bronze: 2,000 minutes -- Silver: 10,000 minutes -- Gold: 50,000 minutes - -Quotas apply to: - -- Groups, where the minutes are shared across all members of the group, its subgroups, and nested projects. To view the group's usage, navigate to the group, then **Settings > Usage Quotas**. -- Your personal account, where the minutes are available for your personal projects. To view and buy personal minutes, click your avatar, then **Settings > [Usage Quotas](https://gitlab.com/profile/usage_quotas#pipelines-quota-tab)**. - -Only pipeline minutes for GitLab shared runners are restricted. If you have a specific runner set up for your projects, there is no limit to your build time on GitLab.com. - -The available quota is reset on the first of each calendar month at midnight UTC. - -When the CI minutes are depleted, an email is sent automatically to notify the owner(s) -of the namespace. You can [purchase additional CI minutes](#purchasing-additional-ci-minutes), or upgrade your account to [Silver or Gold](https://about.gitlab.com/pricing/). Your own runners can still be used even if you reach your limits. - -### Purchasing additional CI minutes - -If you're using GitLab.com, you can purchase additional CI minutes so your -pipelines won't be blocked after you have used all your CI minutes from your -main quota. You can find pricing for additional CI/CD minutes in the [GitLab Customers Portal](https://customers.gitlab.com/plans). Additional minutes: - -- Are only used once the shared quota included in your subscription runs out. -- Roll over month to month. - -To purchase additional minutes for your group on GitLab.com: - -1. From your group, go to **Settings > Usage Quotas**. -1. Select **Buy additional minutes** and you will be directed to the Customers Portal. -1. Locate the subscription card that's linked to your group on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. -1. Once we have processed your payment, the extra CI minutes will be 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: +## GitLab for Education subscriptions -1. Click your avatar, then go to **Settings > Usage Quotas**. -1. Select **Buy additional minutes** and you will be directed to the Customers Portal. -1. Locate the subscription card that's linked to your personal namespace on GitLab.com, click **Buy more CI minutes**, and complete the details about the transaction. Once we have processed your payment, the extra CI minutes will be synced to your personal namespace. -1. To confirm the available CI minutes for your personal projects, click your avatar, then go to **Settings > Usage Quotas**. +The GitLab Education license can only be used for instructional-use or +non-commercial academic research. - The **Additional minutes** displayed now includes the purchased additional CI minutes, plus any minutes rolled over from last month. +Find more information how to apply and renew at +[GitLab for Education](https://about.gitlab.com/solutions/education/). -Be aware that: +## GitLab for Open Source subscriptions -- If you have purchased extra CI minutes before the purchase of a paid plan, - we will calculate a pro-rated charge for your paid plan. That means you may - be charged for less than one year since your subscription was previously - created with the extra CI minutes. -- Once the extra CI minutes have been assigned to a Group, they can't be transferred - to a different Group. -- 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. +All [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/program/) +requests, including subscription renewals, must be made by using the application process. +If you have any questions, send an email to `opensource@gitlab.com` for assistance. ## Contact Support -We also encourage all users to search our project trackers for known issues and -existing feature requests in the [GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/) project. - -These issues are the best avenue for getting updates on specific product plans -and for communicating directly with the relevant GitLab team members. - Learn more about: - The tiers of [GitLab Support](https://about.gitlab.com/support/). - [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new). -## GitLab for Education subscriptions - -To renew a [GitLab for Education](https://about.gitlab.com/solutions/education/) subscription, send an email to `education@gitlab.com` with the following information: - -1. The number of seats for the renewal. You can add seats if needed. -1. The use case for the license. Specifically, we need verification that the use meets the conditions of the [End User License Agreement](https://about.gitlab.com/terms/#edu-oss). Note that university infrastructure operations and information technology operations don't fall within the stated terms of the Education Program. For details, see the [Education FAQ](https://about.gitlab.com/solutions/education/#FAQ). -1. The full name, email address, and phone number of the primary contact who will be signing the renewal quote. Only signatures by faculty or staff with proper signing authority on the behalf of the university will be accepted. - -After we receive the above information, we will process the request and return a renewal quote for signature. Please allow a minimum of 2 business days for return. Email us at `education@gitlab.com` with any questions. - -## GitLab for Open Source subscriptions +We also encourage all users to search our project trackers for known issues and +existing feature requests in the +[GitLab project](https://gitlab.com/gitlab-org/gitlab/-/issues/). -All [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/program/) requests, including subscription renewals, must be made by using the application process. If you have any questions, send an email to `opensource@gitlab.com` for assistance. +These issues are the best avenue for getting updates on specific product plans +and for communicating directly with the relevant GitLab team members. <!-- ## Troubleshooting diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md new file mode 100644 index 00000000000..a0eb998c545 --- /dev/null +++ b/doc/subscriptions/self_managed/index.md @@ -0,0 +1,318 @@ +--- +type: index, reference +--- + +# GitLab self-managed subscription **(STARTER ONLY)** + +You can install, administer, and maintain your own GitLab instance. + +In this page we'll go through the details of your GitLab self-managed subscription. + +## Choose a GitLab tier + +Pricing is [tier-based](https://about.gitlab.com/pricing/), allowing you to choose +the features which fit your budget. For information on what features are available +at each tier, see the +[GitLab self-managed feature comparison](https://about.gitlab.com/pricing/self-managed/feature-comparison/). + +## Choose the number of users + +A self-managed subscription uses a hybrid model. You pay for a subscription +according to the maximum number of users enabled during the subscription period. +For instances that aren't offline or on a closed network, the maximum number of +simultaneous users in the self-managed installation is checked each quarter, +using [Seat Link](#seat-link). + +Every occupied seat, whether by person, job, or bot is counted in the subscription, +with the following exceptions: + +- [Deactivated](../../user/admin_area/activating_deactivating_users.md#deactivating-a-user) and + [blocked](../../user/admin_area/blocking_unblocking_users.md) users who are restricted prior to the + renewal of a subscription won't be counted as active users for the renewal subscription. They may + count as active users in the subscription period in which they were originally added. +- Members with Guest permissions on an Ultimate subscription. +- GitLab-created service accounts: `Ghost User`, `Support Bot` and [`Project bot users`](../../user/project/settings/project_access_tokens.md#project-bot-users). + +### Users statistics + +To view a breakdown of the users within your instance, including active, billable, +and blocked, go to **Admin Area > Overview > Dashboard** and select **Users statistics** +in the **Users** section. For more details, see +[Users statistics](../../user/admin_area/index.md#users-statistics). + +NOTE: **Note:** +If you have LDAP integration enabled, anyone in the configured domain can sign up for a GitLab account. This can result in an unexpected bill at time of renewal. Consider [disabling new signups](../../user/admin_area/settings/sign_up_restrictions.md) and managing new users manually instead. + +## Obtain a subscription + +To subscribe to GitLab through a self-managed installation: + +1. Go to the [Customers Portal](https://customers.gitlab.com/) and purchase a + **Starter**, **Premium**, or **Ultimate** self-managed plan. +1. After purchase, a license file is sent to the email address associated to the Customers Portal account, + which must be [uploaded to your GitLab instance](../../user/admin_area/license.md#uploading-your-license). + +TIP: **Tip:** +If you're purchasing a subscription for an existing **Core** self-managed +instance, ensure you're purchasing enough seats to +[cover your users](../../user/admin_area/index.md#administering-users). + +## View your subscription + +If you are an administrator, to view the status of your self-managed subscription, +log in to the your GitLab instance and go to the **License** page: + +1. Go to **Admin Area**. +1. From the left-hand menu, select **License**. + +Read more about the [license admin area](../../user/admin_area/license.md). + +## Renew your subscription + +To renew your subscription, +[prepare for renewal by reviewing your account](#prepare-for-renewal-by-reviewing-your-account), +then [renew your self-managed subscription](#renew-a-subscription). + +### Prepare for renewal by reviewing your account + +The [Customers Portal](https://customers.gitlab.com/customers/sign_in) is your +tool for renewing and modifying your subscription. Before going ahead with renewal, +log in and verify or update: + +- The invoice contact details on the **Account details** page. +- The credit card on file on the **Payment Methods** page. + +TIP: **Tip:** +Contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) +if you need assistance accessing the Customers Portal or if you need to change +the contact person who manages your subscription. + +It's important to regularly review your user accounts, because: + +- A GitLab subscription is based on the number of users. You will pay more than + you should if you renew for too many users, while the renewal will fail if you + attempt to renew a subscription for too few users. +- Stale user accounts can be a security risk. A regular review helps reduce this risk. + +#### Users over License + +A GitLab subscription is valid for a specific number of users. For details, see +[Choose the number of users](#choose-the-number-of-users). If the active user +count exceeds the number included in the subscription, known as the number of +_users over license_, you must pay for the excess number of users either before +renewal, or at the time of renewal. This is also known the _true up_ process. + +Self-managed instances can add users to a subscription any time during the +subscription period. The cost of additional users added during the subscription +period is prorated from the date of purchase through the end of the subscription period. + +To add users to a subscription: + +1. Log in to the [Customers Portal](https://customers.gitlab.com/). +1. Navigate to the **Manage Purchases** page. +1. Select **Add more seats** on the relevant subscription card. +1. Enter the number of additional users. +1. Select **Proceed to checkout**. +1. Review the **Subscription Upgrade Detail**. The system lists the total price for all users on the system and a credit for what you've already paid. You will only be charged for the net change. +1. Select **Confirm Upgrade**. + +The following will be emailed to you: + +- A payment receipt. You can also access this information in the Customers Portal under [**View invoices**](https://customers.gitlab.com/receipts). +- A new license. [Upload this license](../../user/admin_area/license.md#uploading-your-license) to your instance to use it. + +### Renew a subscription + +Starting 30 days before a subscription expires, GitLab notifies administrators of the date of expiry with a banner in the GitLab user interface. + +We recommend following these steps during renewal: + +1. Prune any inactive or unwanted users by [blocking them](../../user/admin_area/blocking_unblocking_users.md#blocking-a-user). +1. Determine if you have a need for user growth in the upcoming subscription. +1. Log in to the [Customers Portal](https://customers.gitlab.com/customers/sign_in) and select the **Renew** button beneath your existing subscription. + + TIP: **Tip:** + If you need to change your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team via `renewals@gitlab.com` for assistance as this can't be done in the Customers Portal. + +1. In the first box, enter the total number of user licenses you’ll need for the upcoming year. Be sure this number is at least **equal to, or greater than** the number of active users in the system at the time of performing the renewal. +1. Enter the number of [users over license](#users-over-license) in the second box for the user overage incurred in your previous subscription term. + + TIP: **Tip:** + You can find the _users over license_ in your instance's **Admin** dashboard by clicking on the **Admin Area** in the top bar, or going to `/admin`. + + The following table describes details of your admin dashboard and renewal terms: + + | Field | Description | + |:------|:------------| + | Users in License | The number of users you've paid for in the current license loaded on the system. This does not include the amount you've paid for `Users over license` during renewal. | + | Active users | The number of current active users on your system. | + | Maximum users | The highest number of active users on your system during the term of the loaded license. If this number exceeds your users in license count at any point, you incur users over license. | + | Users over license | The number of users that exceed the `Users in License` for the current license term. Charges for this number of users will be incurred at the next renewal. | + +1. Review your renewal details and complete the payment process. +1. A license for the renewal term will be available for download on the [Manage Purchases](https://customers.gitlab.com/subscriptions) page on the relevant subscription card. Select **Copy license to clipboard** or **Download license** to get a copy. +1. [Upload](../../user/admin_area/license.md#uploading-your-license) your new license to your instance. + +An invoice will be generated for the renewal and available for viewing or download on the [View invoices](https://customers.gitlab.com/receipts) page. If you have difficulty during the renewal process, contact our [support team](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293) for assistance. + +### Seat Link + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208832) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.9. + +Seat Link allows GitLab Inc. to provide our self-managed customers with prorated charges for user growth throughout the year using a quarterly reconciliation process. + +Seat Link daily sends a count of all users in connected self-managed instances to GitLab. That information is used to automate prorated reconciliations. The data is sent securely through an encrypted HTTPS connection. + +Seat Link provides **only** the following information to GitLab: + +- Date +- License key +- Historical maximum user count +- Active users count + +For offline or closed network customers, the existing [true-up model](#users-over-license) will be used. Prorated charges are not possible without user count data. + +<details> +<summary>Click here to view example content of a Seat Link POST request.</summary> + +<pre><code> +{ + date: '2020-01-29', + license_key: 'ZXlKa1lYUmhJam9pWm5WNmVsTjVZekZ2YTJoV2NucDBh +RXRxTTA5amQxcG1VMVZqDQpXR3RwZEc5SGIyMVhibmxuZDJ0NWFrNXJTVzVH +UzFCT1hHNVRiVFIyT0ZaUFlVSm1OV1ZGV0VObE1uVk4NCk4xY3ZkM1F4Y2to +MFFuVklXSFJvUWpSM01VdE9SVE5rYkVjclZrdDJORkpOTlhka01qaE5aalpj +YmxSMg0KWVd3MFNFTldTRmRtV1ZGSGRDOUhPR05oUVZvNUsxVnRXRUZIZFU1 +U1VqUm5aVFZGZUdwTWIxbDFZV1EyDQphV1JTY1V4c1ZYSjNPVGhrYVZ4dVlu +TkpWMHRJZUU5dmF6ZEJRVVkxTlVWdFUwMTNSMGRHWm5SNlJFcFYNClQyVkJl +VXc0UzA0NWFFb3ZlSFJrZW0xbVRqUlZabkZ4U1hWcWNXRnZYRzVaTm5GSmVW +UnJVR1JQYTJKdA0KU0ZZclRHTmFPRTVhZEVKMUt6UjRkSE15WkRCT1UyNWlS +MGRJZDFCdmRFWk5Za2h4Tm5sT1VsSktlVlYyDQpXRmhjYmxSeU4wRnRNMU5q +THpCVWFGTmpTMnh3UWpOWVkyc3pkbXBST1dnelZHY3hUV3hxVDIwdlZYRlQN +Ck9EWTJSVWx4WlVOT01EQXhVRlZ3ZGs1Rk0xeHVSVEJTTDFkMWJUQTVhV1ZK +WjBORFdWUktaRXNyVnpsTw0KTldkWWQwWTNZa05VWlZBMmRUVk9kVUpxT1hV +Mk5VdDFTUzk0TUU5V05XbFJhWGh0WEc1cVkyWnhaeTlXDQpTMEpyZWt0cmVY +bzBOVGhFVG1oU1oxSm5WRFprY0Uwck0wZEdhVUpEV1d4a1RXZFRjVU5tYTB0 +a2RteEQNCmNWTlFSbFpuWlZWY2JpdFVVbXhIV0d4MFRuUnRWbkJKTkhwSFJt +TnRaMGsyV0U1MFFUUXJWMUJVTWtOSA0KTVhKUWVGTkxPVTkzV1VsMlVUUldk +R3hNTWswNU1USlNjRnh1U1UxTGJTdHRRM1l5YTFWaWJtSlBTMkUxDQplRkpL +SzJSckszaG1hVXB1ZVRWT1UwdHZXV0ZOVG1WamMyVjRPV0pSUlZkUU9UUnpU +VWh2Wlc5cFhHNUgNClNtRkdVMDUyY1RGMWNGTnhVbU5JUkZkeGVWcHVRMnBh +VTBSUGR6VnRNVGhvWTFBM00zVkZlVzFOU0djMA0KY1ZFM1FWSlplSFZ5UzFS +aGIxTmNia3BSUFQxY2JpSXNJbxRsZVNJNkltZFhiVzFGVkRZNWNFWndiV2Rt +DQpNWEIyY21SbFFrdFNZamxaYURCdVVHcHhiRlV3Tm1WQ2JGSlFaSFJ3Y0Rs +cFMybGhSMnRPTkZOMWNVNU0NClVGeHVTa3N6TUUxcldVOTVWREl6WVVWdk5U +ZGhWM1ZvVjJkSFRtZFBZVXRJTkVGcE55dE1NRE5dWnpWeQ0KWlV0aWJsVk9T +RmRzVVROUGRHVXdWR3hEWEc1MWjWaEtRMGQ2YTAxWFpUZHJURTVET0doV00w +ODRWM0V2DQphV2M1YWs5cWFFWk9aR3BYTm1aVmJXNUNaazlXVUVRMWRrMXpj +bTFDV0V4dldtRmNibFpTTWpWU05VeFMNClEwTjRNMWxWCUtSVGEzTTJaV2xE +V0hKTFRGQmpURXRsZFVaQlNtRnJTbkpPZGtKdlUyUmlNVWxNWWpKaQ0KT0dw +c05YbE1kVnh1YzFWbk5VZDFhbU56ZUM5Tk16TXZUakZOVW05cVpsVTNObEo0 +TjJ4eVlVUkdkWEJtDQpkSHByYWpreVJrcG9UVlo0Y0hKSU9URndiV2RzVFdO +VlhHNXRhVmszTkV0SVEzcEpNMWRyZEVoRU4ydHINCmRIRnFRVTlCVUVVM1pV +SlRORE4xUjFaYVJGb3JlWGM5UFZ4dUlpd2lhWFlpt2lKV00yRnNVbk5RTjJk +Sg0KU1hNMGExaE9SVGR2V2pKQlBUMWNiaUo5DQo=', + max_historical_user_count: 10, + active_users: 6 +} +</code></pre> + +</details> + +You can view the exact JSON payload in the administration panel. To view the payload: + +1. Navigate to **Admin Area > Settings > Metrics and profiling** and expand **Seat Link**. +1. Click **Preview payload**. + +#### Disable Seat Link + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/212375) in [GitLab Starter](https://about.gitlab.com/pricing/) 12.10. + +Seat Link is enabled by default. + +To disable this feature, go to **Admin Area > Settings > Metrics and profiling**, uncheck the **Enable Seat Link** checkbox > **Save changes**. + +To disable Seat Link in an Omnibus GitLab installation, and prevent it from +being configured in the future through the administration panel, set the following in +[`gitlab.rb`](https://docs.gitlab.com/omnibus/settings/configuration.html#configuration-options): + +```ruby +gitlab_rails['seat_link_enabled'] = false +``` + +To disable Seat Link in a GitLab source installation, and prevent it from +being configured in the future through the administration panel, +set the following in `gitlab.yml`: + +```yaml +production: &base + # ... + gitlab: + # ... + seat_link_enabled: false +``` + +## Upgrade your subscription tier + +To upgrade your [GitLab tier](https://about.gitlab.com/pricing/), contact our sales team as this +can't be done in the Customers Portal. You can either send an email to `renewals@gitlab.com`, or +complete the [**Contact Sales**](https://about.gitlab.com/sales/) form. Include details of which subscription you want to upgrade and the desired tier in your message. + +After messaging the sales team, the workflow is as follows: + +1. Receive a reply from the sales team, asking for confirmation of the upgrade. +1. Reply to the sales team, confirming details of the upgrade. +1. Receive a quote from the sales team. +1. Sign and return the quote. +1. Receive the new license. +1. Upload the new license. For details, see [Uploading your license](../../user/admin_area/license.md#uploading-your-license). + +The new subscription tier is active when the license file is uploaded. + +## Subscription expiry + +When your subscription or trial expires, GitLab does not delete your data, but it +may become inaccessible, depending on the tier at expiry. Some features may not +behave as expected if you're not prepared for the expiry. For example, +[environment specific variables not being passed](https://gitlab.com/gitlab-org/gitlab/-/issues/24759). +If you renew or upgrade, your data will again be accessible. + +For self-managed customers, there is a 14-day grace period when your features +will continue to work as-is, after which the entire instance will become read +only. + +However, if you remove the license, you will immediately revert to Core +features, and the instance will be read / write again. + +## Customers portal + +GitLab provides a [customer portal](../index.md#customers-portal) where you can +manage your subscriptions and your account details. + +## Contact Support + +Learn more about: + +- The tiers of [GitLab Support](https://about.gitlab.com/support/). +- [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new). + +We also encourage all users to search our project trackers for known issues and +existing feature requests in the [GitLab](https://gitlab.com/gitlab-org/gitlab/-/issues/) project. + +These issues are the best avenue for getting updates on specific product plans +and for communicating directly with the relevant GitLab team members. + +## Troubleshooting + +### Credit card declined + +If your credit card is declined when purchasing a GitLab subscription, possible reasons include: + +- The credit card details provided are incorrect. +- The credit card account has insufficient funds. +- You are using a virtual credit card and it has insufficient funds, or has expired. +- The transaction exceeds the credit limit. +- The transaction exceeds the credit card's maximum transaction amount. + +Check with your financial institution to confirm if any of these reasons apply. If they don't +apply, contact [GitLab Support](https://support.gitlab.com/hc/en-us/requests/new?ticket_form_id=360000071293). diff --git a/doc/user/admin_area/activating_deactivating_users.md b/doc/user/admin_area/activating_deactivating_users.md index 448c65038c2..29f162616bf 100644 --- a/doc/user/admin_area/activating_deactivating_users.md +++ b/doc/user/admin_area/activating_deactivating_users.md @@ -44,7 +44,7 @@ Please note that for the deactivation option to be visible to an admin, the user Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user). NOTE: **Note:** -A deactivated user does not consume a [seat](../../subscriptions/index.md#choosing-the-number-of-users). +A deactivated user does not consume a [seat](../../subscriptions/self_managed/index.md#choose-the-number-of-users). ## Activating a user @@ -63,7 +63,7 @@ Users can also be activated using the [GitLab API](../../api/users.md#activate-u NOTE: **Note:** Activating a user will change the user's state to active and it consumes a -[seat](../../subscriptions/index.md#choosing-the-number-of-users). +[seat](../../subscriptions/self_managed/index.md#choose-the-number-of-users). TIP: **Tip:** A deactivated user can also activate their account themselves by simply logging back in via the UI. diff --git a/doc/user/admin_area/blocking_unblocking_users.md b/doc/user/admin_area/blocking_unblocking_users.md index 2f98709a089..d8dde317d38 100644 --- a/doc/user/admin_area/blocking_unblocking_users.md +++ b/doc/user/admin_area/blocking_unblocking_users.md @@ -33,7 +33,7 @@ Personal projects, and group and user history of the blocked user will be left i Users can also be blocked using the [GitLab API](../../api/users.md#block-user). NOTE: **Note:** -A blocked user does not consume a [seat](../../subscriptions/index.md#choosing-the-number-of-users). +A blocked user does not consume a [seat](../../subscriptions/self_managed/index.md#choose-the-number-of-users). ## Unblocking a user @@ -48,4 +48,4 @@ Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-us NOTE: **Note:** Unblocking a user will change the user's state to active and it consumes a -[seat](../../subscriptions/index.md#choosing-the-number-of-users). +[seat](../../subscriptions/self_managed/index.md#choose-the-number-of-users). diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 0669e290d87..58430ab615b 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -147,7 +147,7 @@ The following totals are also included: GitLab billing is based on the number of **Active users**, calculated as **Total users** - **Blocked users**. For details of active users, see -[Choosing the number of users](../../subscriptions/index.md#choosing-the-number-of-users). +[Choosing the number of users](../../subscriptions/self_managed/index.md#choose-the-number-of-users). NOTE: **Note:** Users statistics are calculated daily, so user changes made since the last update won't be diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md index 2c849db66b1..ecbc615f56a 100644 --- a/doc/user/admin_area/license.md +++ b/doc/user/admin_area/license.md @@ -125,7 +125,7 @@ before uploading your license. GitLab.com users cannot upload and use a self-managed license. If you wish to use paid features on GitLab.com, a separate subscription may be -[purchased](../../subscriptions/index.md#subscribe-to-gitlabcom). +[purchased](../../subscriptions/gitlab_com/index.md). ### Users exceed license limit upon renewal diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index 960f7053106..73a8e727389 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -419,6 +419,31 @@ variables: DAST_REQUEST_HEADERS: "Authorization: Bearer my.token" ``` +### URL scan + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214120) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.4. + +A URL scan allows you to specify which parts of a website are scanned by DAST. + +#### Define the URLs to scan + +To specify the paths to be scanned, add a comma-separated list of the paths to the `DAST_PATHS` environment variable. Note that you can only scan paths of a single host. + +```yaml +include: + - template: DAST.gitlab-ci.yml + +variables: + DAST_PATHS=/page1.html,/category1/page1.html,/page3.html +``` + +NOTE: **Note:** +`DAST_AUTH_EXCLUDE_URLS` are ignored when `DAST_PATHS` is set. + +#### Full Scan + +To perform a [full scan](#full-scan) on the listed paths, use the `DAST_FULL_SCAN_ENABLED` environment variable. + ### Customizing the DAST settings CAUTION: **Deprecation:** @@ -472,6 +497,7 @@ DAST can be [configured](#customizing-the-dast-settings) using environment varia | `DAST_XML_REPORT` | string | The filename of the XML report written at the end of a scan. | | `DAST_INCLUDE_ALPHA_VULNERABILITIES` | boolean | Set to `true` to include alpha passive and active scan rules. Default: `false` | | `DAST_USE_AJAX_SPIDER` | boolean | Set to `true` to use the AJAX spider in addition to the traditional spider, useful for crawling sites that require JavaScript. Default: `false` | +| `DAST_PATHS` | string | Set to a comma-separated list of URLs for DAST to scan. For example, `/page1.html,/category1/page3.html,/page2.html` | | `DAST_ZAP_CLI_OPTIONS` | string | ZAP server command-line options. For example, `-Xmx3072m` would set the Java maximum memory allocation pool size. | | `DAST_ZAP_LOG_CONFIGURATION` | string | Set to a semicolon-separated list of additional log4j properties for the ZAP Server. For example, `log4j.logger.org.parosproxy.paros.network.HttpSender=DEBUG;log4j.logger.com.crawljax=DEBUG` | diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 29530a87775..2243ffa0cb1 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -125,7 +125,7 @@ service included with GitLab that coordinates the jobs. If the project is on GitLab.com, shared runners are available (the first 2000 minutes are free, you can -[buy more later](../../subscriptions/index.md#purchasing-additional-ci-minutes)) +[buy more later](../../subscriptions/gitlab_com/index.md#purchase-additional-ci-minutes)) and you do not have to deploy one if they are enough for your needs. If a project-specific runner is desired, or there are no shared runners, it is easy to deploy one. diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index ffeda0cdc92..9066d8a5ee4 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -137,7 +137,7 @@ Linux shared runners on GitLab.com run in [autoscale mode](https://docs.gitlab.c Autoscaling means reduced waiting times to spin up CI/CD jobs, and isolated VMs for each project, thus maximizing security. They're free to use for public open source projects and limited to 2000 CI minutes per month per group for private projects. More minutes -[can be purchased](../../subscriptions/index.md#purchasing-additional-ci-minutes), if +[can be purchased](../../subscriptions/gitlab_com/index.md#purchase-additional-ci-minutes), if needed. Read about all [GitLab.com plans](https://about.gitlab.com/pricing/). All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, CoreOS and the latest Docker Engine diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index 34c75b6b04c..077666bc036 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -193,7 +193,7 @@ Before diving into the details, some things you should be aware of: longer, but it means you don’t get stuck without security patches for base images. - Doing an explicit `docker pull` before each `docker run` fetches the latest image that was just built. This is especially important if you are - using multiple Runners that cache images locally. Using the Git SHA in your + using multiple runners that cache images locally. Using the Git SHA in your image tag makes this less necessary since each job is unique and you shouldn't ever have a stale image. However, it's still possible to have a stale image if you re-build a given commit after a dependency has changed. @@ -240,8 +240,8 @@ There are three ways to authenticate to the Container Registry via ### Container Registry examples with GitLab CI/CD -If you're using Docker-in-Docker on your Runners, this is how your `.gitlab-ci.yml` -should look similar to this: +If you're using Docker-in-Docker on your runners, this is how your `.gitlab-ci.yml` +should look: ```yaml build: diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 72bf9c6b753..1b8c16f401c 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -85,7 +85,7 @@ token.save! ``` This can be shortened into a single-line shell command using the -[GitLab Rails Runner](../../administration/troubleshooting/debug.md#using-the-rails-runner): +[Rails runner](../../administration/troubleshooting/debug.md#using-the-rails-runner): ```shell sudo gitlab-rails runner "token = User.find_by_username('automation-bot').personal_access_tokens.create(scopes: [:read_user, :read_repository], name: 'Automation token'); token.set_token('token-string-here123'); token.save!" @@ -113,7 +113,7 @@ token.revoke! ``` This can be shorted into a single-line shell command using the -[GitLab Rails Runner](../../administration/troubleshooting/debug.md#using-the-rails-runner): +[Rails runner](../../administration/troubleshooting/debug.md#using-the-rails-runner): ```shell sudo gitlab-rails runner "PersonalAccessToken.find_by_token('token-string-here123').revoke!" diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md index 6af08b06294..1157c2c5632 100644 --- a/doc/user/project/clusters/serverless/index.md +++ b/doc/user/project/clusters/serverless/index.md @@ -52,7 +52,7 @@ To run Knative on GitLab, you will need: The simplest way to get started is to add a cluster using GitLab's [GKE integration](../add_remove_clusters.md). The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory. 1. **GitLab Runner:** A runner is required to run the CI jobs that will deploy serverless - applications or functions onto your cluster. You can install the GitLab Runner + applications or functions onto your cluster. You can install GitLab Runner onto the existing Kubernetes cluster. See [Installing Applications](../index.md#installing-applications) for more information. 1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an external IP address or hostname for all the applications served by Knative. You will be prompted to enter a diff --git a/doc/user/project/import/gemnasium.md b/doc/user/project/import/gemnasium.md index 8957960d098..f21ec26bdef 100644 --- a/doc/user/project/import/gemnasium.md +++ b/doc/user/project/import/gemnasium.md @@ -83,7 +83,7 @@ back to both GitLab and GitHub when completed. ![click on connected project](img/gemnasium/project_connected.png) - Your project is now mirrored on GitLab, where the Runners will be able to access + Your project is now mirrored on GitLab, where the runners will be able to access your source code and run your tests. Optional step: If you set this up on GitLab.com, make sure the project is diff --git a/doc/user/project/merge_requests/code_quality.md b/doc/user/project/merge_requests/code_quality.md index 3e30d9dc17c..e03d4e99b86 100644 --- a/doc/user/project/merge_requests/code_quality.md +++ b/doc/user/project/merge_requests/code_quality.md @@ -77,7 +77,7 @@ First, you need GitLab Runner configured: - For the [Docker-in-Docker workflow](../../../ci/docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor). - With enough disk space to handle generated Code Quality files. For example on the [GitLab project](https://gitlab.com/gitlab-org/gitlab) the files are approximately 7 GB. -Once you set up the Runner, include the Code Quality template in your CI configuration: +Once you set up GitLab Runner, include the Code Quality template in your CI configuration: ```yaml include: @@ -136,7 +136,7 @@ This information will be automatically extracted and shown right in the merge re CAUTION: **Caution:** On self-managed instances, if a malicious actor compromises the Code Quality job -definition they will be able to execute privileged Docker commands on the Runner +definition they will be able to execute privileged Docker commands on the runner host. Having proper access control policies mitigates this attack vector by allowing access only to trusted actors. diff --git a/doc/user/project/merge_requests/load_performance_testing.md b/doc/user/project/merge_requests/load_performance_testing.md index aadd2d965ef..daebd71e14f 100644 --- a/doc/user/project/merge_requests/load_performance_testing.md +++ b/doc/user/project/merge_requests/load_performance_testing.md @@ -102,7 +102,7 @@ job. An example configuration workflow: -1. Set up a GitLab Runner that can run Docker containers, such as a Runner using the +1. Set up GitLab Runner to run Docker containers, like the [Docker-in-Docker workflow](../../../ci/docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor). 1. Configure the default Load Performance Testing CI job in your `.gitlab-ci.yml` file. You need to include the template and configure it with variables: diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index a9d2c813f93..c3825371030 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -83,9 +83,9 @@ We try to make sure that this token doesn't leak by: 1. Masking the job token from job logs. 1. Granting permissions to the job token **only** when the job is running. -However, this brings a question about the Runners security. To make sure that +However, this brings up a question about the runner's security. To make sure that this token doesn't leak, you should also make sure that you configure -your Runners in the most possible secure way, by avoiding the following: +your runners in the most possible secure way, by avoiding the following: 1. Any usage of Docker's `privileged` mode is risky if the machines are re-used. 1. Using the `shell` executor since jobs run on the same machine. @@ -95,13 +95,13 @@ to steal the tokens of other jobs. ## Before GitLab 8.12 -In versions before GitLab 8.12, all CI jobs would use the CI Runner's token +In versions before GitLab 8.12, all CI jobs would use the runner's token to checkout project sources. -The project's Runner's token was a token that you could find under the +The project's runner token was a token that you could find under the project's **Settings > Pipelines** and was limited to access only that project. -It could be used for registering new specific Runners assigned to the project +It could be used for registering new specific runners assigned to the project and to checkout project sources. It could also be used with the GitLab Container Registry for that project, allowing pulling and pushing Docker images from within the CI job. @@ -123,7 +123,7 @@ Using single token had multiple security implications: - The token would be readable to anyone who had Developer access to a project that could run CI jobs, allowing the developer to register any specific - Runner for that project. + runner for that project. - The token would allow to access only the project's sources, forbidding from accessing any other projects. - The token was not expiring and was multi-purpose: used for checking out sources, @@ -205,7 +205,7 @@ Container Registries for private projects. > > - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes > for permissions. This makes the `image:` directive not work with private -> projects automatically and it needs to be configured manually on Runner's host +> projects automatically and it needs to be configured manually on the GitLab Runner host > with a predefined account (for example administrator's personal account with > access token created explicitly for this purpose). This issue is resolved with > latest changes in GitLab Runner 1.8 which receives GitLab credentials with diff --git a/doc/user/project/pages/getting_started/pages_from_scratch.md b/doc/user/project/pages/getting_started/pages_from_scratch.md index dc76e7165e8..a7eb4c4019f 100644 --- a/doc/user/project/pages/getting_started/pages_from_scratch.md +++ b/doc/user/project/pages/getting_started/pages_from_scratch.md @@ -7,8 +7,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Create a GitLab Pages website from scratch This tutorial shows you how to create a Pages site from scratch. You will start with -a blank project and create your own CI file, which gives instruction to the -[GitLab Runner](https://docs.gitlab.com/runner/). When your CI/CD +a blank project and create your own CI file, which gives instruction to +a [runner](https://docs.gitlab.com/runner/). When your CI/CD [pipeline](../../../../ci/pipelines/index.md) runs, the Pages site is created. This example uses the [Jekyll](https://jekyllrb.com/) Static Site Generator (SSG). @@ -48,7 +48,7 @@ Create three files in the root (top-level) directory. ## Choose a Docker image -In this example, the Runner uses a [Docker image](../../../../ci/docker/using_docker_images.md) +In this example, the runner uses a [Docker image](../../../../ci/docker/using_docker_images.md) to run scripts and deploy the site. This specific Ruby image is maintained on [DockerHub](https://hub.docker.com/_/ruby). @@ -93,7 +93,7 @@ job: ``` For GitLab Pages, this `job` has a specific name, called `pages`. -This setting tells the Runner you want the job to deploy your website +This setting tells the runner you want the job to deploy your website with GitLab Pages: ```yaml @@ -122,7 +122,7 @@ pages: ## Specify the `public` directory for artifacts Now that Jekyll has output the files to the `public` directory, -the Runner needs to know where to get them. The artifacts are stored +the runner needs to know where to get them. The artifacts are stored in the `public` directory: ```yaml diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md index b497e6ae563..fbb8b490bb0 100644 --- a/doc/user/project/pages/introduction.md +++ b/doc/user/project/pages/introduction.md @@ -34,7 +34,7 @@ If you are using [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlabcom) to hos - The domain name for GitLab Pages on GitLab.com is `gitlab.io`. - Custom domains and TLS support are enabled. - Shared runners are enabled by default, provided for free and can be used to - build your website. If you want you can still bring your own Runner. + build your website. If you want you can still bring your own runner. ## Example projects diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md index c10d03ed5aa..597d8dfe3f1 100644 --- a/doc/user/project/settings/project_access_tokens.md +++ b/doc/user/project/settings/project_access_tokens.md @@ -54,7 +54,7 @@ When the project access token is [revoked](#revoking-a-project-access-token) the records will be moved to a system-wide user with the username "Ghost User". For more information, see [Associated Records](../../profile/account/delete_account.md#associated-records). -Project bot users are a [GitLab-created service account](../../../subscriptions/index.md#self-managed), but count as a licensed seat. +Project bot users are a [GitLab-created service account](../../../subscriptions/self_managed/index.md#choose-the-number-of-users), but count as a licensed seat. These users will not count against your licensed seat in the future when [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/223695) is resolved. ## Revoking a project access token diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 668e833072b..fe3915db883 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10357,6 +10357,9 @@ msgstr "" msgid "Expand dropdown" msgstr "" +msgid "Expand file" +msgstr "" + msgid "Expand milestones" msgstr "" @@ -25730,6 +25733,9 @@ msgstr "" msgid "This field is required." msgstr "" +msgid "This file is collapsed." +msgstr "" + msgid "This group" msgstr "" diff --git a/qa/qa/resource/project.rb b/qa/qa/resource/project.rb index a6bc24fdd04..0025ebb2fd5 100644 --- a/qa/qa/resource/project.rb +++ b/qa/qa/resource/project.rb @@ -12,6 +12,8 @@ module QA attr_accessor :repository_storage # requires admin access attr_writer :initialize_with_readme attr_writer :auto_devops_enabled + attr_writer :github_personal_access_token + attr_writer :github_repository_path attribute :default_branch attribute :id @@ -22,6 +24,7 @@ module QA attribute :runners_token attribute :visibility attribute :template_name + attribute :import attribute :group do Group.fabricate! @@ -57,6 +60,7 @@ module QA @auto_devops_enabled = false @visibility = :public @template_name = nil + @import = false self.name = "the_awesome_project" end @@ -66,6 +70,8 @@ module QA end def fabricate! + return if @import + unless @standalone group.visit! Page::Group::Show.perform(&:go_to_new_project) diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb index e5ecaeae139..df28d63b113 100644 --- a/qa/qa/resource/project_imported_from_github.rb +++ b/qa/qa/resource/project_imported_from_github.rb @@ -4,28 +4,25 @@ require 'securerandom' module QA module Resource - class ProjectImportedFromGithub < Base - attr_accessor :name - attr_writer :personal_access_token, :github_repository_path - - attribute :group do - Group.fabricate! - end - + class ProjectImportedFromGithub < Resource::Project def fabricate! + super + group.visit! Page::Group::Show.perform(&:go_to_new_project) - - Page::Project::New.perform(&:click_import_project) - + go_to_import_tab Page::Project::New.perform(&:click_github_link) Page::Project::Import::Github.perform do |import_page| - import_page.add_personal_access_token(@personal_access_token) + import_page.add_personal_access_token(@github_personal_access_token) import_page.import!(@github_repository_path, @name) end end + + def go_to_import_tab + Page::Project::New.perform(&:click_import_project) + end end end end diff --git a/spec/features/merge_request/user_expands_diff_spec.rb b/spec/features/merge_request/user_expands_diff_spec.rb index fc17ef011c2..b0cfa8d0d54 100644 --- a/spec/features/merge_request/user_expands_diff_spec.rb +++ b/spec/features/merge_request/user_expands_diff_spec.rb @@ -17,11 +17,11 @@ RSpec.describe 'User expands diff', :js do it 'allows user to expand diff' do page.within find('[id="19763941ab80e8c09871c0a425f0560d9053bcb3"]') do - click_link 'Click to expand it.' + find('[data-testid="expandButton"]').click wait_for_requests - expect(page).not_to have_content('Click to expand it.') + expect(page).not_to have_content('Expand File') expect(page).to have_selector('.code') end end diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb index e38f9e3b142..7a3a14e61e3 100644 --- a/spec/features/merge_request/user_sees_diff_spec.rb +++ b/spec/features/merge_request/user_sees_diff_spec.rb @@ -68,7 +68,7 @@ RSpec.describe 'Merge request > User sees diff', :js do end context 'as user who needs to fork' do - it 'shows fork/cancel confirmation', :sidekiq_might_not_need_inline, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/196749' do + it 'shows fork/cancel confirmation', :sidekiq_might_not_need_inline do sign_in(user) visit diffs_project_merge_request_path(project, merge_request) diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js index 0b0a7f966c5..3c39dd2d385 100644 --- a/spec/frontend/diffs/components/diff_file_spec.js +++ b/spec/frontend/diffs/components/diff_file_spec.js @@ -90,8 +90,8 @@ describe('DiffFile', () => { vm.isCollapsed = true; vm.$nextTick(() => { - expect(vm.$el.innerText).toContain('This diff is collapsed'); - expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1); + expect(vm.$el.innerText).toContain('This file is collapsed.'); + expect(vm.$el.querySelector('[data-testid="expandButton"]')).not.toBeFalsy(); done(); }); @@ -102,8 +102,8 @@ describe('DiffFile', () => { vm.isCollapsed = true; vm.$nextTick(() => { - expect(vm.$el.innerText).toContain('This diff is collapsed'); - expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1); + expect(vm.$el.innerText).toContain('This file is collapsed.'); + expect(vm.$el.querySelector('[data-testid="expandButton"]')).not.toBeFalsy(); done(); }); @@ -121,8 +121,8 @@ describe('DiffFile', () => { vm.isCollapsed = true; vm.$nextTick(() => { - expect(vm.$el.innerText).toContain('This diff is collapsed'); - expect(vm.$el.querySelectorAll('.js-click-to-expand').length).toEqual(1); + expect(vm.$el.innerText).toContain('This file is collapsed.'); + expect(vm.$el.querySelector('[data-testid="expandButton"]')).not.toBeFalsy(); done(); }); @@ -135,7 +135,7 @@ describe('DiffFile', () => { vm.file.viewer.name = diffViewerModes.renamed; vm.$nextTick(() => { - expect(vm.$el.innerText).not.toContain('This diff is collapsed'); + expect(vm.$el.innerText).not.toContain('This file is collapsed.'); done(); }); @@ -148,7 +148,7 @@ describe('DiffFile', () => { vm.file.viewer.name = diffViewerModes.mode_changed; vm.$nextTick(() => { - expect(vm.$el.innerText).not.toContain('This diff is collapsed'); + expect(vm.$el.innerText).not.toContain('This file is collapsed.'); done(); }); diff --git a/spec/frontend/import_projects/components/import_projects_table_spec.js b/spec/frontend/import_projects/components/import_projects_table_spec.js index bcb5a3a2231..1dbad588ec4 100644 --- a/spec/frontend/import_projects/components/import_projects_table_spec.js +++ b/spec/frontend/import_projects/components/import_projects_table_spec.js @@ -1,13 +1,12 @@ import { nextTick } from 'vue'; import Vuex from 'vuex'; import { createLocalVue, shallowMount } from '@vue/test-utils'; -import { GlLoadingIcon, GlButton } from '@gitlab/ui'; +import { GlLoadingIcon, GlButton, GlIntersectionObserver } from '@gitlab/ui'; import state from '~/import_projects/store/state'; import * as getters from '~/import_projects/store/getters'; import { STATUSES } from '~/import_projects/constants'; import ImportProjectsTable from '~/import_projects/components/import_projects_table.vue'; import ProviderRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue'; -import PageQueryParamSync from '~/import_projects/components/page_query_param_sync.vue'; describe('ImportProjectsTable', () => { let wrapper; @@ -35,6 +34,7 @@ describe('ImportProjectsTable', () => { const importAllFn = jest.fn(); const importAllModalShowFn = jest.fn(); const setPageFn = jest.fn(); + const fetchReposFn = jest.fn(); function createComponent({ state: initialState, @@ -53,7 +53,7 @@ describe('ImportProjectsTable', () => { ...customGetters, }, actions: { - fetchRepos: jest.fn(), + fetchRepos: fetchReposFn, fetchJobs: jest.fn(), fetchNamespaces: jest.fn(), importAll: importAllFn, @@ -203,21 +203,29 @@ describe('ImportProjectsTable', () => { }); }); - it('passes current page to page-query-param-sync component', () => { - expect(wrapper.find(PageQueryParamSync).props().page).toBe(pageInfo.page); + it('does not call fetchRepos on mount', () => { + expect(fetchReposFn).not.toHaveBeenCalled(); }); - it('dispatches setPage when page-query-param-sync emits popstate', () => { - const NEW_PAGE = 2; - wrapper.find(PageQueryParamSync).vm.$emit('popstate', NEW_PAGE); + it('renders intersection observer component', () => { + expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true); + }); + + it('calls fetchRepos when intersection observer appears', async () => { + wrapper.find(GlIntersectionObserver).vm.$emit('appear'); - const { calls } = setPageFn.mock; + await nextTick(); - expect(calls).toHaveLength(1); - expect(calls[0][1]).toBe(NEW_PAGE); + expect(fetchReposFn).toHaveBeenCalled(); }); }); + it('calls fetchRepos on mount', () => { + createComponent(); + + expect(fetchReposFn).toHaveBeenCalled(); + }); + it.each` hasIncompatibleRepos | shouldRenderSlot | action ${false} | ${false} | ${'does not render'} diff --git a/spec/frontend/import_projects/components/page_query_param_sync_spec.js b/spec/frontend/import_projects/components/page_query_param_sync_spec.js deleted file mode 100644 index be19ecca1ba..00000000000 --- a/spec/frontend/import_projects/components/page_query_param_sync_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import { TEST_HOST } from 'helpers/test_constants'; - -import PageQueryParamSync from '~/import_projects/components/page_query_param_sync.vue'; - -describe('PageQueryParamSync', () => { - let originalPushState; - let originalAddEventListener; - let originalRemoveEventListener; - - const pushStateMock = jest.fn(); - const addEventListenerMock = jest.fn(); - const removeEventListenerMock = jest.fn(); - - beforeAll(() => { - window.location.search = ''; - originalPushState = window.pushState; - - window.history.pushState = pushStateMock; - - originalAddEventListener = window.addEventListener; - window.addEventListener = addEventListenerMock; - - originalRemoveEventListener = window.removeEventListener; - window.removeEventListener = removeEventListenerMock; - }); - - afterAll(() => { - window.history.pushState = originalPushState; - window.addEventListener = originalAddEventListener; - window.removeEventListener = originalRemoveEventListener; - }); - - let wrapper; - beforeEach(() => { - wrapper = shallowMount(PageQueryParamSync, { - propsData: { page: 3 }, - }); - }); - - afterEach(() => { - wrapper.destroy(); - }); - - it('calls push state with page number when page is updated and differs from 1', async () => { - wrapper.setProps({ page: 2 }); - - await nextTick(); - - const { calls } = pushStateMock.mock; - expect(calls).toHaveLength(1); - expect(calls[0][2]).toBe(`${TEST_HOST}/?page=2`); - }); - - it('calls push state without page number when page is updated and is 1', async () => { - wrapper.setProps({ page: 1 }); - - await nextTick(); - - const { calls } = pushStateMock.mock; - expect(calls).toHaveLength(1); - expect(calls[0][2]).toBe(`${TEST_HOST}/`); - }); - - it('subscribes to popstate event on create', () => { - expect(addEventListenerMock).toHaveBeenCalledWith('popstate', expect.any(Function)); - }); - - it('unsubscribes from popstate event when destroyed', () => { - const [, fn] = addEventListenerMock.mock.calls[0]; - - wrapper.destroy(); - - expect(removeEventListenerMock).toHaveBeenCalledWith('popstate', fn); - }); - - it('emits popstate event when popstate is triggered', async () => { - const [, fn] = addEventListenerMock.mock.calls[0]; - - delete window.location; - window.location = new URL(`${TEST_HOST}/?page=5`); - fn(); - - expect(wrapper.emitted().popstate[0]).toStrictEqual([5]); - }); -}); diff --git a/spec/frontend/import_projects/store/actions_spec.js b/spec/frontend/import_projects/store/actions_spec.js index 45a59b3f6d6..6951f2bf04d 100644 --- a/spec/frontend/import_projects/store/actions_spec.js +++ b/spec/frontend/import_projects/store/actions_spec.js @@ -83,7 +83,7 @@ describe('import_projects store actions', () => { afterEach(() => mock.restore()); - it('dispatches stopJobsPolling actions and commits REQUEST_REPOS, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => { + it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_SUCCESS mutations on a successful request', () => { mock.onGet(MOCK_ENDPOINT).reply(200, payload); return testAction( @@ -91,54 +91,65 @@ describe('import_projects store actions', () => { null, localState, [ + { type: SET_PAGE, payload: 1 }, { type: REQUEST_REPOS }, { type: RECEIVE_REPOS_SUCCESS, payload: convertObjectPropsToCamelCase(payload, { deep: true }), }, ], - [{ type: 'stopJobsPolling' }, { type: 'fetchJobs' }], + [], ); }); - it('dispatches stopJobsPolling action and commits REQUEST_REPOS, RECEIVE_REPOS_ERROR mutations on an unsuccessful request', () => { + it('commits SET_PAGE, REQUEST_REPOS, RECEIVE_REPOS_ERROR and SET_PAGE again mutations on an unsuccessful request', () => { mock.onGet(MOCK_ENDPOINT).reply(500); return testAction( fetchRepos, null, localState, - [{ type: REQUEST_REPOS }, { type: RECEIVE_REPOS_ERROR }], - [{ type: 'stopJobsPolling' }], + [ + { type: SET_PAGE, payload: 1 }, + { type: REQUEST_REPOS }, + { type: SET_PAGE, payload: 0 }, + { type: RECEIVE_REPOS_ERROR }, + ], + [], ); }); - describe('when pagination is enabled', () => { - it('includes page in url query params', async () => { - const { fetchRepos: fetchReposWithPagination } = actionsFactory({ - endpoints, - hasPagination: true, - }); + it('includes page in url query params', async () => { + let requestedUrl; + mock.onGet().reply(config => { + requestedUrl = config.url; + return [200, payload]; + }); - let requestedUrl; - mock.onGet().reply(config => { - requestedUrl = config.url; - return [200, payload]; - }); + const localStateWithPage = { ...localState, pageInfo: { page: 2 } }; - await testAction( - fetchReposWithPagination, - null, - localState, - expect.any(Array), - expect.any(Array), - ); + await testAction(fetchRepos, null, localStateWithPage, expect.any(Array), expect.any(Array)); - expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localState.pageInfo.page}`); - }); + expect(requestedUrl).toBe(`${MOCK_ENDPOINT}?page=${localStateWithPage.pageInfo.page + 1}`); }); - describe('when filtered', () => { + it('correctly updates current page on an unsuccessful request', () => { + mock.onGet(MOCK_ENDPOINT).reply(500); + const CURRENT_PAGE = 5; + + return testAction( + fetchRepos, + null, + { ...localState, pageInfo: { page: CURRENT_PAGE } }, + expect.arrayContaining([ + { type: SET_PAGE, payload: CURRENT_PAGE + 1 }, + { type: SET_PAGE, payload: CURRENT_PAGE }, + ]), + [], + ); + }); + + describe('when /home/xanf/projects/gdk/gitlab/spec/frontend/import_projects/store/actions_spec.jsfiltered', () => { it('fetches repos with filter applied', () => { mock.onGet(`${TEST_HOST}/endpoint.json?filter=filter`).reply(200, payload); @@ -147,13 +158,14 @@ describe('import_projects store actions', () => { null, { ...localState, filter: 'filter' }, [ + { type: SET_PAGE, payload: 1 }, { type: REQUEST_REPOS }, { type: RECEIVE_REPOS_SUCCESS, payload: convertObjectPropsToCamelCase(payload, { deep: true }), }, ], - [{ type: 'stopJobsPolling' }, { type: 'fetchJobs' }], + [], ); }); }); diff --git a/spec/frontend/import_projects/store/mutations_spec.js b/spec/frontend/import_projects/store/mutations_spec.js index 7b85e1a5298..5d78a7fa9e7 100644 --- a/spec/frontend/import_projects/store/mutations_spec.js +++ b/spec/frontend/import_projects/store/mutations_spec.js @@ -1,9 +1,11 @@ import * as types from '~/import_projects/store/mutation_types'; import mutations from '~/import_projects/store/mutations'; +import getInitialState from '~/import_projects/store/state'; import { STATUSES } from '~/import_projects/constants'; describe('import_projects store mutations', () => { let state; + const SOURCE_PROJECT = { id: 1, full_name: 'full/name', @@ -19,13 +21,23 @@ describe('import_projects store mutations', () => { }; describe(`${types.SET_FILTER}`, () => { - it('overwrites current filter value', () => { - state = { filter: 'some-value' }; - const NEW_VALUE = 'new-value'; + const NEW_VALUE = 'new-value'; + beforeEach(() => { + state = { + filter: 'some-value', + repositories: ['some', ' repositories'], + pageInfo: { page: 1 }, + }; mutations[types.SET_FILTER](state, NEW_VALUE); + }); - expect(state.filter).toBe(NEW_VALUE); + it('removes current repositories list', () => { + expect(state.repositories.length).toBe(0); + }); + + it('resets current page to 0', () => { + expect(state.pageInfo.page).toBe(0); }); }); @@ -48,7 +60,7 @@ describe('import_projects store mutations', () => { }; it('recreates importSource from response', () => { - state = {}; + state = getInitialState(); mutations[types.RECEIVE_REPOS_SUCCESS](state, response); @@ -62,7 +74,7 @@ describe('import_projects store mutations', () => { }); it('passes project to importProject', () => { - state = {}; + state = getInitialState(); mutations[types.RECEIVE_REPOS_SUCCESS](state, response); @@ -74,7 +86,8 @@ describe('import_projects store mutations', () => { describe('for importable projects', () => { beforeEach(() => { - state = {}; + state = getInitialState(); + const response = { importedProjects: [], providerRepos: [SOURCE_PROJECT], @@ -95,7 +108,7 @@ describe('import_projects store mutations', () => { }; beforeEach(() => { - state = {}; + state = getInitialState(); mutations[types.RECEIVE_REPOS_SUCCESS](state, response); }); @@ -115,7 +128,8 @@ describe('import_projects store mutations', () => { importedProjects: [], providerRepos: [], }; - state = {}; + + state = getInitialState(); mutations[types.RECEIVE_REPOS_SUCCESS](state, response); @@ -125,16 +139,18 @@ describe('import_projects store mutations', () => { it('passes response as it is', () => { const response = []; - state = {}; + state = getInitialState(); mutations[types.RECEIVE_REPOS_SUCCESS](state, response); - expect(state.repositories).toBe(response); + expect(state.repositories).toStrictEqual(response); }); it('sets repos loading flag to false', () => { const response = []; + state = getInitialState(); + mutations[types.RECEIVE_REPOS_SUCCESS](state, response); expect(state.isLoadingRepos).toBe(false); @@ -143,7 +159,7 @@ describe('import_projects store mutations', () => { describe(`${types.RECEIVE_REPOS_ERROR}`, () => { it('sets repos loading flag to false', () => { - state = {}; + state = getInitialState(); mutations[types.RECEIVE_REPOS_ERROR](state); @@ -291,17 +307,6 @@ describe('import_projects store mutations', () => { }); }); - describe(`${types.SET_PAGE_INFO}`, () => { - it('sets passed page info', () => { - state = {}; - const pageInfo = { page: 1, total: 10 }; - - mutations[types.SET_PAGE_INFO](state, pageInfo); - - expect(state.pageInfo).toBe(pageInfo); - }); - }); - describe(`${types.SET_PAGE}`, () => { it('sets page number', () => { const NEW_PAGE = 4; diff --git a/spec/models/ci_platform_metric_spec.rb b/spec/models/ci_platform_metric_spec.rb index b421518ce3b..0b00875df43 100644 --- a/spec/models/ci_platform_metric_spec.rb +++ b/spec/models/ci_platform_metric_spec.rb @@ -46,44 +46,45 @@ RSpec.describe CiPlatformMetric do it 'inserts platform target counts for that day' do Timecop.freeze(today) do - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'aws') - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'aws') - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'fargate') - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'fargate') - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'fargate') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'ECS') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'ECS') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'FARGATE') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'FARGATE') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'FARGATE') described_class.insert_auto_devops_platform_targets! end Timecop.freeze(tomorrow) do - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'fargate') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'FARGATE') described_class.insert_auto_devops_platform_targets! end expect(platform_target_counts_by_day).to eq({ - today.to_date => { 'aws' => 2, 'fargate' => 3 }, - tomorrow.to_date => { 'aws' => 2, 'fargate' => 4 } + today.to_date => { 'ECS' => 2, 'FARGATE' => 3 }, + tomorrow.to_date => { 'ECS' => 2, 'FARGATE' => 4 } }) end end - context 'when there are ci variable values too long for platform_target' do + context 'when there are invalid ci variable values for platform_target' do let(:today) { Time.zone.local(1982, 4, 24) } - it 'truncates those values' do - max = described_class::PLATFORM_TARGET_MAX_LENGTH + it 'ignores those values' do Timecop.freeze(today) do - create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'F' * (max + 1)) + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'ECS') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'FOO') + create(:ci_variable, key: described_class::CI_VARIABLE_KEY, value: 'BAR') described_class.insert_auto_devops_platform_targets! end expect(platform_target_counts_by_day).to eq({ - today.to_date => { 'F' * max => 1 } + today.to_date => { 'ECS' => 1 } }) end end context 'when there are no platform target variables' do it 'does not generate any new platform metrics' do - create(:ci_variable, key: 'KEY_WHATEVER', value: 'aws') + create(:ci_variable, key: 'KEY_WHATEVER', value: 'ECS') described_class.insert_auto_devops_platform_targets! expect(platform_target_counts_by_day).to eq({}) |