Add latest changes from gitlab-org/gitlab@master
|
@ -118,7 +118,10 @@ export default class ShortcutsIssuable extends Shortcuts {
|
|||
// Wait for the sidebar to trigger('click') open
|
||||
// so it doesn't cause our dropdown to close preemptively
|
||||
setTimeout(() => {
|
||||
document.querySelector(`.block.${name} .shortcut-sidebar-dropdown-toggle`).click();
|
||||
const editBtn =
|
||||
document.querySelector(`.block.${name} .shortcut-sidebar-dropdown-toggle`) ||
|
||||
document.querySelector(`.block.${name} .edit-link`);
|
||||
editBtn.click();
|
||||
}, DEBOUNCE_DROPDOWN_DELAY);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ import initFilePickers from '~/file_pickers';
|
|||
import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
|
||||
import initProjectDeleteButton from '~/projects/project_delete_button';
|
||||
import initServiceDesk from '~/projects/settings_service_desk';
|
||||
import initTransferProjectForm from '~/projects/settings/init_transfer_project_form';
|
||||
import initSearchSettings from '~/search_settings';
|
||||
import initSettingsPanels from '~/settings_panels';
|
||||
import setupTransferEdit from '~/transfer_edit';
|
||||
import UserCallout from '~/user_callout';
|
||||
import initTopicsTokenSelector from '~/projects/settings/topics';
|
||||
import initProjectPermissionsSettings from '../shared/permissions';
|
||||
|
@ -26,7 +26,7 @@ initServiceDesk();
|
|||
|
||||
initProjectLoadingSpinner();
|
||||
initProjectPermissionsSettings();
|
||||
setupTransferEdit('.js-project-transfer-form', 'select.select2');
|
||||
initTransferProjectForm();
|
||||
|
||||
dirtySubmitFactory(document.querySelectorAll('.js-general-settings-form, .js-mr-settings-form'));
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<script>
|
||||
import { GlFormGroup } from '@gitlab/ui';
|
||||
import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
|
||||
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
|
||||
|
||||
export default {
|
||||
name: 'TransferProjectForm',
|
||||
components: {
|
||||
GlFormGroup,
|
||||
NamespaceSelect,
|
||||
ConfirmDanger,
|
||||
},
|
||||
props: {
|
||||
namespaces: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
confirmationPhrase: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
confirmButtonText: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return { selectedNamespace: null };
|
||||
},
|
||||
computed: {
|
||||
hasSelectedNamespace() {
|
||||
return Boolean(this.selectedNamespace?.id);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSelect(selectedNamespace) {
|
||||
this.selectedNamespace = selectedNamespace;
|
||||
this.$emit('selectNamespace', selectedNamespace.id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<gl-form-group>
|
||||
<namespace-select
|
||||
class="qa-namespaces-list"
|
||||
data-testid="transfer-project-namespace"
|
||||
:full-width="true"
|
||||
:data="namespaces"
|
||||
:selected-namespace="selectedNamespace"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</gl-form-group>
|
||||
<confirm-danger
|
||||
button-class="qa-transfer-button"
|
||||
:disabled="!hasSelectedNamespace"
|
||||
:phrase="confirmationPhrase"
|
||||
:button-text="confirmButtonText"
|
||||
@confirm="$emit('confirm')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,53 @@
|
|||
import Vue from 'vue';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import TransferProjectForm from './components/transfer_project_form.vue';
|
||||
|
||||
const prepareNamespaces = (rawNamespaces = '') => {
|
||||
const data = JSON.parse(rawNamespaces);
|
||||
return {
|
||||
group: data?.group.map(convertObjectPropsToCamelCase),
|
||||
user: data?.user.map(convertObjectPropsToCamelCase),
|
||||
};
|
||||
};
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-transfer-project-form');
|
||||
if (!el) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
targetFormId = null,
|
||||
targetHiddenInputId = null,
|
||||
buttonText: confirmButtonText = '',
|
||||
phrase: confirmationPhrase = '',
|
||||
confirmDangerMessage = '',
|
||||
namespaces = '',
|
||||
} = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
provide: {
|
||||
confirmDangerMessage,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(TransferProjectForm, {
|
||||
props: {
|
||||
confirmButtonText,
|
||||
confirmationPhrase,
|
||||
namespaces: prepareNamespaces(namespaces),
|
||||
},
|
||||
on: {
|
||||
selectNamespace: (id) => {
|
||||
if (targetHiddenInputId && document.getElementById(targetHiddenInputId)) {
|
||||
document.getElementById(targetHiddenInputId).value = id;
|
||||
}
|
||||
},
|
||||
confirm: () => {
|
||||
if (targetFormId) document.getElementById(targetFormId)?.submit();
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -47,7 +47,7 @@ export default {
|
|||
actionPrimary() {
|
||||
return {
|
||||
text: this.confirmButtonText,
|
||||
attributes: [{ variant: 'danger', disabled: !this.isValid }],
|
||||
attributes: [{ variant: 'danger', disabled: !this.isValid, class: 'qa-confirm-button' }],
|
||||
};
|
||||
},
|
||||
},
|
||||
|
@ -95,7 +95,7 @@ export default {
|
|||
<gl-form-input
|
||||
id="confirm_name_input"
|
||||
v-model="confirmationPhrase"
|
||||
class="form-control"
|
||||
class="form-control qa-confirm-input"
|
||||
data-testid="confirm-danger-input"
|
||||
type="text"
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<script>
|
||||
import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlSearchBoxByType } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
export const i18n = {
|
||||
DEFAULT_TEXT: __('Select a new namespace'),
|
||||
GROUPS: __('Groups'),
|
||||
USERS: __('Users'),
|
||||
};
|
||||
|
||||
const filterByName = (data, searchTerm = '') =>
|
||||
data.filter((d) => d.humanName.toLowerCase().includes(searchTerm));
|
||||
|
||||
export default {
|
||||
name: 'NamespaceSelect',
|
||||
components: {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlDropdownSectionHeader,
|
||||
GlSearchBoxByType,
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
searchTerm: '',
|
||||
selectedNamespace: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasUserNamespaces() {
|
||||
return this.data.user?.length;
|
||||
},
|
||||
hasGroupNamespaces() {
|
||||
return this.data.group?.length;
|
||||
},
|
||||
filteredGroupNamespaces() {
|
||||
if (!this.hasGroupNamespaces) return [];
|
||||
return filterByName(this.data.group, this.searchTerm);
|
||||
},
|
||||
filteredUserNamespaces() {
|
||||
if (!this.hasUserNamespaces) return [];
|
||||
return filterByName(this.data.user, this.searchTerm);
|
||||
},
|
||||
selectedNamespaceText() {
|
||||
return this.selectedNamespace?.humanName || this.$options.i18n.DEFAULT_TEXT;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleSelect(item) {
|
||||
this.selectedNamespace = item;
|
||||
this.$emit('select', item);
|
||||
},
|
||||
},
|
||||
i18n,
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-dropdown :text="selectedNamespaceText" :block="fullWidth">
|
||||
<template #header>
|
||||
<gl-search-box-by-type v-model.trim="searchTerm" />
|
||||
</template>
|
||||
<div v-if="hasGroupNamespaces" class="qa-namespaces-list-groups">
|
||||
<gl-dropdown-section-header>{{ $options.i18n.GROUPS }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-for="item in filteredGroupNamespaces"
|
||||
:key="item.id"
|
||||
class="qa-namespaces-list-item"
|
||||
@click="handleSelect(item)"
|
||||
>{{ item.humanName }}</gl-dropdown-item
|
||||
>
|
||||
</div>
|
||||
<div v-if="hasUserNamespaces" class="qa-namespaces-list-users">
|
||||
<gl-dropdown-section-header>{{ $options.i18n.USERS }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
v-for="item in filteredUserNamespaces"
|
||||
:key="item.id"
|
||||
class="qa-namespaces-list-item"
|
||||
@click="handleSelect(item)"
|
||||
>{{ item.humanName }}</gl-dropdown-item
|
||||
>
|
||||
</div>
|
||||
</gl-dropdown>
|
||||
</template>
|
|
@ -119,7 +119,10 @@ class ProjectsController < Projects::ApplicationController
|
|||
|
||||
if @project.errors[:new_namespace].present?
|
||||
flash[:alert] = @project.errors[:new_namespace].first
|
||||
return redirect_to edit_project_path(@project)
|
||||
end
|
||||
|
||||
render_edit
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
|
|
|
@ -76,7 +76,10 @@ module Repositories
|
|||
existing_oids = project.lfs_objects_oids(oids: objects_oids)
|
||||
|
||||
objects.each do |object|
|
||||
object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
|
||||
next if existing_oids.include?(object[:oid])
|
||||
next if should_auto_link? && oids_from_fork.include?(object[:oid]) && link_to_project!(object)
|
||||
|
||||
object[:actions] = upload_actions(object)
|
||||
end
|
||||
|
||||
objects
|
||||
|
@ -150,6 +153,26 @@ module Repositories
|
|||
|
||||
Gitlab::LfsToken.new(user).basic_encoding
|
||||
end
|
||||
|
||||
def should_auto_link?
|
||||
return false unless Feature.enabled?(:lfs_auto_link_fork_source, project)
|
||||
return false unless project.forked?
|
||||
|
||||
# Sanity check in case for some reason the user doesn't have access to the parent
|
||||
can?(user, :download_code, project.fork_source)
|
||||
end
|
||||
|
||||
def oids_from_fork
|
||||
@oids_from_fork ||= project.lfs_objects_oids_from_fork_source(oids: objects_oids)
|
||||
end
|
||||
|
||||
def link_to_project!(object)
|
||||
lfs_object = LfsObject.for_oid_and_size(object[:oid], object[:size])
|
||||
|
||||
return unless lfs_object
|
||||
|
||||
LfsObjectsProject.link_to_project!(lfs_object, project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -88,6 +88,13 @@ module NamespacesHelper
|
|||
group.namespace_settings.public_send(method_name, **args) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def namespaces_as_json(selected = :current_user)
|
||||
{
|
||||
group: formatted_namespaces(current_user.manageable_groups_with_routes),
|
||||
user: formatted_namespaces([current_user.namespace])
|
||||
}.to_json
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Many importers create a temporary Group, so use the real
|
||||
|
@ -119,6 +126,17 @@ module NamespacesHelper
|
|||
|
||||
[group_label.camelize, elements]
|
||||
end
|
||||
|
||||
def formatted_namespaces(namespaces)
|
||||
namespaces.sort_by(&:human_name).map! do |n|
|
||||
{
|
||||
id: n.id,
|
||||
display_path: n.full_path,
|
||||
human_name: n.human_name,
|
||||
name: n.name
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
NamespacesHelper.prepend_mod_with('NamespacesHelper')
|
||||
|
|
|
@ -13,6 +13,7 @@ class LfsObject < ApplicationRecord
|
|||
scope :with_files_stored_locally, -> { where(file_store: LfsObjectUploader::Store::LOCAL) }
|
||||
scope :with_files_stored_remotely, -> { where(file_store: LfsObjectUploader::Store::REMOTE) }
|
||||
scope :for_oids, -> (oids) { where(oid: oids) }
|
||||
scope :for_oid_and_size, -> (oid, size) { find_by(oid: oid, size: size) }
|
||||
|
||||
validates :oid, presence: true, uniqueness: true
|
||||
|
||||
|
|
|
@ -21,9 +21,19 @@ class LfsObjectsProject < ApplicationRecord
|
|||
scope :project_id_in, ->(ids) { where(project_id: ids) }
|
||||
scope :lfs_object_in, -> (lfs_objects) { where(lfs_object: lfs_objects) }
|
||||
|
||||
def self.link_to_project!(lfs_object, project)
|
||||
# We can't use an upsert here because there is no uniqueness constraint:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/347466
|
||||
self.safe_find_or_create_by!(lfs_object_id: lfs_object.id, project_id: project.id) # rubocop:disable Performance/ActiveRecordSubtransactionMethods
|
||||
end
|
||||
|
||||
def self.update_statistics_for_project_id(project_id)
|
||||
ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size]) # rubocop:disable CodeReuse/Worker
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_project_statistics
|
||||
ProjectCacheWorker.perform_async(project_id, [], [:lfs_objects_size])
|
||||
self.class.update_statistics_for_project_id(project_id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1569,6 +1569,12 @@ class Project < ApplicationRecord
|
|||
oids(lfs_objects, oids: oids)
|
||||
end
|
||||
|
||||
def lfs_objects_oids_from_fork_source(oids: [])
|
||||
return [] unless forked?
|
||||
|
||||
oids(fork_source.lfs_objects, oids: oids)
|
||||
end
|
||||
|
||||
def personal?
|
||||
!group
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ module Groups
|
|||
validate_params
|
||||
|
||||
update_shared_runners
|
||||
update_pending_builds!
|
||||
update_pending_builds_async
|
||||
|
||||
success
|
||||
|
||||
|
@ -28,12 +28,18 @@ module Groups
|
|||
group.update_shared_runners_setting!(params[:shared_runners_setting])
|
||||
end
|
||||
|
||||
def update_pending_builds!
|
||||
return unless group.previous_changes.include?('shared_runners_enabled')
|
||||
def update_pending_builds?
|
||||
group.previous_changes.include?('shared_runners_enabled')
|
||||
end
|
||||
|
||||
update_params = { instance_runners_enabled: group.shared_runners_enabled }
|
||||
def update_pending_builds_async
|
||||
return unless update_pending_builds?
|
||||
|
||||
::Ci::UpdatePendingBuildService.new(group, update_params).execute
|
||||
group.run_after_commit_or_now do |group|
|
||||
pending_builds_params = { instance_runners_enabled: group.shared_runners_enabled }
|
||||
|
||||
::Ci::UpdatePendingBuildService.new(group, pending_builds_params).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
- return unless can?(current_user, :change_namespace, @project)
|
||||
- form_id = "transfer-project-form"
|
||||
- hidden_input_id = "new_namespace_id"
|
||||
- initial_data = { namespaces: namespaces_as_json, button_text: s_('ProjectSettings|Transfer project'), confirm_danger_message: transfer_project_message(@project), phrase: @project.name, target_form_id: form_id, target_hidden_input_id: hidden_input_id }
|
||||
|
||||
.sub-section
|
||||
%h4.danger-title= _('Transfer project')
|
||||
= form_for @project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } do |f|
|
||||
= form_for @project, url: transfer_project_path(@project), method: :put, html: { class: 'js-project-transfer-form', id: form_id } do |f|
|
||||
.form-group
|
||||
- link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/settings/index', anchor: 'transferring-an-existing-project-into-another-namespace') }
|
||||
%p= _("Transfer your project into another namespace. %{link_start}Learn more.%{link_end}").html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
|
||||
|
@ -11,7 +14,6 @@
|
|||
%li= _('You can only transfer the project to namespaces you manage.')
|
||||
%li= _('You will need to update your local repositories to point to the new location.')
|
||||
%li= _('Project visibility level will be changed to match namespace rules when transferring to a group.')
|
||||
= hidden_field_tag(hidden_input_id)
|
||||
= label_tag :new_namespace_id, _('Select a new namespace'), class: 'gl-font-weight-bold'
|
||||
.form-group
|
||||
= select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2'
|
||||
= f.submit 'Transfer project', class: "gl-button btn btn-danger js-legacy-confirm-danger qa-transfer-button", data: { "confirm-danger-message" => transfer_project_message(@project) }
|
||||
.js-transfer-project-form{ data: initial_data }
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
:plain
|
||||
location.href = "#{edit_project_path(@project)}";
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: lfs_auto_link_fork_source
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75972
|
||||
rollout_issue_url:
|
||||
milestone: '14.6'
|
||||
type: development
|
||||
group: group::source code
|
||||
default_enabled: false
|
|
@ -49,6 +49,7 @@ options:
|
|||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_secure_binaries
|
||||
- p_ci_templates_security_dast_api
|
||||
- p_ci_templates_security_dast_api_latest
|
||||
- p_ci_templates_security_container_scanning
|
||||
- p_ci_templates_security_dast_latest
|
||||
- p_ci_templates_security_dependency_scanning
|
||||
|
@ -153,6 +154,7 @@ options:
|
|||
- p_ci_templates_implicit_security_api_fuzzing_latest
|
||||
- p_ci_templates_implicit_security_secure_binaries
|
||||
- p_ci_templates_implicit_security_dast_api
|
||||
- p_ci_templates_implicit_security_dast_api_latest
|
||||
- p_ci_templates_implicit_security_container_scanning
|
||||
- p_ci_templates_implicit_security_dast_latest
|
||||
- p_ci_templates_implicit_security_dependency_scanning
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_api_latest_monthly
|
||||
description: Monthly counts for DAST API latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
product_category: dynamic_application_security_testing
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.6'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_implicit_security_dast_api_latest
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_api_latest_monthly
|
||||
description: Monthly counts for DAST API latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
product_category: dynamic_application_security_testing
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "14.6"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_dast_api_latest
|
|
@ -49,6 +49,7 @@ options:
|
|||
- p_ci_templates_security_api_fuzzing_latest
|
||||
- p_ci_templates_security_secure_binaries
|
||||
- p_ci_templates_security_dast_api
|
||||
- p_ci_templates_security_dast_api_latest
|
||||
- p_ci_templates_security_container_scanning
|
||||
- p_ci_templates_security_dast_latest
|
||||
- p_ci_templates_security_dependency_scanning
|
||||
|
@ -153,6 +154,7 @@ options:
|
|||
- p_ci_templates_implicit_security_api_fuzzing_latest
|
||||
- p_ci_templates_implicit_security_secure_binaries
|
||||
- p_ci_templates_implicit_security_dast_api
|
||||
- p_ci_templates_implicit_security_dast_api_latest
|
||||
- p_ci_templates_implicit_security_container_scanning
|
||||
- p_ci_templates_implicit_security_dast_latest
|
||||
- p_ci_templates_implicit_security_dependency_scanning
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_dast_api_latest_weekly
|
||||
description: Weekly counts for DAST API latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
product_category: dynamic_application_security_testing
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: '14.6'
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_implicit_security_dast_api_latest
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_security_dast_api_latest_weekly
|
||||
description: Weekly counts for DAST API latest CI template
|
||||
product_section: sec
|
||||
product_stage: secure
|
||||
product_group: dynamic_analysis
|
||||
product_category: dynamic_application_security_testing
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "14.6"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73876
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_security_dast_api_latest
|
|
@ -0,0 +1,16 @@
|
|||
- name: "Deprecation of bundler-audit Dependency Scanning tool" # The name of the feature to be deprecated
|
||||
announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
|
||||
announcement_date: "2021-12-22" # The date of the milestone release when this feature was first announced as deprecated. This should almost always be the 22nd of a month (YYYY-MM-22), unless you did an out of band blog post.
|
||||
removal_milestone: "15.0" # The milestone when this feature is planned to be removed
|
||||
body: | # Do not modify this line, instead modify the lines below.
|
||||
As of 14.6 bundler-audit is being deprecated from Dependency Scanning. It will continue to be in our CI/CD template while deprecated. We are removing bundler-audit from Dependency Scanning on May 22, 2022 in 15.0. After this removal Ruby scanning functionality will not be affected as it is still being covered by Gemnasium.
|
||||
|
||||
If you have explicitly excluded bundler-audit using DS_EXCLUDED_ANALYZERS you will need to clean up (remove the reference) in 15.0. If you have customized your pipeline's Dependency Scanning configuration, for example to edit the `bundler-audit-dependency_scanning` job, you will want to switch to gemnasium-dependency_scanning before removal in 15.0, to prevent your pipeline from failing. If you have not used the DS_EXCLUDED_ANALYZERS to reference bundler-audit, or customized your template specifically for bundler-audit, you will not need to take action.
|
||||
# The following items are not published on the docs page, but may be used in the future.
|
||||
stage: secure # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
|
||||
tiers: ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/289832 # (optional) This is a link to the deprecation issue in GitLab
|
||||
documentation_url: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/analyzers.html # (optional) This is a link to the current documentation page
|
||||
image_url: # (optional) This is a link to a thumbnail image depicting the feature
|
||||
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
|
||||
removal_date: 2022-05-22 # (optional - may be required in the future) YYYY-MM-DD format. This should almost always be the 22nd of a month (YYYY-MM-22), the date of the milestone release when this feature is planned to be removed
|
|
@ -9,8 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
GitLab offers a way to view the changes made within the GitLab server for owners and administrators
|
||||
on a [paid plan](https://about.gitlab.com/pricing/).
|
||||
|
||||
GitLab system administrators can also take advantage of the logs located on the
|
||||
file system. See [the logs system documentation](logs.md#audit_jsonlog) for more details.
|
||||
GitLab system administrators can also view all audit events by accessing the [`audit_json.log` file](logs.md#audit_jsonlog).
|
||||
|
||||
You can:
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ GitLab has several features based on receiving incoming email messages:
|
|||
|
||||
- [Reply by Email](reply_by_email.md): allow GitLab users to comment on issues
|
||||
and merge requests by replying to notification email.
|
||||
- [New issue by email](../user/project/issues/managing_issues.md#new-issue-via-email):
|
||||
- [New issue by email](../user/project/issues/managing_issues.md#by-sending-an-email):
|
||||
allow GitLab users to create a new issue by sending an email to a
|
||||
user-specific email address.
|
||||
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email):
|
||||
|
|
|
@ -133,7 +133,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
|
|||
- Instances.
|
||||
- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance.
|
||||
- [Incoming email](incoming_email.md): Configure incoming emails to allow
|
||||
users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#new-issue-via-email) and
|
||||
users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#by-sending-an-email) and
|
||||
[merge requests by email](../user/project/merge_requests/creating_merge_requests.md#by-sending-an-email), and to enable [Service Desk](../user/project/service_desk.md).
|
||||
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
|
||||
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
|
||||
|
|
|
@ -9,14 +9,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
The CI/CD YAML reference uses a standard style to make it easier to use and update.
|
||||
|
||||
The reference information should be kept as simple as possible, and expanded details
|
||||
and examples documented in a separate page.
|
||||
and examples should be documented on other pages.
|
||||
|
||||
## YAML reference structure
|
||||
|
||||
Every YAML keyword must have its own section in the reference. The sections should
|
||||
be nested so that the keywords follow a logical tree structure. For example:
|
||||
|
||||
```plaintext
|
||||
```markdown
|
||||
### `artifacts`
|
||||
#### `artifacts:name`
|
||||
#### `artifacts:paths`
|
||||
|
@ -27,128 +27,127 @@ be nested so that the keywords follow a logical tree structure. For example:
|
|||
|
||||
## YAML reference style
|
||||
|
||||
Each keyword entry in the reference should use the following style:
|
||||
Each keyword entry in the reference:
|
||||
|
||||
````markdown
|
||||
### `keyword-name`
|
||||
- Must have a simple introductory section. The introduction should give the fundamental
|
||||
information needed to use the keyword. Advanced details and tasks should be in
|
||||
feature pages, not the reference page.
|
||||
|
||||
> Version information
|
||||
- Must use the keyword name as the title, for example:
|
||||
|
||||
Keyword description and main details.
|
||||
```markdown
|
||||
### `artifacts`
|
||||
```
|
||||
|
||||
**Keyword type**:
|
||||
- Should include the following sections:
|
||||
- [Keyword type](#keyword-type)
|
||||
- [Possible inputs](#possible-inputs)
|
||||
- [Example of `keyword-name`](#example-of-keyword-name)
|
||||
- (Optional) Can also include the following sections when needed:
|
||||
- [Additional details](#additional-details)
|
||||
- [Related topics](#related-topics)
|
||||
|
||||
The keyword name must always be in backticks without a final `:`, like `artifacts`, not `artifacts:`.
|
||||
If it is a subkey of another keyword, write out all the subkeys to the "parent" key the first time it
|
||||
is used, like `artifacts:reports:dast`. Afterwards, you can use just the subkey alone, like `dast`.
|
||||
|
||||
## Keyword type
|
||||
|
||||
The keyword can be either a job or global keyword. If it can be used in a `default`
|
||||
section, make not of that as well, for example:
|
||||
|
||||
- `**Keyword type**: Global keyword.`
|
||||
- `**Keyword type**: Job keyword. You can use it only as part of a job.`
|
||||
- ``**Keyword type**: Job keyword. You can use it only as part of a job or in the [`default:` section](#default).``
|
||||
|
||||
### Possible inputs
|
||||
|
||||
List all the possible inputs, and any extra details about the inputs, such as defaults
|
||||
or changes due to different GitLab versions, for example:
|
||||
|
||||
```markdown
|
||||
**Possible inputs**:
|
||||
|
||||
**Example of `keyword-name`**:
|
||||
|
||||
(optional) In this example...
|
||||
|
||||
(optional) **Additional details**:
|
||||
|
||||
- List of extra details.
|
||||
|
||||
(optional) **Related topics**:
|
||||
|
||||
- List of links to topics related to the keyword.
|
||||
````
|
||||
|
||||
- ``### `keyword-name` ``: The keyword name must always be in backticks.
|
||||
If it is a subkey of another keyword, write out all the keywords, with each separated
|
||||
by `:`, for example: `artifacts:reports:dast`.
|
||||
|
||||
- ``> Version information``: The [version history details](../documentation/styleguide/index.md#version-text-in-the-version-history).
|
||||
If the keyword is feature flagged, see the [feature flag documentation guide](../documentation/feature_flags.md)
|
||||
as well.
|
||||
|
||||
- `Keyword description and main details.`: A simple description of the keyword, and
|
||||
how to use it. Additional use cases and benefits should be added to a page outside
|
||||
the reference document. Link to that document in this section.
|
||||
|
||||
- `**Keyword type**:`: Most keywords are defined at the job level, like `script`,
|
||||
or at the pipeline level, like `stages`. Add the appropriate line:
|
||||
|
||||
- `**Keyword type**: Job keyword. You can use it only as part of a job.`
|
||||
- `**Keyword type**: Pipeline keyword. You cannot use it as part of a job.`
|
||||
|
||||
If a keyword can be used at both the job and pipeline level, like `variables`,
|
||||
explain it in detail instead of using the pre-written lines above.
|
||||
|
||||
- `**Possible inputs**:`: Explain in detail which inputs the keyword can accept.
|
||||
You can add the details in a sentence, paragraph, or list.
|
||||
|
||||
- ``**Example of `keyword-name`**:``: An example configuration that uses the keyword.
|
||||
Do not add extra keywords that are not required to understand the behavior.
|
||||
|
||||
- (optional) `In this example...`: If the example needs extra details,
|
||||
add the clarification text below the example.
|
||||
|
||||
- (optional) `**Additional details**:` If there are any caveats or extra details you
|
||||
want to document along with the keyword, add each one as a list item here.
|
||||
|
||||
- (optional) `**Related topics**:` If there are any other keywords or pages that
|
||||
relate to this keyword, add these links as list items here.
|
||||
|
||||
### YAML reference style example
|
||||
|
||||
See the [`only:changes` / `except:changes`](../../ci/yaml/index.md#onlychanges--exceptchanges)
|
||||
documentation for an example of the YAML reference style. The following example is a
|
||||
shortened version of that documentation's Markdown:
|
||||
|
||||
````markdown
|
||||
#### `only:changes` / `except:changes`
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/19232) in GitLab 11.4.
|
||||
|
||||
Use the `changes` keyword with `only` to run a job, or with `except` to skip a job,
|
||||
when a Git push event modifies a file.
|
||||
|
||||
Use `changes` in pipelines with the following refs:
|
||||
|
||||
- `branches`
|
||||
- `external_pull_requests`
|
||||
- `merge_requests` (see additional details about [using `only:changes` with pipelines for merge requests](../jobs/job_control.md#use-onlychanges-with-pipelines-for-merge-requests))
|
||||
|
||||
**Keyword type**: Job keyword. You can use it only as part of a job.
|
||||
|
||||
**Possible inputs**: An array including any number of:
|
||||
|
||||
- Paths to files.
|
||||
- Wildcard paths for single directories, for example `path/to/directory/*`, or a directory
|
||||
and all its subdirectories, for example `path/to/directory/**/*`.
|
||||
|
||||
**Example of `only:changes`**:
|
||||
|
||||
```yaml
|
||||
docker build:
|
||||
script: docker build -t my-image:$CI_COMMIT_REF_SLUG .
|
||||
only:
|
||||
refs:
|
||||
- branches
|
||||
changes:
|
||||
- Dockerfile
|
||||
- docker/scripts/*
|
||||
- dockerfiles/**/*
|
||||
- `true` (default if not defined) or `false`.
|
||||
```
|
||||
|
||||
In this example, `docker build` only runs in branch pipelines, and only if at least one of
|
||||
these files changed:
|
||||
```markdown
|
||||
**Possible inputs**:
|
||||
|
||||
- `Dockerfile`.
|
||||
- Any file in `docker/scripts`
|
||||
- Any file in `dockerfiles/` or any of its subdirectories.
|
||||
- A single exit code.
|
||||
- An array of exit codes.
|
||||
```
|
||||
|
||||
```markdown
|
||||
**Possible inputs**:
|
||||
|
||||
- A string with the long description.
|
||||
- The path to a file that contains the description. Introduced in [GitLab 13.7](https://gitlab.com/gitlab-org/release-cli/-/merge_requests/67).
|
||||
- The file location must be relative to the project directory (`$CI_PROJECT_DIR`).
|
||||
- If the file is a symbolic link, it must be in the `$CI_PROJECT_DIR`.
|
||||
- The `./path/to/file` and filename can't contain spaces.
|
||||
```
|
||||
|
||||
### Example of `keyword-name`
|
||||
|
||||
An example of the keyword. Use the minimum number of other keywords necessary
|
||||
to make the example valid. If the example needs explanation, add it after the example,
|
||||
for example:
|
||||
|
||||
````markdown
|
||||
**Example of `dast`**:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- build
|
||||
- dast
|
||||
|
||||
include:
|
||||
- template: DAST.gitlab-ci.yml
|
||||
|
||||
dast:
|
||||
dast_configuration:
|
||||
site_profile: "Example Co"
|
||||
scanner_profile: "Quick Passive Test"
|
||||
```
|
||||
|
||||
In this example, the `dast` job extends the `dast` configuration added with the `include:` keyword
|
||||
to select a specific site profile and scanner profile.
|
||||
````
|
||||
|
||||
### Additional details
|
||||
|
||||
The additional details should be an unordered list of extra information that is
|
||||
useful to know, but not important enough to put in the introduction. This information
|
||||
can include changes introduced in different GitLab versions. For example:
|
||||
|
||||
```markdown
|
||||
**Additional details**:
|
||||
|
||||
- If you use refs other than `branches`, `external_pull_requests`, or `merge_requests`,
|
||||
`changes` can't determine if a given file is new or old and always returns `true`.
|
||||
- If you use `only: changes` with other refs, jobs ignore the changes and always run.
|
||||
- If you use `except: changes` with other refs, jobs ignore the changes and never run.
|
||||
- The expiration time period begins when the artifact is uploaded and stored on GitLab.
|
||||
If the expiry time is not defined, it defaults to the [instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration).
|
||||
- To override the expiration date and protect artifacts from being automatically deleted:
|
||||
- Select **Keep** on the job page.
|
||||
- [In GitLab 13.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/22761), set the value of
|
||||
`expire_in` to `never`.
|
||||
```
|
||||
|
||||
### Related topics
|
||||
|
||||
The related topics should be an unordered list of crosslinks to related pages, including:
|
||||
|
||||
- Specific tasks that you can accomplish with the keyword.
|
||||
- Advanced examples of the keyword.
|
||||
- Other related keywords that can be used together with this keyword.
|
||||
|
||||
For example:
|
||||
|
||||
```markdown
|
||||
**Related topics**:
|
||||
|
||||
- [`only: changes` and `except: changes` examples](../jobs/job_control.md#onlychanges--exceptchanges-examples).
|
||||
- If you use `changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds),
|
||||
you should [also use `only:merge_requests`](../jobs/job_control.md#use-onlychanges-with-pipelines-for-merge-requests).
|
||||
- Use `changes` with [scheduled pipelines](../jobs/job_control.md#use-onlychanges-with-scheduled-pipelines).
|
||||
````
|
||||
- You can specify a [fallback cache key](../caching/index.md#use-a-fallback-cache-key)
|
||||
to use if the specified `cache:key` is not found.
|
||||
- You can [use multiple cache keys](../caching/index.md#use-multiple-caches) in a single job.
|
||||
- See the [common `cache` use cases](../caching/index.md#common-use-cases-for-caches) for more
|
||||
`cache:key` examples.
|
||||
```
|
||||
|
|
|
@ -29,7 +29,7 @@ The following are guides to basic GitLab functionality:
|
|||
- [Feature branch workflow](feature_branch_workflow.md).
|
||||
- [Fork a project](../user/project/repository/forking_workflow.md#creating-a-fork), to duplicate projects so they can be worked on in parallel.
|
||||
- [Add a file](add-file.md), to add new files to a project's repository.
|
||||
- [Create an issue](../user/project/issues/managing_issues.md#create-a-new-issue),
|
||||
- [Create an issue](../user/project/issues/managing_issues.md#create-an-issue),
|
||||
to start collaborating within a project.
|
||||
- [Create a merge request](../user/project/merge_requests/creating_merge_requests.md), to request changes made in a branch
|
||||
be merged into a project's repository.
|
||||
|
|
|
@ -116,6 +116,14 @@ Long term service and support (LTSS) for SUSE Linux Enterprise Server (SLES) 12
|
|||
|
||||
Announced: 2021-11-22
|
||||
|
||||
### Deprecation of bundler-audit Dependency Scanning tool
|
||||
|
||||
As of 14.6 bundler-audit is being deprecated from Dependency Scanning. It will continue to be in our CI/CD template while deprecated. We are removing bundler-audit from Dependency Scanning on May 22, 2022 in 15.0. After this removal Ruby scanning functionality will not be affected as it is still being covered by Gemnasium.
|
||||
|
||||
If you have explicitly excluded bundler-audit using DS_EXCLUDED_ANALYZERS you will need to clean up (remove the reference) in 15.0. If you have customized your pipeline's Dependency Scanning configuration, for example to edit the `bundler-audit-dependency_scanning` job, you will want to switch to gemnasium-dependency_scanning before removal in 15.0, to prevent your pipeline from failing. If you have not used the DS_EXCLUDED_ANALYZERS to reference bundler-audit, or customized your template specifically for bundler-audit, you will not need to take action.
|
||||
|
||||
Announced: 2021-12-22
|
||||
|
||||
### GitLab Serverless
|
||||
|
||||
[GitLab Serverless](https://docs.gitlab.com/ee/user/project/clusters/serverless/) is a feature set to support Knative-based serverless development with automatic deployments and monitoring.
|
||||
|
|
|
@ -71,7 +71,7 @@ To create the `.gitlab/issue_templates` directory:
|
|||
1. Select **New directory**.
|
||||
1. Name your directory `issue_templates` and commit to your default branch.
|
||||
|
||||
To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-a-new-issue)
|
||||
To check if this has worked correctly, [create a new issue](issues/managing_issues.md#create-an-issue)
|
||||
and see if you can choose a description template.
|
||||
|
||||
## Create a merge request template
|
||||
|
|
Before Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 8.7 KiB |
|
@ -29,7 +29,7 @@ To learn how the GitLab Strategic Marketing department uses GitLab issues with [
|
|||
|
||||
## Related topics
|
||||
|
||||
- [Create issues](managing_issues.md#create-a-new-issue)
|
||||
- [Create issues](managing_issues.md#create-an-issue)
|
||||
- [Create an issue from a template](../../project/description_templates.md#use-the-templates)
|
||||
- [Edit issues](managing_issues.md#edit-an-issue)
|
||||
- [Move issues](managing_issues.md#moving-issues)
|
||||
|
|
|
@ -4,148 +4,228 @@ group: Project Management
|
|||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Managing issues **(FREE)**
|
||||
# Manage issues **(FREE)**
|
||||
|
||||
[GitLab Issues](index.md) are the fundamental medium for collaborating on ideas and
|
||||
planning work in GitLab.
|
||||
|
||||
Key actions for issues include:
|
||||
## Create an issue
|
||||
|
||||
- [Creating issues](#create-a-new-issue)
|
||||
- [Moving issues](#moving-issues)
|
||||
- [Closing issues](#closing-issues)
|
||||
- [Deleting issues](#deleting-issues)
|
||||
- [Promoting issues](#promote-an-issue-to-an-epic) **(PREMIUM)**
|
||||
|
||||
## Create a new issue
|
||||
|
||||
When you create a new issue, you are prompted to enter the fields of the issue.
|
||||
When you create an issue, you are prompted to enter the fields of the issue.
|
||||
If you know the values you want to assign to an issue, you can use
|
||||
[quick actions](../quick_actions.md) to enter them.
|
||||
|
||||
While creating an issue, you can associate it to an existing epic from current group by
|
||||
selecting it using **Epic** dropdown.
|
||||
You can create an issue in many ways in GitLab:
|
||||
|
||||
### Accessing the New Issue form
|
||||
- [From a project](#from-a-project)
|
||||
- [From a group](#from-a-group)
|
||||
- [From another issue](#from-another-issue)
|
||||
- [From an issue board](#from-an-issue-board)
|
||||
- [By sending an email](#by-sending-an-email)
|
||||
- Using a URL with prefilled fields
|
||||
- [Using Service Desk](#using-service-desk)
|
||||
|
||||
There are many ways to get to the New Issue form from a project's page:
|
||||
### From a project
|
||||
|
||||
- Navigate to your **Project's Dashboard** > **Issues** > **New Issue**:
|
||||
Prerequisites:
|
||||
|
||||
![New issue from the issue list view](img/new_issue_from_tracker_list.png)
|
||||
- You must have at least the [Guest role](../../permissions.md) for the project.
|
||||
|
||||
- From an **open issue** in your project, click the vertical ellipsis (**{ellipsis_v}**) button
|
||||
to open a dropdown menu, and then click **New Issue** to create a new issue in the same project:
|
||||
To create an issue:
|
||||
|
||||
![New issue from an open issue](img/new_issue_from_open_issue_v13_6.png)
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. Either:
|
||||
|
||||
- From your **Project's Dashboard**, click the plus sign (**+**) to open a dropdown
|
||||
menu with a few options. Select **New Issue** to create an issue in that project:
|
||||
- On the left sidebar, select **Issues**, and then, in the top right corner, select **New issue**.
|
||||
- On the top bar, select the plus sign (**{plus-square}**) and then, under **This project**,
|
||||
select **New issue**.
|
||||
|
||||
![New issue from a project's dashboard](img/new_issue_from_projects_dashboard.png)
|
||||
1. Complete the [fields](#fields-in-the-new-issue-form).
|
||||
1. Select **Create issue**.
|
||||
|
||||
- From an **issue board**, create a new issue by clicking on the plus sign (**+**) at the top of a list.
|
||||
It opens a new issue for that project, pre-labeled with its respective list.
|
||||
The newly created issue opens.
|
||||
|
||||
![From the issue board](img/new_issue_from_issue_board.png)
|
||||
### From a group
|
||||
|
||||
### Elements of the New Issue form
|
||||
Issues belong to projects, but when you're in a group, you can access and create issues that belong
|
||||
to the projects in the group.
|
||||
|
||||
> Ability to add the new issue to an epic [was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.1.
|
||||
Prerequisites:
|
||||
|
||||
![New issue from the issues list](img/new_issue_v13_1.png)
|
||||
- You must have at least the [Guest role](../../permissions.md) for a project in the group.
|
||||
|
||||
When you're creating a new issue, these are the fields you can fill in:
|
||||
To create an issue from a group:
|
||||
|
||||
- Title
|
||||
- Description
|
||||
- Checkbox to make the issue [confidential](confidential_issues.md)
|
||||
- Assignee
|
||||
- Weight
|
||||
- [Epic](../../group/epics/index.md)
|
||||
- Due date
|
||||
- Milestone
|
||||
- Labels
|
||||
|
||||
### New issue from the group-level issue tracker
|
||||
|
||||
To visit the issue tracker for all projects in your group:
|
||||
|
||||
1. Go to the group dashboard.
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. On the left sidebar, select **Issues**.
|
||||
1. In the top-right, select the **Select project to create issue** button.
|
||||
1. In the top right corner, select **Select project to create issue**.
|
||||
1. Select the project you'd like to create an issue for. The button now reflects the selected
|
||||
project.
|
||||
1. Select the button to create an issue in the selected project.
|
||||
1. Select **New issue in `<project name>`**.
|
||||
1. Complete the [fields](#fields-in-the-new-issue-form).
|
||||
1. Select **Create issue**.
|
||||
|
||||
![Select project to create issue](img/select_project_from_group_level_issue_tracker_v13_11.png)
|
||||
The newly created issue opens.
|
||||
|
||||
The project you selected most recently becomes the default for your next visit.
|
||||
This should save you a lot of time and clicks, if you mostly create issues for the same project.
|
||||
This can save you a lot of time and clicks, if you mostly create issues for the same project.
|
||||
|
||||
### New issue via Service Desk
|
||||
### From another issue
|
||||
|
||||
Enable [Service Desk](../service_desk.md) for your project and offer email support.
|
||||
Now, when your customer sends a new email, a new issue can be created in
|
||||
the appropriate project and followed up from there.
|
||||
> New issue becoming linked to the issue of origin [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68226) in GitLab 14.3.
|
||||
|
||||
### New issue via email
|
||||
You can create a new issue from an existing one. The two issues can then be marked as related.
|
||||
|
||||
A link to **Email a new issue to this project** is displayed at the bottom of a project's
|
||||
**Issues List** page. The link is shown only if your GitLab instance has [incoming email](../../../administration/incoming_email.md)
|
||||
configured and there is at least one issue in the issue list.
|
||||
Prerequisites:
|
||||
|
||||
![Bottom of a project issues page](img/new_issue_from_email.png)
|
||||
- You must have at least the [Guest role](../../permissions.md) for the project.
|
||||
|
||||
When you click this link, an email address is generated and displayed, which should be used
|
||||
by **you only**, to create issues in this project. You can save this address as a
|
||||
contact in your email client for quick access.
|
||||
To create an issue from another issue:
|
||||
|
||||
1. In an existing issue, select the vertical ellipsis (**{ellipsis_v}**).
|
||||
1. Select **New issue**.
|
||||
1. Complete the [fields](#fields-in-the-new-issue-form).
|
||||
The new issue's description is prefilled with `Related to #123`, where `123` is the ID of the
|
||||
issue of origin. If you keep this mention in the description, the two issues become
|
||||
[linked](related_issues.md).
|
||||
1. Select **Create issue**.
|
||||
|
||||
The newly created issue opens.
|
||||
|
||||
### From an issue board
|
||||
|
||||
You can create a new issue from an [issue board](../issue_board.md).
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the [Guest role](../../permissions.md) for the project.
|
||||
|
||||
To create an issue from a project issue board:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. Select **Issues > Boards**.
|
||||
1. At the top of a board list, select **New issue** (**{plus-square}**).
|
||||
1. Enter the issue's title.
|
||||
1. Select **Create issue**.
|
||||
|
||||
To create an issue from a group issue board:
|
||||
|
||||
1. On the top bar, select **Menu > Groups** and find your group.
|
||||
1. Select **Issues > Boards**.
|
||||
1. At the top of a board list, select **New issue** (**{plus-square}**).
|
||||
1. Enter the issue's title.
|
||||
1. Under **Projects**, select the project in the group that the issue should belong to.
|
||||
1. Select **Create issue**.
|
||||
|
||||
The issue is created and shows up in the board list. It shares the list's characteristic, so, for
|
||||
example, if the list is scoped to a label `Frontend`, the new issue also has this label.
|
||||
|
||||
### By sending an email
|
||||
|
||||
> Generated email address format changed in GitLab 11.7.
|
||||
> The older format is still supported, so existing aliases and contacts still work.
|
||||
|
||||
You can send an email to create an issue in a project on the project's
|
||||
**Issues List** page.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Your GitLab instance must have [incoming email](../../../administration/incoming_email.md)
|
||||
configured.
|
||||
- There must be at least one issue in the issue list.
|
||||
- You must have at least the [Guest role](../../permissions.md) for the project.
|
||||
|
||||
To email an issue to a project:
|
||||
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. Select **Issues**.
|
||||
1. At the bottom of the page, select **Email a new issue to this project**.
|
||||
1. To copy the email address, select **Copy** (**{copy-to-clipboard}**).
|
||||
1. From your email client, send an email to this address.
|
||||
The subject is used as the title of the new issue, and the email body becomes the description.
|
||||
You can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md).
|
||||
|
||||
A new issue is created, with your user as the author.
|
||||
You can save this address as a contact in your email client to use it again.
|
||||
|
||||
WARNING:
|
||||
This is a private email address, generated just for you. **Keep it to yourself**,
|
||||
as anyone who knows it can create issues or merge requests as if they
|
||||
were you. If the address is compromised, or you want to regenerate it,
|
||||
click **Email a new issue to this project**, followed by **reset it**.
|
||||
The email address you see is a private email address, generated just for you.
|
||||
**Keep it to yourself**, because anyone who knows it can create issues or merge requests as if they
|
||||
were you.
|
||||
|
||||
Sending an email to this address creates a new issue associated with your account for
|
||||
this project, where:
|
||||
To regenerate the email address:
|
||||
|
||||
- The email subject becomes the issue title.
|
||||
- The email body becomes the issue description.
|
||||
- [Markdown](../../markdown.md) and [quick actions](../quick_actions.md) are supported.
|
||||
1. On the issues list, select **Email a new issue to this project**.
|
||||
1. Select **reset this token**.
|
||||
|
||||
NOTE:
|
||||
In GitLab 11.7, we updated the format of the generated email address. However the
|
||||
older format is still supported, allowing existing aliases or contacts to continue working.
|
||||
|
||||
### New issue via URL with prefilled fields
|
||||
### Using a URL with prefilled values
|
||||
|
||||
To link directly to the new issue page with prefilled fields, use query
|
||||
string parameters in a URL. You can embed a URL in an external
|
||||
HTML page to create issues with certain
|
||||
fields prefilled.
|
||||
HTML page to create issues with certain fields prefilled.
|
||||
|
||||
| Field | URL Parameter Name | Notes |
|
||||
|----------------------|-----------------------|-------------------------------------------------------|
|
||||
| title | `issue[title]` | |
|
||||
| description | `issue[description]` | Cannot be used at the same time as `issuable_template`. |
|
||||
| description template | `issuable_template` | Cannot be used at the same time as `issue[description]`. |
|
||||
| issue type | `issue[issue_type]` | Either `incident` or `issue`. |
|
||||
| confidential | `issue[confidential]` | Parameter value must be `true` to set to confidential. |
|
||||
| Field | URL parameter | Notes |
|
||||
| -------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Title | `issue[title]` | Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
|
||||
| Issue type | `issue[issue_type]` | Either `incident` or `issue`. |
|
||||
| Description template | `issuable_template` | Cannot be used at the same time as `issue[description]`. Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
|
||||
| Description | `issue[description]` | Cannot be used at the same time as `issuable_template`. Must be [URL-encoded](../../../api/index.md#namespaced-path-encoding). |
|
||||
| Confidential | `issue[confidential]` | If `true`, the issue is marked as confidential. |
|
||||
|
||||
Follow these examples to form your new issue URL with prefilled fields.
|
||||
Adapt these examples to form your new issue URL with prefilled fields.
|
||||
To create an issue in the GitLab project:
|
||||
|
||||
- For a new issue in the GitLab Community Edition project with a pre-filled title
|
||||
and a pre-filled description, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
|
||||
- For a new issue in the GitLab Community Edition project with a pre-filled title
|
||||
and a pre-filled description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
|
||||
- For a new issue in the GitLab Community Edition project with a pre-filled title,
|
||||
a pre-filled description, and the confidential flag set, the URL would be `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true`
|
||||
- With a prefilled title and description:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Whoa%2C%20we%27re%20half-way%20there&issue[description]=Whoa%2C%20livin%27%20in%20a%20URL
|
||||
```
|
||||
|
||||
- With a prefilled title and description template:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Feature%20Proposal%20-%20basic
|
||||
```
|
||||
|
||||
- With a prefilled title, description, and marked as confidential:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea&issue[confidential]=true
|
||||
```
|
||||
|
||||
### Using Service Desk
|
||||
|
||||
To offer email support, enable [Service Desk](../service_desk.md) for your project.
|
||||
|
||||
Now, when your customer sends a new email, a new issue can be created in
|
||||
the appropriate project and followed up from there.
|
||||
|
||||
### Fields in the new issue form
|
||||
|
||||
> Adding the new issue to an epic [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847) in GitLab 13.1.
|
||||
|
||||
When you're creating a new issue, you can complete the following fields:
|
||||
|
||||
- Title
|
||||
- Type: either issue (default) or incident
|
||||
- [Description template](../description_templates.md): overwrites anything in the Description text box
|
||||
- Description: you can use [Markdown](../../markdown.md) and [quick actions](../quick_actions.md)
|
||||
- Checkbox to make the issue [confidential](confidential_issues.md)
|
||||
- [Assignees](#assignee)
|
||||
- [Weight](issue_weight.md)
|
||||
- [Epic](../../group/epics/index.md)
|
||||
- [Due date](due_dates.md)
|
||||
- [Milestone](../milestones/index.md)
|
||||
- [Labels](../labels.md)
|
||||
|
||||
## Edit an issue
|
||||
|
||||
You can edit an issue's title and description.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- You must have at least the [Reporter role](../../permissions.md) for a project.
|
||||
|
||||
To edit an issue, select **Edit title and description** (**{pencil}**).
|
||||
|
||||
### Bulk edit issues at the project level
|
||||
|
|
|
@ -4,7 +4,7 @@ group: Workspace
|
|||
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
|
||||
---
|
||||
|
||||
# Working with projects **(FREE)**
|
||||
# Manage projects **(FREE)**
|
||||
|
||||
Most work in GitLab is done in a [project](../../user/project/index.md). Files and
|
||||
code are saved in projects, and most features are in the scope of projects.
|
||||
|
@ -16,7 +16,7 @@ To explore projects:
|
|||
1. On the top bar, select **Menu > Projects**.
|
||||
1. Select **Explore projects**.
|
||||
|
||||
GitLab displays a list of projects, sorted by last updated date.
|
||||
The **Projects** page shows a list of projects, sorted by last updated date.
|
||||
|
||||
- To view projects with the most [stars](#star-a-project), select **Most stars**.
|
||||
- To view projects with the largest number of comments in the past month, select **Trending**.
|
||||
|
@ -26,14 +26,37 @@ The **Explore projects** tab is visible to unauthenticated users unless the
|
|||
[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
|
||||
is restricted. Then the tab is visible only to signed-in users.
|
||||
|
||||
### Who can view the **Projects** page
|
||||
|
||||
When you select a project, the project landing page shows the project contents.
|
||||
|
||||
For public projects, and members of internal and private projects
|
||||
with [permissions to view the project's code](../permissions.md#project-members-permissions),
|
||||
the project landing page shows:
|
||||
|
||||
- A [`README` or index file](repository/index.md#readme-and-index-files).
|
||||
- A list of directories in the project's repository.
|
||||
|
||||
For users without permission to view the project's code, the landing page shows:
|
||||
|
||||
- The wiki homepage.
|
||||
- The list of issues in the project.
|
||||
|
||||
### Access a project page with the project ID
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53671) in GitLab 11.8.
|
||||
|
||||
To access a project from the GitLab UI using the project ID,
|
||||
visit the `/projects/:id` URL in your browser or other tool accessing the project.
|
||||
|
||||
## Explore topics
|
||||
|
||||
You can explore popular project topics available on GitLab. To explore project topics:
|
||||
To explore project topics:
|
||||
|
||||
1. On the top bar, select **Menu > Projects**.
|
||||
1. Select **Explore topics**.
|
||||
|
||||
GitLab displays a list of topics sorted by the number of associated projects.
|
||||
The **Projects** page shows list of topics sorted by the number of associated projects.
|
||||
To view projects associated with a topic, select a topic from the list.
|
||||
|
||||
You can assign topics to a project on the [Project Settings page](settings/index.md#topics).
|
||||
|
@ -260,9 +283,8 @@ To add a star to a project:
|
|||
|
||||
## Delete a project
|
||||
|
||||
After you delete a project, projects in personal namespaces are deleted immediately. You can
|
||||
[enable delayed project removal](../group/index.md#enable-delayed-project-deletion) to
|
||||
delay deletion of projects in a group.
|
||||
After you delete a project, projects in personal namespaces are deleted immediately. To delay deletion of projects in a group
|
||||
you can [enable delayed project removal](../group/index.md#enable-delayed-project-deletion).
|
||||
|
||||
To delete a project:
|
||||
|
||||
|
@ -299,56 +321,27 @@ To leave a project:
|
|||
on the project dashboard when a project is part of a group under a
|
||||
[group namespace](../group/index.md#namespaces).
|
||||
|
||||
## Use your project as a Go package
|
||||
## Use a project as a Go package
|
||||
|
||||
Any project can be used as a Go package. GitLab responds correctly to `go get`
|
||||
and `godoc.org` discovery requests, including the
|
||||
[`go-import`](https://golang.org/cmd/go/#hdr-Remote_import_paths) and
|
||||
[`go-source`](https://github.com/golang/gddo/wiki/Source-Code-Links) meta tags.
|
||||
Prerequisites:
|
||||
|
||||
Private projects, including projects in subgroups, can be used as a Go package.
|
||||
These projects may require configuration to work correctly. GitLab responds correctly
|
||||
to `go get` discovery requests for projects that *are not* in subgroups,
|
||||
regardless of authentication or authorization.
|
||||
[Authentication](#authenticate-go-requests) is required to use a private project
|
||||
in a subgroup as a Go package. Otherwise, GitLab truncates the path for
|
||||
private projects in subgroups to the first two segments, causing `go get` to
|
||||
fail.
|
||||
- Contact your administrator to enable the [GitLab Go Proxy](../packages/go_proxy/index.md).
|
||||
- To use a private project in a subgroup as a Go package, you must [authenticate Go requests](#authenticate-go-requests-to-private-projects). Go requests that are not authenticated cause
|
||||
`go get` to fail. You don't need to authenticate Go requests for projects that are not in subgroups.
|
||||
|
||||
GitLab implements its own Go proxy. This feature must be enabled by an
|
||||
administrator and requires additional configuration. See [GitLab Go
|
||||
Proxy](../packages/go_proxy/index.md).
|
||||
To use a project as a Go package, use the `go get` and `godoc.org` discovery requests. You can use the meta tags:
|
||||
|
||||
### Disable Go module features for private projects
|
||||
- [`go-import`](https://golang.org/cmd/go/#hdr-Remote_import_paths)
|
||||
- [`go-source`](https://github.com/golang/gddo/wiki/Source-Code-Links)
|
||||
|
||||
In Go 1.12 and later, Go queries module proxies and checksum databases in the
|
||||
process of [fetching a
|
||||
module](../../development/go_guide/dependencies.md#fetching). This can be
|
||||
selectively disabled with `GOPRIVATE` (disable both),
|
||||
[`GONOPROXY`](../../development/go_guide/dependencies.md#proxies) (disable proxy
|
||||
queries), and [`GONOSUMDB`](../../development/go_guide/dependencies.md#fetching)
|
||||
(disable checksum queries).
|
||||
### Authenticate Go requests to private projects
|
||||
|
||||
`GOPRIVATE`, `GONOPROXY`, and `GONOSUMDB` are comma-separated lists of Go
|
||||
modules and Go module prefixes. For example,
|
||||
`GOPRIVATE=gitlab.example.com/my/private/project` disables queries for that
|
||||
one project, but `GOPRIVATE=gitlab.example.com` disables queries for *all*
|
||||
projects on GitLab.com. Go does not query module proxies if the module name or a
|
||||
prefix of it appears in `GOPRIVATE` or `GONOPROXY`. Go does not query checksum
|
||||
databases if the module name or a prefix of it appears in `GONOPRIVATE` or
|
||||
`GONOSUMDB`.
|
||||
Prerequisites:
|
||||
|
||||
### Authenticate Go requests
|
||||
- Your GitLab instance must be accessible with HTTPS.
|
||||
- You must have a [personal access token](../profile/personal_access_tokens.md).
|
||||
|
||||
To authenticate requests to private projects made by Go, use a [`.netrc`
|
||||
file](https://everything.curl.dev/usingcurl/netrc) and a [personal access
|
||||
token](../profile/personal_access_tokens.md) in the password field. **This only
|
||||
works if your GitLab instance can be accessed with HTTPS.** The `go` command
|
||||
does not transmit credentials over insecure connections. This authenticates
|
||||
all HTTPS requests made directly by Go, but does not authenticate requests made
|
||||
through Git.
|
||||
|
||||
For example:
|
||||
To authenticate Go requests, create a [`.netrc`](https://everything.curl.dev/usingcurl/netrc) file with the following information:
|
||||
|
||||
```plaintext
|
||||
machine gitlab.example.com
|
||||
|
@ -356,98 +349,106 @@ login <gitlab_user_name>
|
|||
password <personal_access_token>
|
||||
```
|
||||
|
||||
NOTE:
|
||||
On Windows, Go reads `~/_netrc` instead of `~/.netrc`.
|
||||
|
||||
### Authenticate Git fetches
|
||||
The `go` command does not transmit credentials over insecure connections. It authenticates
|
||||
HTTPS requests made by Go, but does not authenticate requests made
|
||||
through Git.
|
||||
|
||||
If a module cannot be fetched from a proxy, Go falls back to using Git (for
|
||||
GitLab projects). Git uses `.netrc` to authenticate requests. You can also
|
||||
configure Git to either:
|
||||
### Authenticate Git requests
|
||||
|
||||
- Embed specific credentials in the request URL.
|
||||
- Use SSH instead of HTTPS, as Go always uses HTTPS to fetch Git repositories.
|
||||
If Go cannot fetch a module from a proxy, it uses Git. Git uses a `.netrc` file to authenticate requests, but you can
|
||||
configure other authentication methods.
|
||||
|
||||
```shell
|
||||
# Embed credentials in any request to GitLab.com:
|
||||
git config --global url."https://${user}:${personal_access_token}@gitlab.example.com".insteadOf "https://gitlab.example.com"
|
||||
Configure Git to either:
|
||||
|
||||
# Use SSH instead of HTTPS:
|
||||
git config --global url."git@gitlab.example.com".insteadOf "https://gitlab.example.com"
|
||||
```
|
||||
- Embed credentials in the request URL:
|
||||
|
||||
```shell
|
||||
git config --global url."https://${user}:${personal_access_token}@gitlab.example.com".insteadOf "https://gitlab.example.com"
|
||||
```
|
||||
|
||||
- Use SSH instead of HTTPS:
|
||||
|
||||
```shell
|
||||
git config --global url."git@gitlab.example.com".insteadOf "https://gitlab.example.com"
|
||||
```
|
||||
|
||||
### Disable Go module fetching for private projects
|
||||
|
||||
To [fetch modules or packages](../../development/go_guide/dependencies.md#fetching), Go uses
|
||||
the [environment variables](../../development/go_guide/dependencies.md#proxies):
|
||||
|
||||
- `GOPRIVATE`
|
||||
- `GONOPROXY`
|
||||
- `GONOSUMDB`
|
||||
|
||||
To disable fetching:
|
||||
|
||||
1. Disable `GOPRIVATE`:
|
||||
- To disable queries for one project, disable `GOPRIVATE=gitlab.example.com/my/private/project`.
|
||||
- To disable queries for all projects on GitLab.com, disable `GOPRIVATE=gitlab.example.com`.
|
||||
1. Disable proxy queries in `GONOPROXY`.
|
||||
1. Disable checksum queries in `GONOSUMDB`.
|
||||
|
||||
- If the module name or its prefix is in `GOPRIVATE` or `GONOPROXY`, Go does not query module
|
||||
proxies.
|
||||
- If the module name or its prefix is in `GONOPRIVATE` or `GONOSUMDB`, Go does not query
|
||||
Checksum databases.
|
||||
|
||||
### Fetch Go modules from Geo secondary sites
|
||||
|
||||
As Go modules are stored in Git repositories, you can use the [Geo](../../administration/geo/index.md)
|
||||
feature that allows Git repositories to be accessed on the secondary Geo servers.
|
||||
Use [Geo](../../administration/geo/index.md) to access Git repositories that contain Go modules
|
||||
on secondary Geo servers.
|
||||
|
||||
In the following examples, the primary's site domain name is `gitlab.example.com`,
|
||||
and the secondary's is `gitlab-secondary.example.com`.
|
||||
You can use SSH or HTTP to access the Geo secondary server.
|
||||
|
||||
`go get` will initially generate some HTTP traffic to the primary, but when the module
|
||||
download commences, the `insteadOf` configuration sends the traffic to the secondary.
|
||||
#### Use SSH to access the Geo secondary server
|
||||
|
||||
#### Use SSH to access the Geo secondary
|
||||
|
||||
To fetch Go modules from the secondary using SSH:
|
||||
To access the Geo secondary server with SSH:
|
||||
|
||||
1. Reconfigure Git on the client to send traffic for the primary to the secondary:
|
||||
|
||||
```plaintext
|
||||
```shell
|
||||
git config --global url."git@gitlab-secondary.example.com".insteadOf "https://gitlab.example.com"
|
||||
git config --global url."git@gitlab-secondary.example.com".insteadOf "http://gitlab.example.com"
|
||||
```
|
||||
|
||||
1. Ensure the client is set up for SSH access to GitLab repositories. This can be tested on the primary,
|
||||
and GitLab will replicate the public key to the secondary.
|
||||
- For `gitlab.example.com`, use the primary site domain name.
|
||||
- For `gitlab-secondary.example.com`, use the secondary site domain name.
|
||||
|
||||
1. Ensure the client is set up for SSH access to GitLab repositories. You can test this on the primary,
|
||||
and GitLab replicates the public key to the secondary.
|
||||
|
||||
The `go get` request generates HTTP traffic to the primary Geo server. When the module
|
||||
download starts, the `insteadOf` configuration sends the traffic to the secondary Geo server.
|
||||
|
||||
#### Use HTTP to access the Geo secondary
|
||||
|
||||
Using HTTP to fetch Go modules does not work with CI/CD job tokens, only with
|
||||
persistent access tokens that are replicated to the secondary.
|
||||
You must use persistent access tokens that replicate to the secondary server. You cannot use
|
||||
CI/CD job tokens to fetch Go modules with HTTP.
|
||||
|
||||
To fetch Go modules from the secondary using HTTP:
|
||||
To access the Geo secondary server with HTTP:
|
||||
|
||||
1. Put in place a Git `insteadOf` redirect on the client:
|
||||
1. Add a Git `insteadOf` redirect on the client:
|
||||
|
||||
```plaintext
|
||||
```shell
|
||||
git config --global url."https://gitlab-secondary.example.com".insteadOf "https://gitlab.example.com"
|
||||
```
|
||||
|
||||
1. Generate a [personal access token](../profile/personal_access_tokens.md) and
|
||||
provide those credentials in the client's `~/.netrc` file:
|
||||
- For `gitlab.example.com`, use the primary site domain name.
|
||||
- For `gitlab-secondary.example.com`, use the secondary site domain name.
|
||||
|
||||
```plaintext
|
||||
1. Generate a [personal access token](../profile/personal_access_tokens.md) and
|
||||
add the credentials in the client's `~/.netrc` file:
|
||||
|
||||
```shell
|
||||
machine gitlab.example.com login USERNAME password TOKEN
|
||||
machine gitlab-secondary.example.com login USERNAME password TOKEN
|
||||
```
|
||||
|
||||
## Access project page with project ID
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/53671) in GitLab 11.8.
|
||||
|
||||
To quickly access a project from the GitLab UI using the project ID,
|
||||
visit the `/projects/:id` URL in your browser or other tool accessing the project.
|
||||
|
||||
## Project's landing page
|
||||
|
||||
The project's landing page shows different information depending on
|
||||
the project's visibility settings and user permissions.
|
||||
|
||||
For public projects, and to members of internal and private projects
|
||||
with [permissions to view the project's code](../permissions.md#project-members-permissions):
|
||||
|
||||
- The content of a
|
||||
[`README` or an index file](repository/index.md#readme-and-index-files)
|
||||
is displayed (if any), followed by the list of directories in the
|
||||
project's repository.
|
||||
- If the project doesn't contain either of these files, the
|
||||
visitor sees the list of files and directories of the repository.
|
||||
|
||||
For users without permissions to view the project's code, GitLab displays:
|
||||
|
||||
- The wiki homepage, if any.
|
||||
- The list of issues in the project.
|
||||
The `go get` request generates HTTP traffic to the primary Geo server. When the module
|
||||
download starts, the `insteadOf` configuration sends the traffic to the secondary Geo server.
|
||||
|
||||
## Related topics
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
variables:
|
||||
FUZZAPI_VERSION: "1"
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||
FUZZAPI_IMAGE: ${SECURE_ANALYZERS_PREFIX}/api-fuzzing:${FUZZAPI_VERSION}
|
||||
FUZZAPI_IMAGE: api-fuzzing
|
||||
|
||||
apifuzzer_fuzz:
|
||||
stage: fuzz
|
||||
image: $FUZZAPI_IMAGE
|
||||
image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $API_FUZZING_DISABLED
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.gitlab-ci.yml
|
||||
|
||||
# To use this template, add the following to your .gitlab-ci.yml file:
|
||||
#
|
||||
# include:
|
||||
# template: DAST-API.latest.gitlab-ci.yml
|
||||
#
|
||||
# You also need to add a `dast` stage to your `stages:` configuration. A sample configuration for DAST API:
|
||||
#
|
||||
# stages:
|
||||
# - build
|
||||
# - test
|
||||
# - deploy
|
||||
# - dast
|
||||
|
||||
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html
|
||||
|
||||
# Configure DAST API scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
|
||||
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html#available-cicd-variables
|
||||
|
||||
variables:
|
||||
# Setting this variable affects all Security templates
|
||||
# (SAST, Dependency Scanning, ...)
|
||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
|
||||
#
|
||||
DAST_API_VERSION: "1"
|
||||
DAST_API_IMAGE: api-fuzzing
|
||||
|
||||
dast_api:
|
||||
stage: dast
|
||||
image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION
|
||||
allow_failure: true
|
||||
rules:
|
||||
- if: $DAST_API_DISABLED
|
||||
when: never
|
||||
- if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH &&
|
||||
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
script:
|
||||
- /peach/analyzer-dast-api
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- gl-assets
|
||||
- gl-dast-api-report.json
|
||||
- gl-*.log
|
||||
reports:
|
||||
dast: gl-dast-api-report.json
|
|
@ -11,19 +11,20 @@ module Gitlab
|
|||
end
|
||||
|
||||
def trace(key, data)
|
||||
result = yield
|
||||
|
||||
yield
|
||||
rescue StandardError => e
|
||||
data[:exception] = e
|
||||
raise e
|
||||
ensure
|
||||
case key
|
||||
when "execute_query"
|
||||
log_execute_query(**data)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_execute_query(query: nil, duration_s: 0)
|
||||
def log_execute_query(query: nil, duration_s: 0, exception: nil)
|
||||
# execute_query should always have :query, but we're just being defensive
|
||||
return unless query
|
||||
|
||||
|
@ -39,6 +40,8 @@ module Gitlab
|
|||
query_string: query.query_string
|
||||
}
|
||||
|
||||
Gitlab::ExceptionLogFormatter.format!(exception, info)
|
||||
|
||||
info.merge!(::Gitlab::ApplicationContext.current)
|
||||
info.merge!(analysis_info) if analysis_info
|
||||
|
||||
|
|
|
@ -17,13 +17,9 @@ module Gitlab
|
|||
def trace(key, data)
|
||||
start_time = Gitlab::Metrics::System.monotonic_time
|
||||
|
||||
result = yield
|
||||
|
||||
duration_s = Gitlab::Metrics::System.monotonic_time - start_time
|
||||
|
||||
data[:duration_s] = duration_s
|
||||
|
||||
result
|
||||
yield
|
||||
ensure
|
||||
data[:duration_s] = Gitlab::Metrics::System.monotonic_time - start_time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -119,6 +119,10 @@
|
|||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
aggregation: weekly
|
||||
- name: p_ci_templates_security_dast_api_latest
|
||||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
aggregation: weekly
|
||||
- name: p_ci_templates_security_container_scanning
|
||||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
|
@ -551,6 +555,10 @@
|
|||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
aggregation: weekly
|
||||
- name: p_ci_templates_implicit_security_dast_api_latest
|
||||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
aggregation: weekly
|
||||
- name: p_ci_templates_implicit_security_container_scanning
|
||||
category: ci_templates
|
||||
redis_slot: ci_templates
|
||||
|
|
|
@ -5,7 +5,6 @@ module QA
|
|||
module Project
|
||||
module Settings
|
||||
class Advanced < Page::Base
|
||||
include Component::Select2
|
||||
include Component::ConfirmModal
|
||||
|
||||
view 'app/views/projects/edit.html.haml' do
|
||||
|
@ -13,8 +12,10 @@ module QA
|
|||
element :change_path_button
|
||||
end
|
||||
|
||||
view 'app/views/projects/_transfer.html.haml' do
|
||||
element :transfer_button
|
||||
view "app/assets/javascripts/vue_shared/components/namespace_select/namespace_select.vue" do
|
||||
element :namespaces_list
|
||||
element :namespaces_list_groups
|
||||
element :namespaces_list_item
|
||||
end
|
||||
|
||||
view 'app/views/projects/settings/_archive.html.haml' do
|
||||
|
@ -42,16 +43,22 @@ module QA
|
|||
click_element :change_path_button
|
||||
end
|
||||
|
||||
def select_namespace(item)
|
||||
click_element :namespaces_list
|
||||
|
||||
within_element(:namespaces_list) do
|
||||
find_element(:namespaces_list_item, text: item).click
|
||||
end
|
||||
end
|
||||
|
||||
def transfer_project!(project_name, namespace)
|
||||
QA::Runtime::Logger.info "Transferring project: #{project_name} to namespace: #{namespace}"
|
||||
|
||||
click_element_coordinates(:archive_project_content)
|
||||
|
||||
expand_select_list
|
||||
|
||||
# Workaround for a failure to search when there are no spaces around the /
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/218965
|
||||
search_and_select(namespace.gsub(%r{([^\s])/([^\s])}, '\1 / \2'))
|
||||
select_namespace(namespace.gsub(%r{([^\s])/([^\s])}, '\1 / \2'))
|
||||
|
||||
click_element(:transfer_button)
|
||||
fill_confirmation_text(project_name)
|
||||
|
|
|
@ -899,10 +899,34 @@ RSpec.describe ProjectsController do
|
|||
describe '#transfer', :enable_admin_mode do
|
||||
render_views
|
||||
|
||||
let_it_be(:project, reload: true) { create(:project) }
|
||||
let(:project) { create(:project) }
|
||||
|
||||
let_it_be(:admin) { create(:admin) }
|
||||
let_it_be(:new_namespace) { create(:namespace) }
|
||||
|
||||
shared_examples 'project namespace is not changed' do |flash_message|
|
||||
it 'project namespace is not changed' do
|
||||
controller.instance_variable_set(:@project, project)
|
||||
sign_in(admin)
|
||||
|
||||
old_namespace = project.namespace
|
||||
|
||||
put :transfer,
|
||||
params: {
|
||||
namespace_id: old_namespace.path,
|
||||
new_namespace_id: new_namespace_id,
|
||||
id: project.path
|
||||
},
|
||||
format: :js
|
||||
|
||||
project.reload
|
||||
|
||||
expect(project.namespace).to eq(old_namespace)
|
||||
expect(response).to redirect_to(edit_project_path(project))
|
||||
expect(flash[:alert]).to eq flash_message
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates namespace' do
|
||||
sign_in(admin)
|
||||
|
||||
|
@ -921,26 +945,15 @@ RSpec.describe ProjectsController do
|
|||
end
|
||||
|
||||
context 'when new namespace is empty' do
|
||||
it 'project namespace is not changed' do
|
||||
controller.instance_variable_set(:@project, project)
|
||||
sign_in(admin)
|
||||
let(:new_namespace_id) { nil }
|
||||
|
||||
old_namespace = project.namespace
|
||||
|
||||
put :transfer,
|
||||
params: {
|
||||
namespace_id: old_namespace.path,
|
||||
new_namespace_id: nil,
|
||||
id: project.path
|
||||
},
|
||||
format: :js
|
||||
|
||||
project.reload
|
||||
|
||||
expect(project.namespace).to eq(old_namespace)
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(flash[:alert]).to eq s_('TransferProject|Please select a new namespace for your project.')
|
||||
it_behaves_like 'project namespace is not changed', s_('TransferProject|Please select a new namespace for your project.')
|
||||
end
|
||||
|
||||
context 'when new namespace is the same as the current namespace' do
|
||||
let(:new_namespace_id) { project.namespace.id }
|
||||
|
||||
it_behaves_like 'project namespace is not changed', s_('TransferProject|Project is already in this namespace.')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -44,4 +44,90 @@ RSpec.describe 'Blob shortcuts', :js do
|
|||
include_examples 'quotes the selected text'
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "opens assignee dropdown for editing" do
|
||||
it "opens assignee dropdown for editing" do
|
||||
find('body').native.send_key('a')
|
||||
|
||||
expect(find('.block.assignee')).to have_selector('.js-sidebar-assignee-data')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'pressing "a"' do
|
||||
describe 'On an Issue' do
|
||||
before do
|
||||
stub_feature_flags(issue_assignees_widget: false)
|
||||
visit project_issue_path(project, issue)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens assignee dropdown for editing'
|
||||
end
|
||||
|
||||
describe 'On a Merge Request' do
|
||||
before do
|
||||
stub_feature_flags(issue_assignees_widget: false)
|
||||
visit project_merge_request_path(project, merge_request)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens assignee dropdown for editing'
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "opens milestones dropdown for editing" do
|
||||
it "opens milestones dropdown for editing" do
|
||||
find('body').native.send_key('m')
|
||||
|
||||
expect(find('[data-testid="milestone-edit"]')).to have_selector('.gl-new-dropdown-inner')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'pressing "m"' do
|
||||
describe 'On an Issue' do
|
||||
before do
|
||||
visit project_issue_path(project, issue)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens milestones dropdown for editing'
|
||||
end
|
||||
|
||||
describe 'On a Merge Request' do
|
||||
before do
|
||||
visit project_merge_request_path(project, merge_request)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens milestones dropdown for editing'
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples "opens labels dropdown for editing" do
|
||||
it "opens labels dropdown for editing" do
|
||||
find('body').native.send_key('l')
|
||||
|
||||
expect(find('.js-labels-block')).to have_selector('[data-testid="labels-select-dropdown-contents"]')
|
||||
end
|
||||
end
|
||||
|
||||
describe 'pressing "l"' do
|
||||
describe 'On an Issue' do
|
||||
before do
|
||||
visit project_issue_path(project, issue)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens labels dropdown for editing'
|
||||
end
|
||||
|
||||
describe 'On a Merge Request' do
|
||||
before do
|
||||
visit project_merge_request_path(project, merge_request)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
include_examples 'opens labels dropdown for editing'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,6 +8,8 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
|
|||
let(:group) { create(:group) }
|
||||
|
||||
before do
|
||||
stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 120)
|
||||
|
||||
group.add_owner(user)
|
||||
sign_in(user)
|
||||
end
|
||||
|
@ -16,10 +18,12 @@ RSpec.describe 'Projects > Settings > User transfers a project', :js do
|
|||
visit edit_project_path(project)
|
||||
|
||||
page.within('.js-project-transfer-form') do
|
||||
page.find('.select2-container').click
|
||||
page.find('[data-testid="transfer-project-namespace"]').click
|
||||
end
|
||||
|
||||
page.find("div[role='option']", text: group.full_name).click
|
||||
page.within('[data-testid="transfer-project-namespace"]') do
|
||||
page.find("li button", text: group.full_name).click
|
||||
end
|
||||
|
||||
click_button('Transfer project')
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import { namespaces } from 'jest/vue_shared/components/namespace_select/mock_data';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import TransferProjectForm from '~/projects/settings/components/transfer_project_form.vue';
|
||||
import NamespaceSelect from '~/vue_shared/components/namespace_select/namespace_select.vue';
|
||||
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
|
||||
|
||||
describe('Transfer project form', () => {
|
||||
let wrapper;
|
||||
|
||||
const confirmButtonText = 'Confirm';
|
||||
const confirmationPhrase = 'You must construct additional pylons!';
|
||||
|
||||
const createComponent = () =>
|
||||
shallowMountExtended(TransferProjectForm, {
|
||||
propsData: {
|
||||
namespaces,
|
||||
confirmButtonText,
|
||||
confirmationPhrase,
|
||||
},
|
||||
});
|
||||
|
||||
const findNamespaceSelect = () => wrapper.findComponent(NamespaceSelect);
|
||||
const findConfirmDanger = () => wrapper.findComponent(ConfirmDanger);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders the namespace selector', () => {
|
||||
expect(findNamespaceSelect().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the confirm button', () => {
|
||||
expect(findConfirmDanger().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('disables the confirm button by default', () => {
|
||||
expect(findConfirmDanger().attributes('disabled')).toBe('true');
|
||||
});
|
||||
|
||||
describe('with a selected namespace', () => {
|
||||
const [selectedItem] = namespaces.group;
|
||||
|
||||
beforeEach(() => {
|
||||
findNamespaceSelect().vm.$emit('select', selectedItem);
|
||||
});
|
||||
|
||||
it('emits the `selectNamespace` event when a namespace is selected', () => {
|
||||
const args = [selectedItem.id];
|
||||
|
||||
expect(wrapper.emitted('selectNamespace')).toEqual([args]);
|
||||
});
|
||||
|
||||
it('enables the confirm button', () => {
|
||||
expect(findConfirmDanger().attributes('disabled')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('clicking the confirm button emits the `confirm` event', () => {
|
||||
findConfirmDanger().vm.$emit('confirm');
|
||||
|
||||
expect(wrapper.emitted('confirm')).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -4,11 +4,11 @@ import { loadHTMLFixture } from 'helpers/fixtures';
|
|||
import setupTransferEdit from '~/transfer_edit';
|
||||
|
||||
describe('setupTransferEdit', () => {
|
||||
const formSelector = '.js-project-transfer-form';
|
||||
const targetSelector = 'select.select2';
|
||||
const formSelector = '.js-group-transfer-form';
|
||||
const targetSelector = '#new_parent_group_id';
|
||||
|
||||
beforeEach(() => {
|
||||
loadHTMLFixture('projects/edit.html');
|
||||
loadHTMLFixture('groups/edit.html');
|
||||
setupTransferEdit(formSelector, targetSelector);
|
||||
});
|
||||
|
||||
|
@ -17,8 +17,8 @@ describe('setupTransferEdit', () => {
|
|||
});
|
||||
|
||||
it('enables submit button when selection changes to non-empty value', () => {
|
||||
const nonEmptyValue = $(formSelector).find(targetSelector).find('option').not(':empty').val();
|
||||
$(formSelector).find(targetSelector).val(nonEmptyValue).trigger('change');
|
||||
const lastValue = $(formSelector).find(targetSelector).find('.dropdown-content li').last();
|
||||
$(formSelector).find(targetSelector).val(lastValue).trigger('change');
|
||||
|
||||
expect($(formSelector).find(':submit').prop('disabled')).toBeFalsy();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
export const group = [
|
||||
{ id: 1, name: 'Group 1', humanName: 'Group 1' },
|
||||
{ id: 2, name: 'Subgroup 1', humanName: 'Group 1 / Subgroup 1' },
|
||||
];
|
||||
|
||||
export const user = [{ id: 3, name: 'User namespace 1', humanName: 'User namespace 1' }];
|
||||
|
||||
export const namespaces = {
|
||||
group,
|
||||
user,
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import NamespaceSelect, {
|
||||
i18n,
|
||||
} from '~/vue_shared/components/namespace_select/namespace_select.vue';
|
||||
import { user, group, namespaces } from './mock_data';
|
||||
|
||||
describe('Namespace Select', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (props = {}) =>
|
||||
shallowMountExtended(NamespaceSelect, {
|
||||
propsData: {
|
||||
data: namespaces,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
|
||||
const wrappersText = (arr) => arr.wrappers.map((w) => w.text());
|
||||
const flatNamespaces = () => [...group, ...user];
|
||||
const findDropdown = () => wrapper.findComponent(GlDropdown);
|
||||
const findDropdownAttributes = (attr) => findDropdown().attributes(attr);
|
||||
const selectedDropdownItemText = () => findDropdownAttributes('text');
|
||||
const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
|
||||
const findSectionHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader);
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders the dropdown', () => {
|
||||
expect(findDropdown().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('renders each dropdown item', () => {
|
||||
const items = findDropdownItems().wrappers;
|
||||
expect(items).toHaveLength(flatNamespaces().length);
|
||||
});
|
||||
|
||||
it('renders the human name for each item', () => {
|
||||
const dropdownItems = wrappersText(findDropdownItems());
|
||||
const flatNames = flatNamespaces().map(({ humanName }) => humanName);
|
||||
expect(dropdownItems).toEqual(flatNames);
|
||||
});
|
||||
|
||||
it('sets the initial dropdown text', () => {
|
||||
expect(selectedDropdownItemText()).toBe(i18n.DEFAULT_TEXT);
|
||||
});
|
||||
|
||||
it('splits group and user namespaces', () => {
|
||||
const headers = findSectionHeaders();
|
||||
expect(headers).toHaveLength(2);
|
||||
expect(wrappersText(headers)).toEqual([i18n.GROUPS, i18n.USERS]);
|
||||
});
|
||||
|
||||
it('sets the dropdown to full width', () => {
|
||||
expect(findDropdownAttributes('block')).toBeUndefined();
|
||||
|
||||
wrapper = createComponent({ fullWidth: true });
|
||||
|
||||
expect(findDropdownAttributes('block')).not.toBeUndefined();
|
||||
expect(findDropdownAttributes('block')).toBe('true');
|
||||
});
|
||||
|
||||
describe('with a selected namespace', () => {
|
||||
const selectedGroupIndex = 1;
|
||||
const selectedItem = group[selectedGroupIndex];
|
||||
|
||||
beforeEach(() => {
|
||||
findDropdownItems().at(selectedGroupIndex).vm.$emit('click');
|
||||
});
|
||||
|
||||
it('sets the dropdown text', () => {
|
||||
expect(selectedDropdownItemText()).toBe(selectedItem.humanName);
|
||||
});
|
||||
|
||||
it('emits the `select` event when a namespace is selected', () => {
|
||||
const args = [selectedItem];
|
||||
expect(wrapper.emitted('select')).toEqual([args]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -45,6 +45,39 @@ RSpec.describe NamespacesHelper do
|
|||
user_group.add_owner(user)
|
||||
end
|
||||
|
||||
describe '#namespaces_as_json' do
|
||||
let(:result) { helper.namespaces_as_json(user) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user).and_return(user)
|
||||
end
|
||||
|
||||
it 'returns the user\'s groups' do
|
||||
json_data = Gitlab::Json.parse(result)
|
||||
|
||||
expect(result).to include('group')
|
||||
expect(json_data['group']).to include(
|
||||
"id" => user_group.id,
|
||||
"name" => user_group.name,
|
||||
"display_path" => user_group.full_path,
|
||||
"human_name" => user_group.human_name
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns the user\'s namespace' do
|
||||
user_namespace = user.namespace
|
||||
json_data = Gitlab::Json.parse(result)
|
||||
|
||||
expect(result).to include('user')
|
||||
expect(json_data['user']).to include(
|
||||
"id" => user_namespace.id,
|
||||
"name" => user_namespace.name,
|
||||
"display_path" => user_namespace.full_path,
|
||||
"human_name" => user_namespace.human_name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#namespaces_options' do
|
||||
context 'when admin mode is enabled', :enable_admin_mode do
|
||||
it 'returns groups without being a member for admin' do
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require "fast_spec_helper"
|
||||
require "support/graphql/fake_query_type"
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
|
||||
let(:dummy_schema) do
|
||||
|
@ -49,4 +48,15 @@ RSpec.describe Gitlab::Graphql::Tracers::LoggerTracer do
|
|||
|
||||
dummy_schema.execute(query_string, variables: variables)
|
||||
end
|
||||
|
||||
it 'logs exceptions for breaking queries' do
|
||||
query_string = "query fooOperation { breakingField }"
|
||||
|
||||
expect(::Gitlab::GraphqlLogger).to receive(:info).with(a_hash_including({
|
||||
'exception.message' => 'This field is supposed to break',
|
||||
'exception.class' => 'RuntimeError'
|
||||
}))
|
||||
|
||||
expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'fast_spec_helper'
|
||||
require 'spec_helper'
|
||||
require 'rspec-parameterized'
|
||||
require "support/graphql/fake_query_type"
|
||||
|
||||
|
@ -36,7 +36,7 @@ RSpec.describe Gitlab::Graphql::Tracers::MetricsTracer do
|
|||
end
|
||||
|
||||
with_them do
|
||||
it 'increments sli' do
|
||||
it 'increments apdex sli' do
|
||||
# Trigger initialization
|
||||
fake_schema
|
||||
|
||||
|
@ -56,5 +56,13 @@ RSpec.describe Gitlab::Graphql::Tracers::MetricsTracer do
|
|||
fake_schema.execute("query lorem { helloWorld }")
|
||||
end
|
||||
end
|
||||
|
||||
it "does not record apdex for failing queries" do
|
||||
query_string = "query fooOperation { breakingField }"
|
||||
|
||||
expect(Gitlab::Metrics::RailsSlis.graphql_query_apdex).not_to receive(:increment)
|
||||
|
||||
expect { fake_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
|
|||
|
||||
before do
|
||||
current_time = 0
|
||||
allow(tracer_spy).to receive(:trace)
|
||||
allow(Gitlab::Metrics::System).to receive(:monotonic_time) do
|
||||
current_time += expected_duration
|
||||
end
|
||||
|
@ -30,6 +31,18 @@ RSpec.describe Gitlab::Graphql::Tracers::TimerTracer do
|
|||
|
||||
dummy_schema.execute(query_string)
|
||||
|
||||
expect_to_have_traced(tracer_spy, expected_duration, query_string)
|
||||
end
|
||||
|
||||
it "adds a duration_s even if the query failed" do
|
||||
query_string = "query fooOperation { breakingField }"
|
||||
|
||||
expect { dummy_schema.execute(query_string) }.to raise_error(/This field is supposed to break/)
|
||||
|
||||
expect_to_have_traced(tracer_spy, expected_duration, query_string)
|
||||
end
|
||||
|
||||
def expect_to_have_traced(tracer_spy, expected_duration, query_string)
|
||||
# "parse" and "execute_query" are just arbitrary trace events
|
||||
expect(tracer_spy).to have_received(:trace).with("parse", {
|
||||
duration_s: expected_duration,
|
||||
|
|
|
@ -25,6 +25,28 @@ RSpec.describe LfsObjectsProject do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#link_to_project!' do
|
||||
it 'does not throw error when duplicate exists' do
|
||||
subject
|
||||
|
||||
expect do
|
||||
result = described_class.link_to_project!(subject.lfs_object, subject.project)
|
||||
expect(result).to be_a(LfsObjectsProject)
|
||||
end.not_to change { described_class.count }
|
||||
end
|
||||
|
||||
it 'upserts a new entry and updates the project cache' do
|
||||
new_project = create(:project)
|
||||
|
||||
allow(ProjectCacheWorker).to receive(:perform_async).and_call_original
|
||||
expect(ProjectCacheWorker).to receive(:perform_async).with(new_project.id, [], [:lfs_objects_size])
|
||||
expect { described_class.link_to_project!(subject.lfs_object, new_project) }
|
||||
.to change { described_class.count }
|
||||
|
||||
expect(described_class.find_by(lfs_object_id: subject.lfs_object.id, project_id: new_project.id)).to be_present
|
||||
end
|
||||
end
|
||||
|
||||
describe '#update_project_statistics' do
|
||||
it 'updates project statistics when the object is added' do
|
||||
expect(ProjectCacheWorker).to receive(:perform_async)
|
||||
|
|
|
@ -3581,6 +3581,29 @@ RSpec.describe Project, factory_default: :keep do
|
|||
expect(project.forks).to contain_exactly(forked_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#lfs_object_oids_from_fork_source' do
|
||||
let_it_be(:lfs_object) { create(:lfs_object) }
|
||||
let_it_be(:another_lfs_object) { create(:lfs_object) }
|
||||
|
||||
let(:oids) { [lfs_object.oid, another_lfs_object.oid] }
|
||||
|
||||
context 'when fork has one of two LFS objects' do
|
||||
before do
|
||||
create(:lfs_objects_project, lfs_object: lfs_object, project: project)
|
||||
create(:lfs_objects_project, lfs_object: another_lfs_object, project: forked_project)
|
||||
end
|
||||
|
||||
it 'returns OIDs of owned LFS objects', :aggregate_failures do
|
||||
expect(forked_project.lfs_objects_oids_from_fork_source(oids: oids)).to eq([lfs_object.oid])
|
||||
expect(forked_project.lfs_objects_oids(oids: oids)).to eq([another_lfs_object.oid])
|
||||
end
|
||||
|
||||
it 'returns empty when project is not a fork' do
|
||||
expect(project.lfs_objects_oids_from_fork_source(oids: oids)).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'can housekeep repository' do
|
||||
|
|
|
@ -518,16 +518,46 @@ RSpec.describe 'Git LFS API and storage' do
|
|||
end
|
||||
|
||||
context 'in source of fork project' do
|
||||
let(:other_project) { create(:project, :empty_repo) }
|
||||
let(:project) { fork_project(other_project) }
|
||||
|
||||
before do
|
||||
lfs_object.update!(projects: [other_project])
|
||||
end
|
||||
|
||||
context 'when user has access to both the parent and fork' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
other_project.add_developer(user)
|
||||
end
|
||||
|
||||
it 'links existing LFS objects to other project' do
|
||||
expect(json_response['objects']).to be_kind_of(Array)
|
||||
expect(json_response['objects'].first).to include(sample_object)
|
||||
expect(json_response['objects'].first).not_to have_key('actions')
|
||||
|
||||
expect(lfs_object.reload.projects.pluck(:id)).to match_array([other_project.id, project.id])
|
||||
end
|
||||
|
||||
context 'when feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(lfs_auto_link_fork_source: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'batch upload with existing LFS object'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user does not have access to parent' do
|
||||
before do
|
||||
project.add_developer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'batch upload with existing LFS object'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pushing a LFS object that does not exist' do
|
||||
it_behaves_like 'pushes new LFS objects', renew_authorization: true
|
||||
end
|
||||
|
|
|
@ -63,6 +63,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
|
|||
let_it_be(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: false) }
|
||||
|
||||
it 'updates pending builds for the group' do
|
||||
expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original
|
||||
|
||||
subject
|
||||
|
||||
expect(pending_build_1.reload.instance_runners_enabled).to be_truthy
|
||||
|
@ -73,6 +75,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
|
|||
let(:params) { { shared_runners_setting: 'invalid_enabled' } }
|
||||
|
||||
it 'does not update pending builds for the group' do
|
||||
expect(::Ci::UpdatePendingBuildService).not_to receive(:new).and_call_original
|
||||
|
||||
subject
|
||||
|
||||
expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
|
||||
|
@ -99,6 +103,8 @@ RSpec.describe Groups::UpdateSharedRunnersService do
|
|||
let_it_be(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: true) }
|
||||
|
||||
it 'updates pending builds for the group' do
|
||||
expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original
|
||||
|
||||
subject
|
||||
|
||||
expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
require 'graphql'
|
||||
|
||||
module Graphql
|
||||
class FakeQueryType < Types::BaseObject
|
||||
class FakeQueryType < ::GraphQL::Schema::Object
|
||||
graphql_name 'FakeQuery'
|
||||
|
||||
field :hello_world, String, null: true do
|
||||
argument :message, String, required: false
|
||||
end
|
||||
|
||||
field :breaking_field, String, null: true
|
||||
|
||||
def hello_world(message: "world")
|
||||
"Hello #{message}!"
|
||||
end
|
||||
|
||||
def breaking_field
|
||||
raise "This field is supposed to break"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|