diff options
35 files changed, 1210 insertions, 222 deletions
diff --git a/.gitlab/issue_templates/Broken Master - Flaky.md b/.gitlab/issue_templates/Broken Master - Flaky.md index 6b56845ba8c..d90abe2cc33 100644 --- a/.gitlab/issue_templates/Broken Master - Flaky.md +++ b/.gitlab/issue_templates/Broken Master - Flaky.md @@ -22,4 +22,4 @@ Please read the below documentations for a workflow of triaging and resolving br Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master. -/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::2" +/label ~"failure::flaky-test" ~"Engineering Productivity" ~"priority::2" ~"severity::3" ~"type::bug" ~"bug::transient" diff --git a/.gitlab/issue_templates/Broken Master - Non-flaky.md b/.gitlab/issue_templates/Broken Master - Non-flaky.md index 97a34aa759d..43e73fc5c5a 100644 --- a/.gitlab/issue_templates/Broken Master - Non-flaky.md +++ b/.gitlab/issue_templates/Broken Master - Non-flaky.md @@ -21,4 +21,4 @@ Please read the below documentations for a workflow of triaging and resolving br Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master. -/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" +/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" ~"type::bug" ~"bug::transient" diff --git a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue index 6e24ac6b8d4..a4ef7827f73 100644 --- a/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue +++ b/app/assets/javascripts/ci/pipeline_schedules/components/pipeline_schedules_form.vue @@ -1,18 +1,321 @@ <script> -import { GlForm } from '@gitlab/ui'; +import { + GlButton, + GlDropdown, + GlDropdownItem, + GlFormCheckbox, + GlForm, + GlFormGroup, + GlFormInput, + GlFormTextarea, + GlLink, + GlSprintf, +} from '@gitlab/ui'; +import { uniqueId } from 'lodash'; +import Vue from 'vue'; +import { __, s__ } from '~/locale'; +import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants'; +import RefSelector from '~/ref/components/ref_selector.vue'; +import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue'; +import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue'; +import { VARIABLE_TYPE, FILE_TYPE } from '../constants'; export default { components: { + GlButton, + GlDropdown, + GlDropdownItem, GlForm, + GlFormCheckbox, + GlFormGroup, + GlFormInput, + GlFormTextarea, + GlLink, + GlSprintf, + RefSelector, + TimezoneDropdown, + IntervalPatternInput, }, - inject: { - fullPath: { + inject: [ + 'fullPath', + 'projectId', + 'defaultBranch', + 'cron', + 'cronTimezone', + 'dailyLimit', + 'settingsLink', + ], + props: { + timezoneData: { + type: Array, + required: true, + }, + refParam: { + type: String, + required: false, default: '', }, }, + data() { + return { + refValue: { + shortName: this.refParam, + // this is needed until we add support for ref type in url query strings + // ensure default branch is called with full ref on load + // https://gitlab.com/gitlab-org/gitlab/-/issues/287815 + fullName: this.refParam === this.defaultBranch ? `refs/heads/${this.refParam}` : undefined, + }, + description: '', + scheduleRef: this.defaultBranch, + activated: true, + timezone: this.cronTimezone, + formCiVariables: {}, + // TODO: Add the GraphQL query to help populate the predefined variables + // app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue#131 + predefinedValueOptions: {}, + }; + }, + i18n: { + activated: __('Activated'), + cronTimezone: s__('PipelineSchedules|Cron timezone'), + description: s__('PipelineSchedules|Description'), + shortDescriptionPipeline: s__( + 'PipelineSchedules|Provide a short description for this pipeline', + ), + savePipelineSchedule: s__('PipelineSchedules|Save pipeline schedule'), + cancel: __('Cancel'), + targetBranchTag: __('Select target branch or tag'), + intervalPattern: s__('PipelineSchedules|Interval Pattern'), + variablesDescription: s__( + 'Pipeline|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used by default.', + ), + removeVariableLabel: s__('CiVariables|Remove variable'), + variables: s__('Pipeline|Variables'), + }, + typeOptions: { + [VARIABLE_TYPE]: __('Variable'), + [FILE_TYPE]: __('File'), + }, + formElementClasses: 'gl-md-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0', + computed: { + dropdownTranslations() { + return { + dropdownHeader: this.$options.i18n.targetBranchTag, + }; + }, + refFullName() { + return this.refValue.fullName; + }, + variables() { + return this.formCiVariables[this.refFullName]?.variables ?? []; + }, + descriptions() { + return this.formCiVariables[this.refFullName]?.descriptions ?? {}; + }, + typeOptionsListbox() { + return [ + { + text: __('Variable'), + value: VARIABLE_TYPE, + }, + { + text: __('File'), + value: FILE_TYPE, + }, + ]; + }, + getEnabledRefTypes() { + return [REF_TYPE_BRANCHES, REF_TYPE_TAGS]; + }, + }, + created() { + Vue.set(this.formCiVariables, this.refFullName, { + variables: [], + descriptions: {}, + }); + + this.addEmptyVariable(this.refFullName); + }, + methods: { + addEmptyVariable(refValue) { + const { variables } = this.formCiVariables[refValue]; + + const lastVar = variables[variables.length - 1]; + if (lastVar?.key === '' && lastVar?.value === '') { + return; + } + + variables.push({ + uniqueId: uniqueId(`var-${refValue}`), + variable_type: VARIABLE_TYPE, + key: '', + value: '', + }); + }, + setVariableAttribute(key, attribute, value) { + const { variables } = this.formCiVariables[this.refFullName]; + const variable = variables.find((v) => v.key === key); + variable[attribute] = value; + }, + shouldShowValuesDropdown(key) { + return this.predefinedValueOptions[key]?.length > 1; + }, + removeVariable(index) { + this.variables.splice(index, 1); + }, + canRemove(index) { + return index < this.variables.length - 1; + }, + }, }; </script> <template> - <gl-form /> + <div class="col-lg-8"> + <gl-form> + <!--Description--> + <gl-form-group :label="$options.i18n.description" label-for="schedule-description"> + <gl-form-input + id="schedule-description" + v-model="description" + type="text" + :placeholder="$options.i18n.shortDescriptionPipeline" + data-testid="schedule-description" + /> + </gl-form-group> + <!--Interval Pattern--> + <gl-form-group :label="$options.i18n.intervalPattern" label-for="schedule-interval"> + <interval-pattern-input + id="schedule-interval" + :initial-cron-interval="cron" + :daily-limit="dailyLimit" + :send-native-errors="false" + /> + </gl-form-group> + <!--Timezone--> + <gl-form-group :label="$options.i18n.cronTimezone" label-for="schedule-timezone"> + <timezone-dropdown + id="schedule-timezone" + :value="timezone" + :timezone-data="timezoneData" + name="schedule-timezone" + /> + </gl-form-group> + <!--Branch/Tag Selector--> + <gl-form-group :label="$options.i18n.targetBranchTag" label-for="schedule-target-branch-tag"> + <ref-selector + id="schedule-target-branch-tag" + :enabled-ref-types="getEnabledRefTypes" + :project-id="projectId" + :value="scheduleRef" + :use-symbolic-ref-names="true" + :translations="dropdownTranslations" + class="gl-w-full" + /> + </gl-form-group> + <!--Variable List--> + <gl-form-group :label="$options.i18n.variables"> + <div + v-for="(variable, index) in variables" + :key="variable.uniqueId" + class="gl-mb-3 gl-pb-2" + data-testid="ci-variable-row" + data-qa-selector="ci_variable_row_container" + > + <div + class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row" + > + <gl-dropdown + :text="$options.typeOptions[variable.variable_type]" + :class="$options.formElementClasses" + data-testid="pipeline-form-ci-variable-type" + > + <gl-dropdown-item + v-for="type in Object.keys($options.typeOptions)" + :key="type" + @click="setVariableAttribute(variable.key, 'variable_type', type)" + > + {{ $options.typeOptions[type] }} + </gl-dropdown-item> + </gl-dropdown> + <gl-form-input + v-model="variable.key" + :placeholder="s__('CiVariables|Input variable key')" + :class="$options.formElementClasses" + data-testid="pipeline-form-ci-variable-key" + data-qa-selector="ci_variable_key_field" + @change="addEmptyVariable(refFullName)" + /> + <gl-dropdown + v-if="shouldShowValuesDropdown(variable.key)" + :text="variable.value" + :class="$options.formElementClasses" + class="gl-flex-grow-1 gl-mr-0!" + data-testid="pipeline-form-ci-variable-value-dropdown" + > + <gl-dropdown-item + v-for="value in predefinedValueOptions[variable.key]" + :key="value" + data-testid="pipeline-form-ci-variable-value-dropdown-items" + @click="setVariableAttribute(variable.key, 'value', value)" + > + {{ value }} + </gl-dropdown-item> + </gl-dropdown> + <gl-form-textarea + v-else + v-model="variable.value" + :placeholder="s__('CiVariables|Input variable value')" + class="gl-mb-3 gl-h-7!" + :style="$options.textAreaStyle" + :no-resize="false" + data-testid="pipeline-form-ci-variable-value" + data-qa-selector="ci_variable_value_field" + /> + + <template v-if="variables.length > 1"> + <gl-button + v-if="canRemove(index)" + class="gl-md-ml-3 gl-mb-3" + data-testid="remove-ci-variable-row" + variant="danger" + category="secondary" + icon="clear" + :aria-label="$options.i18n.removeVariableLabel" + @click="removeVariable(index)" + /> + <gl-button + v-else + class="gl-md-ml-3 gl-mb-3 gl-display-none gl-md-display-block gl-visibility-hidden" + icon="clear" + :aria-label="$options.i18n.removeVariableLabel" + /> + </template> + </div> + <div v-if="descriptions[variable.key]" class="gl-text-gray-500 gl-mb-3"> + {{ descriptions[variable.key] }} + </div> + </div> + + <template #description + ><gl-sprintf :message="$options.i18n.variablesDescription"> + <template #link="{ content }"> + <gl-link :href="settingsLink">{{ content }}</gl-link> + </template> + </gl-sprintf></template + > + </gl-form-group> + <!--Activated--> + <gl-form-checkbox id="schedule-active" v-model="activated" class="gl-mb-3">{{ + $options.i18n.activated + }}</gl-form-checkbox> + + <gl-button type="submit" variant="confirm" data-testid="schedule-submit-button">{{ + $options.i18n.savePipelineSchedule + }}</gl-button> + <gl-button type="reset" data-testid="schedule-cancel-button">{{ + $options.i18n.cancel + }}</gl-button> + </gl-form> + </div> </template> diff --git a/app/assets/javascripts/ci/pipeline_schedules/constants.js b/app/assets/javascripts/ci/pipeline_schedules/constants.js new file mode 100644 index 00000000000..b4ab1143f60 --- /dev/null +++ b/app/assets/javascripts/ci/pipeline_schedules/constants.js @@ -0,0 +1,2 @@ +export const VARIABLE_TYPE = 'env_var'; +export const FILE_TYPE = 'file'; diff --git a/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js b/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js index d83417ab84a..445161f99cb 100644 --- a/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js +++ b/app/assets/javascripts/ci/pipeline_schedules/mount_pipeline_schedules_form_app.js @@ -16,7 +16,16 @@ export default (selector) => { return false; } - const { fullPath } = containerEl.dataset; + const { + fullPath, + cron, + dailyLimit, + timezoneData, + cronTimezone, + projectId, + defaultBranch, + settingsLink, + } = containerEl.dataset; return new Vue({ el: containerEl, @@ -24,9 +33,20 @@ export default (selector) => { apolloProvider, provide: { fullPath, + projectId, + defaultBranch, + dailyLimit: dailyLimit ?? '', + cronTimezone: cronTimezone ?? '', + cron: cron ?? '', + settingsLink, }, render(createElement) { - return createElement(PipelineSchedulesForm); + return createElement(PipelineSchedulesForm, { + props: { + timezoneData: JSON.parse(timezoneData), + refParam: defaultBranch, + }, + }); }, }); }; diff --git a/app/assets/javascripts/ide/init_gitlab_web_ide.js b/app/assets/javascripts/ide/init_gitlab_web_ide.js index 8a6965b6415..5e25aa6de21 100644 --- a/app/assets/javascripts/ide/init_gitlab_web_ide.js +++ b/app/assets/javascripts/ide/init_gitlab_web_ide.js @@ -3,6 +3,7 @@ import { __ } from '~/locale'; import { cleanLeadingSeparator } from '~/lib/utils/url_utility'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_action'; import { createAndSubmitForm } from '~/lib/utils/create_and_submit_form'; +import csrf from '~/lib/utils/csrf'; import { getBaseConfig } from './lib/gitlab_web_ide/get_base_config'; import { setupRootElement } from './lib/gitlab_web_ide/setup_root_element'; import { GITLAB_WEB_IDE_FEEDBACK_ISSUE } from './constants'; @@ -27,9 +28,15 @@ export const initGitlabWebIDE = async (el) => { const rootEl = setupRootElement(el); + // See ClientOnlyConfig https://gitlab.com/gitlab-org/gitlab-web-ide/-/blob/main/packages/web-ide-types/src/config.ts#L17 start(rootEl, { ...getBaseConfig(), nonce, + // Use same headers as defined in axios_utils + httpHeaders: { + [csrf.headerKey]: csrf.token, + 'X-Requested-With': 'XMLHttpRequest', + }, projectPath, ref, links: { diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index 85443843684..fd8b1a6290f 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -39,6 +39,11 @@ export default { required: false, default: '', }, + sendNativeErrors: { + type: Boolean, + required: false, + default: true, + }, }, data() { return { @@ -114,9 +119,11 @@ export default { cronInterval() { // updates field validation state when model changes, as // glFieldError only updates on input. - this.$nextTick(() => { - gl.pipelineScheduleFieldErrors.updateFormValidityState(); - }); + if (this.sendNativeErrors) { + this.$nextTick(() => { + gl.pipelineScheduleFieldErrors.updateFormValidityState(); + }); + } }, radioValue: { immediate: true, diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js index 9ef1017f9f2..eb1f705eab9 100644 --- a/app/assets/javascripts/pages/projects/tags/new/index.js +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -1,8 +1,8 @@ import $ from 'jquery'; import GLForm from '~/gl_form'; -import RefSelectDropdown from '~/ref_select_dropdown'; import ZenMode from '~/zen_mode'; +import initNewTagRefSelector from '~/tags/init_new_tag_ref_selector'; +initNewTagRefSelector(); new ZenMode(); // eslint-disable-line no-new new GLForm($('.tag-form')); // eslint-disable-line no-new -new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new diff --git a/app/assets/javascripts/tags/init_new_tag_ref_selector.js b/app/assets/javascripts/tags/init_new_tag_ref_selector.js new file mode 100644 index 00000000000..11c7516f16c --- /dev/null +++ b/app/assets/javascripts/tags/init_new_tag_ref_selector.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import RefSelector from '~/ref/components/ref_selector.vue'; + +export default function initNewTagRefSelector() { + const el = document.querySelector('.js-new-tag-ref-selector'); + + if (el) { + const { projectId, defaultBranchName, hiddenInputName } = el.dataset; + // eslint-disable-next-line no-new + new Vue({ + el, + render(createComponent) { + return createComponent(RefSelector, { + props: { + value: defaultBranchName, + name: hiddenInputName, + projectId, + }, + }); + }, + }); + } +} diff --git a/app/graphql/resolvers/projects/fork_details_resolver.rb b/app/graphql/resolvers/projects/fork_details_resolver.rb new file mode 100644 index 00000000000..fcc13a1bc1e --- /dev/null +++ b/app/graphql/resolvers/projects/fork_details_resolver.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Resolvers + module Projects + class ForkDetailsResolver < BaseResolver + type Types::Projects::ForkDetailsType, null: true + + argument :ref, GraphQL::Types::String, + required: false, + description: 'Ref of the fork. Default value is HEAD.' + + alias_method :project, :object + + def resolve(**args) + return unless project.forked? + + ::Projects::Forks::DivergenceCounts.new(project, args[:ref]).counts + end + end + end +end diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 388e5689166..fe13ee7ef3c 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -539,6 +539,13 @@ module Types resolver: Resolvers::Projects::ForkTargetsResolver, description: 'Namespaces in which the current user can fork the project into.' + field :fork_details, Types::Projects::ForkDetailsType, + calls_gitaly: true, + alpha: { milestone: '15.7' }, + authorize: :read_code, + resolver: Resolvers::Projects::ForkDetailsResolver, + description: 'Details of the fork project compared to its upstream project.' + field :branch_rules, Types::Projects::BranchRuleType.connection_type, null: true, diff --git a/app/graphql/types/projects/fork_details_type.rb b/app/graphql/types/projects/fork_details_type.rb new file mode 100644 index 00000000000..88c17d89620 --- /dev/null +++ b/app/graphql/types/projects/fork_details_type.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Types + module Projects + # rubocop: disable Graphql/AuthorizeTypes + class ForkDetailsType < BaseObject + graphql_name 'ForkDetails' + description 'Details of the fork project compared to its upstream project.' + + field :ahead, GraphQL::Types::Int, + null: true, + description: 'Number of commits ahead of upstream.' + + field :behind, GraphQL::Types::Int, + null: true, + description: 'Number of commits behind upstream.' + end + # rubocop: enable Graphql/AuthorizeTypes + end +end diff --git a/app/views/projects/pipeline_schedules/new.html.haml b/app/views/projects/pipeline_schedules/new.html.haml index d3757d0e339..2d4ed5a9872 100644 --- a/app/views/projects/pipeline_schedules/new.html.haml +++ b/app/views/projects/pipeline_schedules/new.html.haml @@ -9,6 +9,6 @@ = _("Schedule a new pipeline") - if Feature.enabled?(:pipeline_schedules_vue, @project) - #pipeline-schedules-form-new{ data: { full_path: @project.full_path } } + #pipeline-schedules-form-new{ data: { full_path: @project.full_path, cron: @schedule.cron, daily_limit: @schedule.daily_limit, timezone_data: timezone_data.to_json, cron_timezone: @schedule.cron_timezone, project_id: @project.id, default_branch: @project.default_branch, settings_link: project_settings_ci_cd_path(@project), } } - else = render "form" diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index f48404c7407..2f8291d255f 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -20,14 +20,9 @@ = label_tag :tag_name, nil = text_field_tag :tag_name, params[:tag_name], required: true, autofocus: true, class: 'form-control', data: { qa_selector: "tag_name_field" } .form-group.row - .col-sm-12.create-from + .col-sm-auto.create-from = label_tag :ref, 'Create from' - .dropdown - = hidden_field_tag :ref, default_ref - = button_tag type: 'button', title: default_ref, class: 'dropdown-menu-toggle wide js-branch-select monospace', required: true, data: { toggle: 'dropdown', selected: default_ref, field_name: 'ref' } do - .text-left.dropdown-toggle-text= default_ref - = sprite_icon('chevron-down', css_class: 'dropdown-menu-toggle-icon gl-top-3') - = render 'shared/ref_dropdown', dropdown_class: 'wide' + .js-new-tag-ref-selector{ data: { project_id: @project.id, default_branch_name: default_ref, hidden_input_name: 'ref' } } .form-text.text-muted = s_('TagsPage|Existing branch name, tag, or commit SHA') .form-group.row @@ -42,5 +37,4 @@ = s_('TagsPage|Create tag') = render Pajamas::ButtonComponent.new(href: project_tags_path(@project)) do = s_('TagsPage|Cancel') --# haml-lint:disable InlineJavaScript -%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe + diff --git a/data/deprecations/15-7-enable-period-in-terraform-state-name.yml b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml new file mode 100644 index 00000000000..e9db80ea34c --- /dev/null +++ b/data/deprecations/15-7-enable-period-in-terraform-state-name.yml @@ -0,0 +1,25 @@ +- title: "Support for periods (`.`) in Terraform state names might break existing states" + announcement_milestone: "15.7" + announcement_date: "2022-12-22" + removal_milestone: "16.0" + removal_date: "2023-05-22" + breaking_change: true + reporter: nagyv-gitlab + stage: configure + issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/385564 + body: | + Previously, Terraform state names containing periods were not supported. However, you could still use state names with periods via a workaround. + + GitLab 15.7 [adds full support](https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#state-not-found-if-the-state-name-contains-a-period) for state names that contain periods. If you used a workaround to handle these state names, your jobs might fail, or it might look like you've run Terraform for the first time. + + To resolve the issue: + + 1. Change any references to the state file by excluding the period and any characters that follow. + - For example, if your state name is `state.name`, change all references to `state`. + 1. Run your Terraform commands. + + To use the full state name, including the period, [migrate to the full state file](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html#migrate-to-a-gitlab-managed-terraform-state). + end_of_support_milestone: 16.0 + end_of_support_date: 2023-05-22 + tiers: [Free, Silver, Gold, Core, Premium, Ultimate] + documentation_url: 'https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#troubleshooting-terraform-state' diff --git a/db/structure.sql b/db/structure.sql index aa8cae0ee1d..cae57e8aae2 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -31239,6 +31239,8 @@ CREATE INDEX index_user_statuses_on_user_id ON user_statuses USING btree (user_i CREATE UNIQUE INDEX index_user_synced_attributes_metadata_on_user_id ON user_synced_attributes_metadata USING btree (user_id); +CREATE INDEX index_users_for_active_billable ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL) OR (user_type <> ALL ('{1,2,3,4,5,6,7,8,9,11}'::smallint[])))); + CREATE INDEX index_users_on_accepted_term_id ON users USING btree (accepted_term_id); CREATE INDEX index_users_on_admin ON users USING btree (admin); @@ -31471,8 +31473,6 @@ CREATE INDEX index_web_hooks_on_integration_id ON web_hooks USING btree (integra CREATE INDEX index_web_hooks_on_project_id ON web_hooks USING btree (project_id); -CREATE INDEX index_users_for_active_billable ON users USING btree (id) WHERE (((state)::text = 'active'::text) AND ((user_type IS NULL) OR (user_type = ANY (ARRAY[NULL::integer, 6, 4]))) AND ((user_type IS NULL) OR (user_type <> ALL ('{1,2,3,4,5,6,7,8,9,11}'::smallint[])))); - CREATE INDEX index_web_hooks_on_project_id_recent_failures ON web_hooks USING btree (project_id, recent_failures); CREATE INDEX index_web_hooks_on_type ON web_hooks USING btree (type); diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md index 37b43245f3a..5c8c7f7baf1 100644 --- a/doc/administration/auth/ldap/ldap_synchronization.md +++ b/doc/administration/auth/ldap/ldap_synchronization.md @@ -55,7 +55,9 @@ use a [crontab generator](http://www.crontabgenerator.com). The example below shows how to set LDAP user sync to run once every 12 hours at the top of the hour. -**Omnibus installations** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb`: @@ -63,19 +65,77 @@ sync to run once every 12 hours at the top of the hour. gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *" ``` -1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + +:::TabTitle Helm chart (Kubernetes) + +1. Export the Helm values: + + ```shell + helm get values gitlab > gitlab_values.yaml + ``` + +1. Edit `gitlab_values.yaml`: + + ```yaml + global: + appConfig: + cron_jobs: + ldap_sync_worker: + cron: "0 */12 * * *" + ``` + +1. Save the file and apply the new values: + + ```shell + helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab + ``` + +:::TabTitle Docker + +1. Edit `docker-compose.yml`: + + ```yaml + version: "3.6" + services: + gitlab: + environment: + GITLAB_OMNIBUS_CONFIG: | + gitlab_rails['ldap_sync_worker_cron'] = "0 */12 * * *" + ``` + +1. Save the file and restart GitLab: -**Source installations** + ```shell + docker compose up -d + ``` -1. Edit `config/gitlab.yaml`: +:::TabTitle Self-compiled (source) + +1. Edit `/home/git/gitlab/config/gitlab.yml`: ```yaml - cron_jobs: - ldap_sync_worker_cron: - "0 */12 * * *" + production: &base + ee_cron_jobs: + ldap_sync_worker: + cron: "0 */12 * * *" ``` -1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + +::EndTabs ## Group sync @@ -91,41 +151,103 @@ GitLab group membership to be automatically updated based on LDAP group members. The `group_base` configuration should be a base LDAP 'container', such as an 'organization' or 'organizational unit', that contains LDAP groups that should be available to GitLab. For example, `group_base` could be -`ou=groups,dc=example,dc=com`. In the configuration file it looks like the +`ou=groups,dc=example,dc=com`. In the configuration file, it looks like the following. -**Omnibus configuration** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb`: ```ruby gitlab_rails['ldap_servers'] = { - 'main' => { - # snip... - 'group_base' => 'ou=groups,dc=example,dc=com', - } + 'main' => { + 'group_base' => 'ou=groups,dc=example,dc=com', + } } ``` -1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure). +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + +:::TabTitle Helm chart (Kubernetes) + +1. Export the Helm values: + + ```shell + helm get values gitlab > gitlab_values.yaml + ``` -**Source configuration** +1. Edit `gitlab_values.yaml`: + + ```yaml + global: + appConfig: + ldap: + servers: + main: + group_base: ou=groups,dc=example,dc=com + ``` + +1. Save the file and apply the new values: + + ```shell + helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab + ``` + +:::TabTitle Docker + +1. Edit `docker-compose.yml`: + + ```yaml + version: "3.6" + services: + gitlab: + environment: + GITLAB_OMNIBUS_CONFIG: | + gitlab_rails['ldap_servers'] = { + 'main' => { + 'group_base' => 'ou=groups,dc=example,dc=com', + } + } + ``` + +1. Save the file and restart GitLab: + + ```shell + docker compose up -d + ``` + +:::TabTitle Self-compiled (source) 1. Edit `/home/git/gitlab/config/gitlab.yml`: ```yaml - production: + production: &base ldap: servers: main: - # snip... group_base: ou=groups,dc=example,dc=com ``` -1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + +::EndTabs To take advantage of group sync, group Owners or users with the [Maintainer role](../../../user/permissions.md) must -[create one or more LDAP group links](#add-group-links). +[create one or more LDAP group links](../../../user/group/access_and_permissions.md#manage-group-memberships-via-ldap). ### Add group links @@ -146,37 +268,101 @@ as opposed to the full DN. Additionally, if an LDAP user has an `admin` role, but is not a member of the `admin_group` group, GitLab revokes their `admin` role when syncing. -**Omnibus configuration** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb`: ```ruby gitlab_rails['ldap_servers'] = { - 'main' => { - # snip... - 'group_base' => 'ou=groups,dc=example,dc=com', - 'admin_group' => 'my_admin_group', - } + 'main' => { + 'group_base' => 'ou=groups,dc=example,dc=com', + 'admin_group' => 'my_admin_group', + } } ``` -1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure). +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + +:::TabTitle Helm chart (Kubernetes) + +1. Export the Helm values: + + ```shell + helm get values gitlab > gitlab_values.yaml + ``` + +1. Edit `gitlab_values.yaml`: + + ```yaml + global: + appConfig: + ldap: + servers: + main: + group_base: ou=groups,dc=example,dc=com + admin_group: my_admin_group + ``` + +1. Save the file and apply the new values: + + ```shell + helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab + ``` -**Source configuration** +:::TabTitle Docker + +1. Edit `docker-compose.yml`: + + ```yaml + version: "3.6" + services: + gitlab: + environment: + GITLAB_OMNIBUS_CONFIG: | + gitlab_rails['ldap_servers'] = { + 'main' => { + 'group_base' => 'ou=groups,dc=example,dc=com', + 'admin_group' => 'my_admin_group', + } + } + ``` + +1. Save the file and restart GitLab: + + ```shell + docker compose up -d + ``` + +:::TabTitle Self-compiled (source) 1. Edit `/home/git/gitlab/config/gitlab.yml`: ```yaml - production: + production: &base ldap: servers: main: - # snip... group_base: ou=groups,dc=example,dc=com admin_group: my_admin_group ``` -1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + +::EndTabs ### Global group memberships lock @@ -218,7 +404,9 @@ You can manually configure LDAP group sync times by setting the following configuration values. The example below shows how to set group sync to run once every two hours at the top of the hour. -**Omnibus installations** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb`: @@ -226,19 +414,77 @@ sync to run once every two hours at the top of the hour. gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *" ``` -1. [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + +:::TabTitle Helm chart (Kubernetes) + +1. Export the Helm values: + + ```shell + helm get values gitlab > gitlab_values.yaml + ``` + +1. Edit `gitlab_values.yaml`: + + ```yaml + global: + appConfig: + cron_jobs: + ldap_group_sync_worker: + cron: "*/30 * * * *" + ``` + +1. Save the file and apply the new values: + + ```shell + helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab + ``` + +:::TabTitle Docker + +1. Edit `docker-compose.yml`: + + ```yaml + version: "3.6" + services: + gitlab: + environment: + GITLAB_OMNIBUS_CONFIG: | + gitlab_rails['ldap_group_sync_worker_cron'] = "0 */2 * * * *" + ``` + +1. Save the file and restart GitLab: + + ```shell + docker compose up -d + ``` -**Source installations** +:::TabTitle Self-compiled (source) -1. Edit `config/gitlab.yaml`: +1. Edit `/home/git/gitlab/config/gitlab.yml`: ```yaml - cron_jobs: - ldap_group_sync_worker_cron: - "*/30 * * * *" + production: &base + ee_cron_jobs: + ldap_group_sync_worker: + cron: "*/30 * * * *" ``` -1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + +::EndTabs ### External groups @@ -247,35 +493,97 @@ to these groups as [external users](../../../user/admin_area/external_users.md). Group membership is checked periodically through the `LdapGroupSync` background task. -**Omnibus configuration** +::Tabs + +:::TabTitle Linux package (Omnibus) 1. Edit `/etc/gitlab/gitlab.rb`: ```ruby gitlab_rails['ldap_servers'] = { - 'main' => { - # snip... - 'external_groups' => ['interns', 'contractors'], - } + 'main' => { + 'external_groups' => ['interns', 'contractors'], + } } ``` -1. [Apply your changes to GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure). +1. Save the file and reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` -**Source configuration** +:::TabTitle Helm chart (Kubernetes) -1. Edit `config/gitlab.yaml`: +1. Export the Helm values: + + ```shell + helm get values gitlab > gitlab_values.yaml + ``` + +1. Edit `gitlab_values.yaml`: ```yaml - production: + global: + appConfig: + ldap: + servers: + main: + external_groups: ['interns', 'contractors'] + ``` + +1. Save the file and apply the new values: + + ```shell + helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab + ``` + +:::TabTitle Docker + +1. Edit `docker-compose.yml`: + + ```yaml + version: "3.6" + services: + gitlab: + environment: + GITLAB_OMNIBUS_CONFIG: | + gitlab_rails['ldap_servers'] = { + 'main' => { + 'external_groups' => ['interns', 'contractors'], + } + } + ``` + +1. Save the file and restart GitLab: + + ```shell + docker compose up -d + ``` + +:::TabTitle Self-compiled (source) + +1. Edit `/home/git/gitlab/config/gitlab.yml`: + + ```yaml + production: &base ldap: servers: main: - # snip... external_groups: ['interns', 'contractors'] ``` -1. [Restart GitLab](../../restart_gitlab.md#installations-from-source) for the changes to take effect. +1. Save the file and restart GitLab: + + ```shell + # For systems running systemd + sudo systemctl restart gitlab.target + + # For systems running SysV init + sudo service gitlab restart + ``` + +::EndTabs ### Group sync technical details diff --git a/doc/administration/lfs/index.md b/doc/administration/lfs/index.md index cae2dea5ffe..4075f15bbbd 100644 --- a/doc/administration/lfs/index.md +++ b/doc/administration/lfs/index.md @@ -7,7 +7,8 @@ disqus_identifier: 'https://docs.gitlab.com/ee/workflow/lfs/lfs_administration.h # GitLab Git Large File Storage (LFS) Administration **(FREE SELF)** -Documentation about how to use Git LFS are under [Managing large binary files with Git LFS doc](../../topics/git/lfs/index.md). +This page contains information about configuring Git LFS in self-managed GitLab instances. +For user documentation about Git LFS, see [Git Large File Storage](../../topics/git/lfs/index.md). LFS is enabled in GitLab self-managed instances by default. @@ -217,6 +218,12 @@ You can see the total storage used for LFS objects on groups and projects: - In the administration area. - In the [groups](../../api/groups.md) and [projects APIs](../../api/projects.md). +## Related topics + +- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/) +- User documentation: [Git Large File Storage (LFS)](../../topics/git/lfs/index.md) +- [Git LFS developer information](../../development/lfs.md) + ## Troubleshooting ### Missing LFS objects diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 443c0fe9f43..bc7b151a9be 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -13112,6 +13112,17 @@ Describes an external status check. | <a id="fileuploadpath"></a>`path` | [`String!`](#string) | Path of the upload. | | <a id="fileuploadsize"></a>`size` | [`Int!`](#int) | Size of the upload in bytes. | +### `ForkDetails` + +Details of the fork project compared to its upstream project. + +#### Fields + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="forkdetailsahead"></a>`ahead` | [`Int`](#int) | Number of commits ahead of upstream. | +| <a id="forkdetailsbehind"></a>`behind` | [`Int`](#int) | Number of commits behind upstream. | + ### `GeoNode` #### Fields @@ -17457,6 +17468,22 @@ four standard [pagination arguments](#connection-pagination-arguments): | <a id="projectenvironmentsstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. | | <a id="projectenvironmentstype"></a>`type` | [`String`](#string) | Search query for environment type. | +##### `Project.forkDetails` + +Details of the fork project compared to its upstream project. + +WARNING: +**Introduced** in 15.7. +This feature is in Alpha. It can be changed or removed at any time. + +Returns [`ForkDetails`](#forkdetails). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| <a id="projectforkdetailsref"></a>`ref` | [`String`](#string) | Ref of the fork. Default value is HEAD. | + ##### `Project.forkTargets` Namespaces in which the current user can fork the project into. diff --git a/doc/development/lfs.md b/doc/development/lfs.md index 4d3371af1d4..dd7687cd28b 100644 --- a/doc/development/lfs.md +++ b/doc/development/lfs.md @@ -4,7 +4,10 @@ group: Source Code info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Git LFS **(FREE)** +# Git LFS developer information **(FREE)** + +This page contains developer-centric information for GitLab team members. For the +user documentation, see [Git Large File Storage](../topics/git/lfs/index.md). ## Deep Dive @@ -86,3 +89,9 @@ request is not preserved for the internal API requests made by Gitaly correlation IDs for those API requests are random values until [this Workhorse issue](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/309) is resolved. + +## Related topics + +- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/) +- User documentation: [Git Large File Storage (LFS)](../topics/git/lfs/index.md) +- [GitLab Git Large File Storage (LFS) Administration](../administration/lfs/index.md) for self-managed instances diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md index 2bfc7f0243a..fcb6e5c1b20 100644 --- a/doc/policy/maintenance.md +++ b/doc/policy/maintenance.md @@ -36,7 +36,7 @@ The following table describes the version types and their release cadence: | Version type | Description | Cadence | |:-------------|:------------|:--------| -| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 15.0 on May 22, 2022. GitLab schedules major releases on May 22 each year, by default. | +| Major | For significant changes, or when any backward-incompatible changes are introduced to the public API. | Yearly. The next major release is GitLab 16.0 on May 22, 2023. GitLab schedules major releases on May 22 each year, by default. | | Minor | For when new backward-compatible functionality is introduced to the public API, a minor feature is introduced, or when a set of smaller features is rolled out. | Monthly on the 22nd. | | Patch | For backward-compatible bug fixes that fix incorrect behavior. See [Patch releases](#patch-releases). | As needed. | diff --git a/doc/topics/git/lfs/index.md b/doc/topics/git/lfs/index.md index 98710315652..731705fd72e 100644 --- a/doc/topics/git/lfs/index.md +++ b/doc/topics/git/lfs/index.md @@ -148,6 +148,12 @@ LFS object. Technical details about how this works can be found in the [development documentation for LFS](../../../development/lfs.md#including-lfs-blobs-in-project-archives). +## Related topics + +- Blog post: [Getting started with Git LFS](https://about.gitlab.com/blog/2017/01/30/getting-started-with-git-lfs-tutorial/) +- [Git LFS developer information](../../../development/lfs.md) +- [GitLab Git Large File Storage (LFS) Administration](../../../administration/lfs/index.md) for self-managed instances + ## Troubleshooting ### Encountered `n` files that should have been pointers, but weren't diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index 1920e97bb15..43e7c9dac37 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -191,6 +191,31 @@ From GitLab 16.0 and later, the runner registration methods implemented by the n <div class="deprecation removal-160 breaking-change"> +### Support for periods (`.`) in Terraform state names might break existing states + +End of Support: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)<br /> +Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22) + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +Previously, Terraform state names containing periods were not supported. However, you could still use state names with periods via a workaround. + +GitLab 15.7 [adds full support](https://docs.gitlab.com/ee/user/infrastructure/iac/troubleshooting.html#state-not-found-if-the-state-name-contains-a-period) for state names that contain periods. If you used a workaround to handle these state names, your jobs might fail, or it might look like you've run Terraform for the first time. + +To resolve the issue: + + 1. Change any references to the state file by excluding the period and any characters that follow. + - For example, if your state name is `state.name`, change all references to `state`. + 1. Run your Terraform commands. + +To use the full state name, including the period, [migrate to the full state file](https://docs.gitlab.com/ee/user/infrastructure/iac/terraform_state.html#migrate-to-a-gitlab-managed-terraform-state). + +</div> + +<div class="deprecation removal-160 breaking-change"> + ### The Phabricator task importer is deprecated Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22) diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 498d577178f..dca5b5de436 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -309,7 +309,7 @@ table.supported-languages ul { <li> <a id="notes-regarding-supported-languages-and-package-managers-3"></a> <p> - npm is only supported when <code>lockfileVersion = 1</code> or <code>lockfileVersion = 2</code>. Work to add support for <code>lockfileVersion = 3</code> is being tracked in issue <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/365176">GitLab#365176</a>. + npm is supported for <code>lockfileVersion = 1</code>, <code>lockfileVersion = 2</code>, and <code>lockfileVersion = 3</code>. Support for <code>lockfileVersion = 3</code> was <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/365176">introduced</a> in GitLab 15.7</a>. </p> </li> <li> diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md index 8a4843b9843..bd10560e138 100644 --- a/doc/user/group/saml_sso/index.md +++ b/doc/user/group/saml_sso/index.md @@ -123,7 +123,7 @@ It can also help to compare the XML response from your provider with our [exampl > - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/215155) in GitLab 15.5 [with a flag](../../../administration/feature_flags.md) named `transparent_sso_enforcement` to include transparent enforcement even when SSO enforcement is not enabled. Disabled on GitLab.com. FLAG: -On self-managed GitLab, transparent SSO enforcement is unavailable. On GitLab.com, transparent SSO enforcement is unavailable and can be configured by GitLab.com administrators only. +On self-managed GitLab, transparent SSO enforcement is unavailable. On GitLab.com, see the [Transparent SSO rollout](https://gitlab.com/gitlab-org/gitlab/-/issues/375788) issue for the current status. SSO is enforced when users access groups and projects in the organization's group hierarchy. Users can view other groups and projects without SSO sign in. @@ -177,6 +177,17 @@ When SSO is enforced, users are not immediately revoked. If the user: - Has an active session, they can continue accessing the group for up to 24 hours until the identity provider session times out. +### Selectively enable and disable transparent SSO enforcement + +There are two feature flags associated with this feature to allow precise control. If a customer has a problem with transparent SSO on GitLab.com, GitLab can help troubleshoot and override the feature flag as necessary. + +**`transparent_sso_enforcement`:** This feature flag should only be enabled or disabled by the Authentication and Authorization group +or in the case of a serious and widespread issue affecting many groups or users. See [issue 375788](https://gitlab.com/gitlab-org/gitlab/-/issues/375788) for the current GitLab.com rollout status. + +**`transparent_sso_enforcement_override`:** When the `transparent_sso_enforcement` feature flag is enabled, support or production teams can +turn off transparent SSO by enabling this feature flag for a specific customer group. **Enabling** this feature flag +disables transparent SSO enforcement. + ## Providers The SAML standard means that you can use a wide range of identity providers with GitLab. Your identity provider might have relevant documentation. It can be generic SAML documentation or specifically targeted for GitLab. @@ -337,11 +348,14 @@ When a user tries to sign in with Group SSO, GitLab attempts to find or create a ### Linking SAML to your existing GitLab.com account +> **Remember me** checkbox [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/121569) in GitLab 15.7. + To link SAML to your existing GitLab.com account: 1. Sign in to your GitLab.com account. [Reset your password](https://gitlab.com/users/password/new) if necessary. 1. Locate and visit the **GitLab single sign-on URL** for the group you're signing in to. A group owner can find this on the group's **Settings > SAML SSO** page. If the sign-in URL is configured, users can connect to the GitLab app from the identity provider. -1. Optional. Select the **Remember me** checkbox to stay signed in to GitLab for 2 weeks. You may still be asked to re-authenticate with your SAML provider more frequently. +1. Optional. Select the **Remember me** checkbox to stay signed in to GitLab for 2 weeks. You may still be asked to re-authenticate with your SAML provider + more frequently. 1. Select **Authorize**. 1. Enter your credentials on the identity provider if prompted. 1. You are then redirected back to GitLab.com and should now have access to the group. In the future, you can use SAML to sign in to GitLab.com. diff --git a/doc/user/group/saml_sso/troubleshooting.md b/doc/user/group/saml_sso/troubleshooting.md index 521bec04c4a..f8075e62ecc 100644 --- a/doc/user/group/saml_sso/troubleshooting.md +++ b/doc/user/group/saml_sso/troubleshooting.md @@ -269,3 +269,15 @@ Pay particular attention to the following 403 errors: - `app_not_configured` - `app_not_configured_for_user` + +## SAML Name ID and email address do not match your user account **(PREMIUM SAAS)** + +If users encounter the error `SAML Name ID and email address do not match your user account. Contact an administrator.` +this means: + +- The NameID value sent by SAML does not match the existing SAML identity `extern_uid` value. +- Either the SAML response did not include an email address or the email address did not match the user's GitLab email address. + +A GitLab group Owner can use the [SAML API](../../../api/saml.md) to update the user's SAML `extern_uid`. +The `extern_uid` value must match the Name ID value sent by the SAML identity provider (IdP). Depending on the IdP configuration +this may be a generated unique ID, an email address, or other value. diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 956202bb990..540db463f0a 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -118,27 +118,25 @@ A project-level endpoint is also required to install NuGet packages from a proje To use the [project-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio: 1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/). -1. In Windows, select **File > Options**. On macOS, select **Visual Studio > Preferences**. +1. In Windows, select **Tools > Options**. On macOS, select **Visual Studio > Preferences**. 1. In the **NuGet** section, select **Sources** to view a list of all your NuGet sources. 1. Select **Add**. 1. Complete the following fields: - **Name**: Name for the source. - - **Location**: `https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json`, + - **Source**: `https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json`, where `<your_project_id>` is your project ID, and `gitlab.example.com` is your domain name. - - **Username**: Your GitLab username or deploy token username. - - **Password**: Your personal access token or deploy token. - - ![Visual Studio Adding a NuGet source](img/visual_studio_adding_nuget_source.png) 1. Select **Save**. +1. When you access the package, you must enter your **Username** and **Password**: -The source is displayed in your list. + - **Username**: Your GitLab username or deploy token username. + - **Password**: Your personal access token or deploy token. -![Visual Studio NuGet source added](img/visual_studio_nuget_source_added.png) +The source is displayed in your list. -If you get a warning, ensure that the **Location**, **Username**, and +If you get a warning, ensure that the **Source**, **Username**, and **Password** are correct. #### Group-level endpoint @@ -148,27 +146,25 @@ To install a package from a group, use a group-level endpoint. To use the [group-level](#use-the-gitlab-endpoint-for-nuget-packages) NuGet endpoint, add the Package Registry as a source with Visual Studio: 1. Open [Visual Studio](https://visualstudio.microsoft.com/vs/). -1. In Windows, select **File > Options**. On macOS, select **Visual Studio > Preferences**. +1. In Windows, select **Tools > Options**. On macOS, select **Visual Studio > Preferences**. 1. In the **NuGet** section, select **Sources** to view a list of all your NuGet sources. 1. Select **Add**. 1. Complete the following fields: - **Name**: Name for the source. - - **Location**: `https://gitlab.example.com/api/v4/groups/<your_group_id>/-/packages/nuget/index.json`, + - **Source**: `https://gitlab.example.com/api/v4/groups/<your_group_id>/-/packages/nuget/index.json`, where `<your_group_id>` is your group ID, and `gitlab.example.com` is your domain name. - - **Username**: Your GitLab username or deploy token username. - - **Password**: Your personal access token or deploy token. - - ![Visual Studio Adding a NuGet source](img/visual_studio_adding_nuget_source.png) 1. Select **Save**. +1. When you access the package, you must enter your **Username** and **Password**. -The source is displayed in your list. + - **Username**: Your GitLab username or deploy token username. + - **Password**: Your personal access token or deploy token. -![Visual Studio NuGet source added](img/visual_studio_nuget_source_added.png) +The source is displayed in your list. -If you get a warning, ensure that the **Location**, **Username**, and +If you get a warning, ensure that the **Source**, **Username**, and **Password** are correct. ### Add a source with the .NET CLI diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md index 5753b0a5b97..2cd4c3650b1 100644 --- a/doc/user/project/import/index.md +++ b/doc/user/project/import/index.md @@ -18,7 +18,7 @@ See these documents to migrate to GitLab: - [From GitLab.com](gitlab_com.md) - [From Gitea](gitea.md) - [From Perforce](perforce.md) -- [From SVN](svn.md) +- [From SVN](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Git-as-a-Client) - [From TFVC](tfvc.md) - [From repository by URL](repo_by_url.md) - [By uploading a manifest file (AOSP)](manifest.md) diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md index 1687d621e2e..6730ef862e6 100644 --- a/doc/user/project/import/svn.md +++ b/doc/user/project/import/svn.md @@ -1,91 +1,11 @@ --- -type: howto -stage: Manage -group: Import -info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +redirect_to: 'index.md' +remove_date: '2023-03-15' --- -# Migrate from Subversion to GitLab **(FREE)** +This document was moved to [another location](index.md). -GitLab uses Git as its version control system. If you're using Subversion (SVN) as your version control system, -you can migrate to using a Git repository in GitLab using `svn2git`. - -You can follow the steps on this page to migrate to Git if your SVN repository: - -- Has a standard format (trunk, branches, and tags). -- Is not nested. - -For a non-standard repository see the [`svn2git` documentation](https://github.com/nirvdrum/svn2git). - -We recommend a hard cut over from SVN to Git and GitLab. Run the migration command once and then have all users use the -new GitLab repository immediately. - -## Install `svn2git` - -Install `svn2git` on a local workstation rather than the GitLab server: - -- On all systems you can install as a Ruby gem if you already have Ruby and Git installed: - - ```shell - sudo gem install svn2git - ``` - -- On Debian-based Linux distributions you can install the native packages: - - ```shell - sudo apt-get install git-core git-svn ruby - ``` - -## Prepare an authors file (recommended) - -Prepare an authors file so `svn2git` can map SVN authors to Git authors. If you choose not to create the authors file, -commits are not attributed to the correct GitLab user. - -To map authors, you must map every author present on changes in the SVN repository. If you don't, the -migration fails and you have to update the author file accordingly. - -1. Search through the SVN repository and output a list of authors: - - ```shell - svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq - ``` - -1. Use the output from the last command to construct the authors file. Create a file called `authors.txt` and add one - mapping per line. For example: - - ```plaintext - sidneyjones = Sidney Jones <sidneyjones@example.com> - ``` - -## Migrate SVN repository to Git repository - -`svn2git` supports excluding certain file paths, branches, tags, and more. See -the [`svn2git` documentation](https://github.com/nirvdrum/svn2git) or run `svn2git --help` for full documentation on all of -the available options. - -For each repository to migrate: - -1. Create a new directory and change into it. -1. For repositories that: - - - Don't require a username and password, run: - - ```shell - svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt - ``` - - - Do require a username and password, run: - - ```shell - svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt --username <username> --password <password> - ``` - -1. Create a new GitLab project for your migrated code. -1. Copy the SSH or HTTP(S) repository URL from the GitLab project page. -1. Add the GitLab repository as a Git remote and push all the changes. This pushes all commits, branches, and tags. - - ```shell - git remote add origin git@gitlab.example.com:<group>/<project>.git - git push --all origin - git push --tags origin - ``` +<!-- This redirect file can be deleted after <2023-03-15>. --> +<!-- Redirects that point to other docs in the same project expire in three months. --> +<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> +<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a073af3bf1e..c60e091568b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2128,6 +2128,9 @@ msgstr "" msgid "Activate Service Desk" msgstr "" +msgid "Activated" +msgstr "" + msgid "Active" msgstr "" @@ -30289,6 +30292,9 @@ msgstr "" msgid "PipelineSchedules|Are you sure you want to delete this pipeline schedule?" msgstr "" +msgid "PipelineSchedules|Cron timezone" +msgstr "" + msgid "PipelineSchedules|Delete pipeline schedule" msgstr "" @@ -30301,6 +30307,9 @@ msgstr "" msgid "PipelineSchedules|Inactive" msgstr "" +msgid "PipelineSchedules|Interval Pattern" +msgstr "" + msgid "PipelineSchedules|Last Pipeline" msgstr "" @@ -30328,6 +30337,9 @@ msgstr "" msgid "PipelineSchedules|Run pipeline schedule" msgstr "" +msgid "PipelineSchedules|Save pipeline schedule" +msgstr "" + msgid "PipelineSchedules|Successfully taken ownership from %{owner}." msgstr "" @@ -40829,15 +40841,6 @@ msgstr "" msgid "TemporaryStorageIncrease|can only be set with more than %{percentage}%% usage" msgstr "" -msgid "TemporaryStorage|GitLab allows you a %{strongStart}free, one-time storage increase%{strongEnd}. For 30 days your storage will be unlimited. This gives you time to reduce your storage usage. After 30 days, your original storage limit of %{limit} applies. If you are at maximum storage capacity, your account will be read-only. To continue using GitLab you'll have to purchase additional storage or decrease storage usage." -msgstr "" - -msgid "TemporaryStorage|Increase storage temporarily" -msgstr "" - -msgid "TemporaryStorage|Temporarily increase storage now?" -msgstr "" - msgid "Terminal" msgstr "" @@ -42727,7 +42730,7 @@ msgstr "" msgid "This variable can not be masked." msgstr "" -msgid "This vulnerability type has been deprecated from GitLab's default ruleset and automatically resolved." +msgid "This vulnerability was automatically resolved because its vulnerability type was disabled in this project or removed from GitLab's default ruleset." msgstr "" msgid "This will invalidate your registered applications and U2F / WebAuthn devices." @@ -44475,9 +44478,6 @@ msgstr "" msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items." msgstr "" -msgid "UsageQuota|Increase storage temporarily" -msgstr "" - msgid "UsageQuota|LFS storage" msgstr "" diff --git a/spec/features/tags/developer_creates_tag_spec.rb b/spec/features/tags/developer_creates_tag_spec.rb index 39d34a5ae64..111710ba325 100644 --- a/spec/features/tags/developer_creates_tag_spec.rb +++ b/spec/features/tags/developer_creates_tag_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'Developer creates tag', feature_category: :source_code_management do +RSpec.describe 'Developer creates tag', :js, feature_category: :source_code_management do let(:user) { create(:user) } let(:group) { create(:group) } let(:project) { create(:project, :repository, namespace: group) } @@ -15,6 +15,8 @@ RSpec.describe 'Developer creates tag', feature_category: :source_code_managemen context 'from tag list' do before do visit project_tags_path(project) + click_link 'New tag' + wait_for_requests end it 'with an invalid name displays an error' do @@ -23,10 +25,17 @@ RSpec.describe 'Developer creates tag', feature_category: :source_code_managemen expect(page).to have_content 'Tag name invalid' end - it 'with an invalid reference displays an error' do - create_tag_in_form(tag: 'v2.0', ref: 'foo') - - expect(page).to have_content 'Target foo is invalid' + it "doesn't allow to select invalid ref" do + ref_name = 'foo' + fill_in 'tag_name', with: 'v2.0' + ref_selector = '.ref-selector' + find(ref_selector).click + wait_for_requests + page.within(ref_selector) do + fill_in _('Search by Git revision'), with: ref_name + wait_for_requests + expect(find('.gl-dropdown-contents')).not_to have_content(ref_name) + end end it 'that already exists displays an error' do @@ -46,27 +55,34 @@ RSpec.describe 'Developer creates tag', feature_category: :source_code_managemen end end - it 'opens dropdown for ref', :js do - click_link 'New tag' - ref_row = find('.form-group:nth-of-type(2) .col-sm-12') + it 'opens dropdown for ref' do + ref_row = find('.form-group:nth-of-type(2) .col-sm-auto') page.within ref_row do ref_input = find('[name="ref"]', visible: false) expect(ref_input.value).to eq 'master' - expect(find('.dropdown-toggle-text')).to have_content 'master' - - find('.js-branch-select').click - - expect(find('.dropdown-menu')).to have_content 'empty-branch' + expect(find('.gl-dropdown-button-text')).to have_content 'master' + find('.ref-selector').click + expect(find('.dropdown-menu')).to have_content 'test' end end end def create_tag_in_form(tag:, ref:, message: nil, desc: nil) - click_link 'New tag' fill_in 'tag_name', with: tag - find('#ref', visible: false).set(ref) + select_ref(ref: ref) fill_in 'message', with: message unless message.nil? fill_in 'release_description', with: desc unless desc.nil? click_button 'Create tag' end + + def select_ref(ref:) + ref_selector = '.ref-selector' + find(ref_selector).click + wait_for_requests + page.within(ref_selector) do + fill_in _('Search by Git revision'), with: ref + wait_for_requests + find('li', text: ref, match: :prefer_exact).click + end + end end diff --git a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js index e5d9b378a42..639c2dbef4c 100644 --- a/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js +++ b/spec/frontend/ci/pipeline_schedules/components/pipeline_schedules_form_spec.js @@ -1,25 +1,160 @@ -import { shallowMount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; import { GlForm } from '@gitlab/ui'; +import { nextTick } from 'vue'; +import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; +import axios from '~/lib/utils/axios_utils'; import PipelineSchedulesForm from '~/ci/pipeline_schedules/components/pipeline_schedules_form.vue'; +import RefSelector from '~/ref/components/ref_selector.vue'; +import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants'; +import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue'; +import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue'; +import { timezoneDataFixture } from '../../../vue_shared/components/timezone_dropdown/helpers'; describe('Pipeline schedules form', () => { let wrapper; + const defaultBranch = 'main'; + const projectId = '1'; + const cron = ''; + const dailyLimit = ''; - const createComponent = () => { - wrapper = shallowMount(PipelineSchedulesForm); + const createComponent = (mountFn = shallowMountExtended, stubs = {}) => { + wrapper = mountFn(PipelineSchedulesForm, { + propsData: { + timezoneData: timezoneDataFixture, + refParam: 'master', + }, + provide: { + fullPath: 'gitlab-org/gitlab', + projectId, + defaultBranch, + cron, + cronTimezone: '', + dailyLimit, + settingsLink: '', + }, + stubs, + }); }; const findForm = () => wrapper.findComponent(GlForm); + const findDescription = () => wrapper.findByTestId('schedule-description'); + const findIntervalComponent = () => wrapper.findComponent(IntervalPatternInput); + const findTimezoneDropdown = () => wrapper.findComponent(TimezoneDropdown); + const findRefSelector = () => wrapper.findComponent(RefSelector); + const findSubmitButton = () => wrapper.findByTestId('schedule-submit-button'); + const findCancelButton = () => wrapper.findByTestId('schedule-cancel-button'); + // Variables + const findVariableRows = () => wrapper.findAllByTestId('ci-variable-row'); + const findKeyInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-key'); + const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value'); + const findRemoveIcons = () => wrapper.findAllByTestId('remove-ci-variable-row'); beforeEach(() => { createComponent(); }); - afterEach(() => { - wrapper.destroy(); + describe('Form elements', () => { + it('displays form', () => { + expect(findForm().exists()).toBe(true); + }); + + it('displays the description input', () => { + expect(findDescription().exists()).toBe(true); + }); + + it('displays the interval pattern component', () => { + const intervalPattern = findIntervalComponent(); + + expect(intervalPattern.exists()).toBe(true); + expect(intervalPattern.props()).toMatchObject({ + initialCronInterval: cron, + dailyLimit, + sendNativeErrors: false, + }); + }); + + it('displays the Timezone dropdown', () => { + const timezoneDropdown = findTimezoneDropdown(); + + expect(timezoneDropdown.exists()).toBe(true); + expect(timezoneDropdown.props()).toMatchObject({ + value: '', + name: 'schedule-timezone', + timezoneData: timezoneDataFixture, + }); + }); + + it('displays the branch/tag selector', () => { + const refSelector = findRefSelector(); + + expect(refSelector.exists()).toBe(true); + expect(refSelector.props()).toMatchObject({ + enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_TAGS], + value: defaultBranch, + projectId, + translations: { dropdownHeader: 'Select target branch or tag' }, + useSymbolicRefNames: true, + state: true, + name: '', + }); + }); + + it('displays the submit and cancel buttons', () => { + expect(findSubmitButton().exists()).toBe(true); + expect(findCancelButton().exists()).toBe(true); + }); }); - it('displays form', () => { - expect(findForm().exists()).toBe(true); + describe('CI variables', () => { + let mock; + + const addVariableToForm = () => { + const input = findKeyInputs().at(0); + input.element.value = 'test_var_2'; + input.trigger('change'); + }; + + beforeEach(() => { + mock = new MockAdapter(axios); + createComponent(mountExtended); + }); + + afterEach(() => { + mock.restore(); + }); + + it('creates blank variable on input change event', async () => { + expect(findVariableRows()).toHaveLength(1); + + addVariableToForm(); + + await nextTick(); + + expect(findVariableRows()).toHaveLength(2); + expect(findKeyInputs().at(1).element.value).toBe(''); + expect(findValueInputs().at(1).element.value).toBe(''); + }); + + it('does not display remove icon for last row', async () => { + addVariableToForm(); + + await nextTick(); + + expect(findRemoveIcons()).toHaveLength(1); + }); + + it('removes ci variable row on remove icon button click', async () => { + addVariableToForm(); + + await nextTick(); + + expect(findVariableRows()).toHaveLength(2); + + findRemoveIcons().at(0).trigger('click'); + + await nextTick(); + + expect(findVariableRows()).toHaveLength(1); + }); }); }); diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js index a4421855f10..36c7c54d080 100644 --- a/spec/frontend/ide/init_gitlab_web_ide_spec.js +++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js @@ -9,6 +9,10 @@ import waitForPromises from 'helpers/wait_for_promises'; jest.mock('@gitlab/web-ide'); jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_action'); jest.mock('~/lib/utils/create_and_submit_form'); +jest.mock('~/lib/utils/csrf', () => ({ + token: 'mock-csrf-token', + headerKey: 'mock-csrf-header', +})); const ROOT_ELEMENT_ID = 'ide'; const TEST_NONCE = 'test123nonce'; @@ -77,6 +81,10 @@ describe('ide/init_gitlab_web_ide', () => { ref: TEST_BRANCH_NAME, gitlabUrl: TEST_GITLAB_URL, nonce: TEST_NONCE, + httpHeaders: { + 'mock-csrf-header': 'mock-csrf-token', + 'X-Requested-With': 'XMLHttpRequest', + }, links: { userPreferences: TEST_USER_PREFERENCES_PATH, feedbackIssue: GITLAB_WEB_IDE_FEEDBACK_ISSUE, diff --git a/spec/graphql/types/projects/fork_details_type_spec.rb b/spec/graphql/types/projects/fork_details_type_spec.rb new file mode 100644 index 00000000000..8e20e2c8299 --- /dev/null +++ b/spec/graphql/types/projects/fork_details_type_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe GitlabSchema.types['ForkDetails'], feature_category: :source_code_management do + specify { expect(described_class.graphql_name).to eq('ForkDetails') } + + it 'has specific fields' do + fields = %i[ + ahead + behind + ] + + expect(described_class).to have_graphql_fields(*fields) + end +end diff --git a/spec/requests/api/graphql/project/fork_details_spec.rb b/spec/requests/api/graphql/project/fork_details_spec.rb new file mode 100644 index 00000000000..efd48b00833 --- /dev/null +++ b/spec/requests/api/graphql/project/fork_details_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'getting project fork details', feature_category: :source_code_management do + include GraphqlHelpers + include ProjectForksHelper + + let_it_be(:project) { create(:project, :public, :repository_private, :repository) } + let_it_be(:current_user) { create(:user, maintainer_projects: [project]) } + let_it_be(:forked_project) { fork_project(project, current_user, repository: true) } + + let(:queried_project) { forked_project } + + let(:query) do + graphql_query_for(:project, + { full_path: queried_project.full_path }, <<~QUERY + forkDetails(ref: "feature"){ + ahead + behind + } + QUERY + ) + end + + it 'returns fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to eq( + { 'ahead' => 1, 'behind' => 29 } + ) + end + + context 'when a project is not a fork' do + let(:queried_project) { project } + + it 'does not return fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to be_nil + end + end + + context 'when a user cannot read the code' do + let_it_be(:current_user) { create(:user) } + + before do + forked_project.update!({ + repository_access_level: 'private', + merge_requests_access_level: 'private' + }) + end + + it 'does not return fork details' do + post_graphql(query, current_user: current_user) + + expect(graphql_data['project']['forkDetails']).to be_nil + end + end +end |