Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6c4f790271
commit
aa72e920ce
|
@ -154,19 +154,6 @@ Layout/ArgumentAlignment:
|
||||||
- 'ee/lib/gitlab/geo/git_ssh_proxy.rb'
|
- 'ee/lib/gitlab/geo/git_ssh_proxy.rb'
|
||||||
- 'ee/lib/gitlab/geo/replicator.rb'
|
- 'ee/lib/gitlab/geo/replicator.rb'
|
||||||
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
|
- 'ee/lib/gitlab/graphql/aggregations/epics/epic_node.rb'
|
||||||
- 'ee/lib/gitlab/insights/executors/dora_executor.rb'
|
|
||||||
- 'ee/lib/gitlab/insights/executors/issuable_executor.rb'
|
|
||||||
- 'ee/lib/gitlab/status_page/pipeline/post_process_pipeline.rb'
|
|
||||||
- 'ee/lib/gitlab/subscription_portal/clients/graphql.rb'
|
|
||||||
- 'ee/lib/gitlab/zoekt/search_results.rb'
|
|
||||||
- 'ee/spec/components/billing/plan_component_spec.rb'
|
|
||||||
- 'ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
|
|
||||||
- 'ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
|
|
||||||
- 'ee/spec/lib/analytics/group_activity_calculator_spec.rb'
|
|
||||||
- 'ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb'
|
|
||||||
- 'ee/spec/lib/api/entities/protected_environments/approval_rule_for_summary_spec.rb'
|
|
||||||
- 'ee/spec/lib/api/entities/protected_environments/approval_rule_spec.rb'
|
|
||||||
- 'ee/spec/lib/api/entities/protected_environments/deploy_access_level_spec.rb'
|
|
||||||
- 'ee/spec/lib/ee/gitlab/scim/params_parser_spec.rb'
|
- 'ee/spec/lib/ee/gitlab/scim/params_parser_spec.rb'
|
||||||
- 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
|
- 'ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
|
||||||
- 'ee/spec/lib/ee/gitlab/usage/service_ping_report_spec.rb'
|
- 'ee/spec/lib/ee/gitlab/usage/service_ping_report_spec.rb'
|
||||||
|
|
|
@ -8,17 +8,14 @@ import {
|
||||||
EDITOR_APP_DRAWER_JOB_ASSISTANT,
|
EDITOR_APP_DRAWER_JOB_ASSISTANT,
|
||||||
EDITOR_APP_DRAWER_NONE,
|
EDITOR_APP_DRAWER_NONE,
|
||||||
pipelineEditorTrackingOptions,
|
pipelineEditorTrackingOptions,
|
||||||
TEMPLATE_REPOSITORY_URL,
|
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
i18n: {
|
i18n: {
|
||||||
browseCatalog: __('Browse CI/CD Catalog'),
|
browseCatalog: __('Browse CI/CD Catalog'),
|
||||||
browseTemplates: __('Browse templates'),
|
|
||||||
help: __('Help'),
|
help: __('Help'),
|
||||||
jobAssistant: s__('JobAssistant|Job assistant'),
|
jobAssistant: s__('JobAssistant|Job assistant'),
|
||||||
},
|
},
|
||||||
TEMPLATE_REPOSITORY_URL,
|
|
||||||
components: {
|
components: {
|
||||||
GlButton,
|
GlButton,
|
||||||
},
|
},
|
||||||
|
@ -58,11 +55,6 @@ export default {
|
||||||
const { label, actions } = pipelineEditorTrackingOptions;
|
const { label, actions } = pipelineEditorTrackingOptions;
|
||||||
this.track(actions.openHelpDrawer, { label });
|
this.track(actions.openHelpDrawer, { label });
|
||||||
},
|
},
|
||||||
trackTemplateBrowsing() {
|
|
||||||
const { label, actions } = pipelineEditorTrackingOptions;
|
|
||||||
|
|
||||||
this.track(actions.browseTemplates, { label });
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -82,16 +74,6 @@ export default {
|
||||||
>
|
>
|
||||||
{{ $options.i18n.browseCatalog }}
|
{{ $options.i18n.browseCatalog }}
|
||||||
</gl-button>
|
</gl-button>
|
||||||
<gl-button
|
|
||||||
:href="$options.TEMPLATE_REPOSITORY_URL"
|
|
||||||
size="small"
|
|
||||||
icon="external-link"
|
|
||||||
target="_blank"
|
|
||||||
data-testid="template-repo-link"
|
|
||||||
@click="trackTemplateBrowsing"
|
|
||||||
>
|
|
||||||
{{ $options.i18n.browseTemplates }}
|
|
||||||
</gl-button>
|
|
||||||
<gl-button
|
<gl-button
|
||||||
icon="information-o"
|
icon="information-o"
|
||||||
size="small"
|
size="small"
|
||||||
|
|
|
@ -88,8 +88,6 @@ export const pipelineEditorTrackingOptions = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TEMPLATE_REPOSITORY_URL =
|
|
||||||
'https://gitlab.com/gitlab-org/gitlab-foss/tree/master/lib/gitlab/ci/templates';
|
|
||||||
export const VALIDATE_TAB_FEEDBACK_URL = 'https://gitlab.com/gitlab-org/gitlab/-/issues/346687';
|
export const VALIDATE_TAB_FEEDBACK_URL = 'https://gitlab.com/gitlab-org/gitlab/-/issues/346687';
|
||||||
|
|
||||||
export const COMMIT_SHA_POLL_INTERVAL = 1000;
|
export const COMMIT_SHA_POLL_INTERVAL = 1000;
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import { GlSprintf } from '@gitlab/ui';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'OrganizationGroupsEditApp',
|
||||||
|
components: { GlSprintf },
|
||||||
|
i18n: {
|
||||||
|
pageTitle: __('Edit group: %{group_name}'),
|
||||||
|
},
|
||||||
|
inject: ['group'],
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="gl-py-6">
|
||||||
|
<h1 class="gl-mt-0 gl-font-size-h-display">
|
||||||
|
<gl-sprintf :message="$options.i18n.pageTitle">
|
||||||
|
<template #group_name>{{ group.fullName }}</template>
|
||||||
|
</gl-sprintf>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||||
|
import App from './components/app.vue';
|
||||||
|
|
||||||
|
export const initOrganizationsGroupsEdit = () => {
|
||||||
|
const el = document.getElementById('js-organizations-groups-edit');
|
||||||
|
|
||||||
|
if (!el) return false;
|
||||||
|
|
||||||
|
const {
|
||||||
|
dataset: { appData },
|
||||||
|
} = el;
|
||||||
|
const { group } = convertObjectPropsToCamelCase(JSON.parse(appData), { deep: true });
|
||||||
|
|
||||||
|
return new Vue({
|
||||||
|
el,
|
||||||
|
name: 'OrganizationGroupsEditRoot',
|
||||||
|
provide: {
|
||||||
|
group,
|
||||||
|
},
|
||||||
|
render(createElement) {
|
||||||
|
return createElement(App);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { initOrganizationsGroupsEdit } from '~/organizations/groups/edit';
|
||||||
|
|
||||||
|
initOrganizationsGroupsEdit();
|
|
@ -81,12 +81,20 @@ export default {
|
||||||
primaryModalProps() {
|
primaryModalProps() {
|
||||||
return {
|
return {
|
||||||
text: this.$options.i18n.modalRemove,
|
text: this.$options.i18n.modalRemove,
|
||||||
attributes: { disabled: this.disableModalSubmit, variant: 'danger' },
|
attributes: {
|
||||||
|
disabled: this.disableModalSubmit,
|
||||||
|
variant: 'danger',
|
||||||
|
type: 'submit',
|
||||||
|
form: this.$options.removeFormId,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
commandModalId() {
|
commandModalId() {
|
||||||
return `init-command-modal-${this.state.name}`;
|
return `init-command-modal-${this.state.name}`;
|
||||||
},
|
},
|
||||||
|
modalInputId() {
|
||||||
|
return `terraform-state-remove-input-${this.state.name}`;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
hideModal() {
|
hideModal() {
|
||||||
|
@ -188,6 +196,7 @@ export default {
|
||||||
this.showCommandModal = true;
|
this.showCommandModal = true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
removeFormId: 'remove-state-form',
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -255,15 +264,16 @@ export default {
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<form :id="$options.removeFormId" @submit.prevent="remove">
|
||||||
<p>
|
<p>
|
||||||
<gl-sprintf :message="$options.i18n.modalBody">
|
<gl-sprintf :message="$options.i18n.modalBody">
|
||||||
<template #name>
|
<template #name>
|
||||||
<span>{{ state.name }}</span>
|
<code>{{ state.name }}</code>
|
||||||
</template>
|
</template>
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<gl-form-group>
|
<gl-form-group :label-for="modalInputId">
|
||||||
<template #label>
|
<template #label>
|
||||||
<gl-sprintf :message="$options.i18n.modalInputLabel">
|
<gl-sprintf :message="$options.i18n.modalInputLabel">
|
||||||
<template #name>
|
<template #name>
|
||||||
|
@ -271,14 +281,9 @@ export default {
|
||||||
</template>
|
</template>
|
||||||
</gl-sprintf>
|
</gl-sprintf>
|
||||||
</template>
|
</template>
|
||||||
<gl-form-input
|
<gl-form-input :id="modalInputId" ref="input" v-model="removeConfirmText" type="text" />
|
||||||
:id="`terraform-state-remove-input-${state.name}`"
|
|
||||||
ref="input"
|
|
||||||
v-model="removeConfirmText"
|
|
||||||
type="text"
|
|
||||||
@keyup.enter="remove"
|
|
||||||
/>
|
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
|
</form>
|
||||||
</gl-modal>
|
</gl-modal>
|
||||||
|
|
||||||
<init-command-modal
|
<init-command-modal
|
||||||
|
|
|
@ -34,8 +34,8 @@
|
||||||
width: $chip-size;
|
width: $chip-size;
|
||||||
height: $chip-size;
|
height: $chip-size;
|
||||||
background: $white;
|
background: $white;
|
||||||
background-image: linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%),
|
background-image: linear-gradient(135deg, $gray-100 25%, transparent 0%, transparent 75%, $gray-100 0%),
|
||||||
linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%);
|
linear-gradient(135deg, $gray-100 25%, transparent 0%, transparent 75%, $gray-100 0%);
|
||||||
background-size: $bg-size $bg-size;
|
background-size: $bg-size $bg-size;
|
||||||
background-position: 0 0, $bg-pos $bg-pos;
|
background-position: 0 0, $bg-pos $bg-pos;
|
||||||
|
|
||||||
|
|
|
@ -83,37 +83,17 @@ $size-scale: (
|
||||||
);
|
);
|
||||||
|
|
||||||
// Color schema
|
// Color schema
|
||||||
$darken-normal-factor: 7% !default;
|
|
||||||
$darken-dark-factor: 10% !default;
|
|
||||||
|
|
||||||
$purple: #6d49cb !default;
|
$purple: #6d49cb !default;
|
||||||
$purple-light: #ede8fb !default;
|
$purple-light: #ede8fb !default;
|
||||||
|
|
||||||
$gray-light: $gray-10 !default;
|
$gray-light: $gray-10 !default;
|
||||||
$gray-lighter: lighten($gray-50, 4) !default;
|
$gray-lighter: lighten($gray-50, 4) !default;
|
||||||
$gray-normal: lighten($gray-50, 2) !default;
|
$gray-normal: lighten($gray-50, 2) !default;
|
||||||
$gray-dark: darken($gray-light, $darken-dark-factor) !default;
|
|
||||||
|
|
||||||
$t-white-a-02: rgba(255, 255, 255, 0.02) !default;
|
|
||||||
$t-white-a-04: rgba(255, 255, 255, 0.04) !default;
|
|
||||||
$t-white-a-06: rgba(255, 255, 255, 0.06) !default;
|
|
||||||
$t-white-a-08: rgba(255, 255, 255, 0.08) !default;
|
|
||||||
$t-white-a-16: rgba(255, 255, 255, 0.16) !default;
|
|
||||||
$t-white-a-24: rgba(255, 255, 255, 0.24) !default;
|
|
||||||
$t-white-a-36: rgba(255, 255, 255, 0.36) !default;
|
|
||||||
|
|
||||||
$t-gray-a-02: rgba(31, 30, 36, 0.02) !default;
|
|
||||||
$t-gray-a-04: rgba(31, 30, 36, 0.04) !default;
|
|
||||||
$t-gray-a-06: rgba(31, 30, 36, 0.06) !default;
|
|
||||||
$t-gray-a-08: rgba(31, 30, 36, 0.08) !default;
|
|
||||||
$t-gray-a-16: rgba(31, 30, 36, 0.16) !default;
|
|
||||||
$t-gray-a-24: rgba(31, 30, 36, 0.24) !default;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* UI elements
|
* UI elements
|
||||||
*/
|
*/
|
||||||
$border-color: $gray-100;
|
$border-color: $gray-100;
|
||||||
$shadow-color: $t-gray-a-08;
|
|
||||||
$well-expand-item: #e8f2f7 !default;
|
$well-expand-item: #e8f2f7 !default;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -27,8 +27,8 @@ $dropdown-item-padding-x: 12px;
|
||||||
$popover-max-width: 300px;
|
$popover-max-width: 300px;
|
||||||
$popover-border-width: 1px;
|
$popover-border-width: 1px;
|
||||||
$popover-border-color: $border-color;
|
$popover-border-color: $border-color;
|
||||||
$popover-box-shadow: 0 $border-radius-small $gl-border-radius-base 0 $shadow-color;
|
$popover-box-shadow: 0 $border-radius-small $gl-border-radius-base 0 $t-gray-a-08;
|
||||||
$popover-arrow-outer-color: $shadow-color;
|
$popover-arrow-outer-color: $t-gray-a-08;
|
||||||
$h1-font-size: 14px * 2.5;
|
$h1-font-size: 14px * 2.5;
|
||||||
$h2-font-size: 14px * 2;
|
$h2-font-size: 14px * 2;
|
||||||
$h3-font-size: 14px * 1.75;
|
$h3-font-size: 14px * 1.75;
|
||||||
|
|
|
@ -3,11 +3,6 @@
|
||||||
$gray-light: lighten($gray-10, 2);
|
$gray-light: lighten($gray-10, 2);
|
||||||
$gray-lighter: darken($gray-50, 4);
|
$gray-lighter: darken($gray-50, 4);
|
||||||
$gray-normal: $gray-50;
|
$gray-normal: $gray-50;
|
||||||
$gray-dark: darken($gray-100, 2);
|
|
||||||
|
|
||||||
// Used for border and background in a couple instances where inverting between modes is desirable
|
|
||||||
// once migrated to suitable color values this can be removed
|
|
||||||
$t-gray-a-08: rgba($gray-950, 0.08);
|
|
||||||
|
|
||||||
$black-normal: $gray-900;
|
$black-normal: $gray-900;
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,13 @@ module Organizations
|
||||||
urgency :low, [:create, :new]
|
urgency :low, [:create, :new]
|
||||||
|
|
||||||
before_action :authorize_create_group!, only: [:new]
|
before_action :authorize_create_group!, only: [:new]
|
||||||
|
before_action :authorize_read_organization!, only: [:edit]
|
||||||
|
before_action :authorize_view_edit_page!, only: [:edit]
|
||||||
|
|
||||||
def new; end
|
def new; end
|
||||||
|
|
||||||
|
def edit; end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
response = create_group
|
response = create_group
|
||||||
@group = response[:group]
|
@group = response[:group]
|
||||||
|
@ -24,10 +28,20 @@ module Organizations
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def group
|
||||||
|
@group ||= Group.in_organization(organization).find_by_full_path(params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
def create_group
|
def create_group
|
||||||
create_service_params = group_params.merge(organization_id: organization.id)
|
create_service_params = group_params.merge(organization_id: organization.id)
|
||||||
Groups::CreateService.new(current_user, create_service_params).execute
|
Groups::CreateService.new(current_user, create_service_params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def authorize_view_edit_page!
|
||||||
|
return render_404 if group.nil?
|
||||||
|
|
||||||
|
access_denied! unless can?(current_user, :view_edit_page, group)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,12 @@ module Organizations
|
||||||
}.to_json
|
}.to_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def organization_groups_edit_app_data(group)
|
||||||
|
{
|
||||||
|
group: group.slice(:full_name)
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
def admin_organizations_index_app_data
|
def admin_organizations_index_app_data
|
||||||
shared_organization_index_app_data.to_json
|
shared_organization_index_app_data.to_json
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ class DraftNote < ApplicationRecord
|
||||||
include Gitlab::Utils::StrongMemoize
|
include Gitlab::Utils::StrongMemoize
|
||||||
include Sortable
|
include Sortable
|
||||||
include ShaAttribute
|
include ShaAttribute
|
||||||
|
include BulkInsertSafe
|
||||||
|
|
||||||
PUBLISH_ATTRS = %i[noteable type note internal].freeze
|
PUBLISH_ATTRS = %i[noteable type note internal].freeze
|
||||||
DIFF_ATTRS = %i[position original_position change_position commit_id].freeze
|
DIFF_ATTRS = %i[position original_position change_position commit_id].freeze
|
||||||
|
|
|
@ -21,6 +21,7 @@ class RemoteMirror < ApplicationRecord
|
||||||
belongs_to :project, inverse_of: :remote_mirrors
|
belongs_to :project, inverse_of: :remote_mirrors
|
||||||
|
|
||||||
validates :url, presence: true, public_url: { schemes: Project::VALID_MIRROR_PROTOCOLS, allow_blank: true, enforce_user: true }
|
validates :url, presence: true, public_url: { schemes: Project::VALID_MIRROR_PROTOCOLS, allow_blank: true, enforce_user: true }
|
||||||
|
validates :only_protected_branches, inclusion: { in: [true, false], message: :blank }
|
||||||
|
|
||||||
before_validation :store_credentials
|
before_validation :store_credentials
|
||||||
after_update :reset_fields, if: :saved_change_to_mirror_url?
|
after_update :reset_fields, if: :saved_change_to_mirror_url?
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
- page_title _("Edit"), @group.name, _("Edit")
|
||||||
|
- add_to_breadcrumbs _('Groups and projects'), groups_and_projects_organization_path(@organization, { display: 'groups' })
|
||||||
|
- add_to_breadcrumbs @group.name, group_path(@group)
|
||||||
|
|
||||||
|
#js-organizations-groups-edit{ data: { app_data: organization_groups_edit_app_data(@group) } }
|
|
@ -16,6 +16,18 @@ resources(:organizations, only: [:show, :index, :new, :create], param: :organiza
|
||||||
|
|
||||||
resource :groups, only: [:new, :create], as: :groups_organization
|
resource :groups, only: [:new, :create], as: :groups_organization
|
||||||
|
|
||||||
|
scope(
|
||||||
|
path: 'groups/*id',
|
||||||
|
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||||
|
) do
|
||||||
|
resource(
|
||||||
|
:groups,
|
||||||
|
path: '/',
|
||||||
|
only: [:edit],
|
||||||
|
as: :groups_organization
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
scope(
|
scope(
|
||||||
path: 'projects/*namespace_id',
|
path: 'projects/*namespace_id',
|
||||||
as: :namespace,
|
as: :namespace,
|
||||||
|
|
|
@ -23,3 +23,4 @@ tokens:
|
||||||
- "GitLab (v)?11."
|
- "GitLab (v)?11."
|
||||||
- "GitLab (v)?12."
|
- "GitLab (v)?12."
|
||||||
- "GitLab (v)?13."
|
- "GitLab (v)?13."
|
||||||
|
- "GitLab (v)?14."
|
||||||
|
|
|
@ -15,7 +15,7 @@ swap:
|
||||||
active users: "billable users"
|
active users: "billable users"
|
||||||
air(?:-| )?gapped: "offline environment"
|
air(?:-| )?gapped: "offline environment"
|
||||||
bullet: "list item"
|
bullet: "list item"
|
||||||
click: "select"
|
(?<!right-)click(?!-through): "select"
|
||||||
code base: "codebase"
|
code base: "codebase"
|
||||||
config: "configuration"
|
config: "configuration"
|
||||||
confirmation box: "confirmation dialog"
|
confirmation box: "confirmation dialog"
|
||||||
|
|
|
@ -133,7 +133,6 @@ Choose the one or three step method according to your registry installation.
|
||||||
|
|
||||||
#### One-step migration
|
#### One-step migration
|
||||||
|
|
||||||
WARNING:
|
|
||||||
WARNING:
|
WARNING:
|
||||||
The registry must be shut down or remain in `read-only` mode during the migration.
|
The registry must be shut down or remain in `read-only` mode during the migration.
|
||||||
Only choose this method if you do not need to write to the registry during the migration
|
Only choose this method if you do not need to write to the registry during the migration
|
||||||
|
|
|
@ -160,7 +160,7 @@ the feature must request to the [AI Gateway](../../architecture/blueprints/ai_ga
|
||||||
1. Verify AI feature by calling the following in the rails console:
|
1. Verify AI feature by calling the following in the rails console:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Gitlab::Llm::AiGateway::Client.new(User.first).stream(prompt: "\n\nHuman: Hi, how are you?\n\nAssistant:")
|
Gitlab::Llm::AiGateway::Client.new(User.first).stream(prompt: [{role: "user", content: "Hi, how are you?"}])
|
||||||
```
|
```
|
||||||
|
|
||||||
### Verify the setup with GraphQL
|
### Verify the setup with GraphQL
|
||||||
|
@ -218,7 +218,7 @@ it will print useful error messages with links to the docs on how to resolve the
|
||||||
GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[<test-group-name>]'
|
GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[<test-group-name>]'
|
||||||
```
|
```
|
||||||
|
|
||||||
[AI Gateway](#set-up) still needs to be setup when using the automated setup.
|
[AI Gateway](#test-ai-features-with-ai-gateway-locally) still needs to be setup when using the automated setup.
|
||||||
|
|
||||||
**Manual way**
|
**Manual way**
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[
|
||||||
1. Enable **Experiment & Beta features**.
|
1. Enable **Experiment & Beta features**.
|
||||||
1. Enable the specific feature flag for the feature you want to test.
|
1. Enable the specific feature flag for the feature you want to test.
|
||||||
1. You can use Rake task `rake gitlab:duo:enable_feature_flags` to enable all feature flags that are assigned to group AI Framework.
|
1. You can use Rake task `rake gitlab:duo:enable_feature_flags` to enable all feature flags that are assigned to group AI Framework.
|
||||||
1. Setup [AI Gateway](#set-up).
|
1. Setup [AI Gateway](#test-ai-features-with-ai-gateway-locally).
|
||||||
|
|
||||||
### Help
|
### Help
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ For each scanner, an analyzer:
|
||||||
- Handles its execution.
|
- Handles its execution.
|
||||||
- Converts its output to a [standard format](../terminology/index.md#secure-report-format).
|
- Converts its output to a [standard format](../terminology/index.md#secure-report-format).
|
||||||
|
|
||||||
## SAST analyzers
|
## Official analyzers
|
||||||
|
|
||||||
SAST supports the following official analyzers:
|
SAST supports the following official analyzers:
|
||||||
|
|
||||||
|
@ -36,8 +36,9 @@ SAST supports the following official analyzers:
|
||||||
- [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix))
|
- [`sobelow`](https://gitlab.com/gitlab-org/security-products/analyzers/sobelow) (Sobelow (Elixir Phoenix))
|
||||||
- [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT))
|
- [`spotbugs`](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) (SpotBugs with the Find Sec Bugs plugin (Ant, Gradle and wrapper, Grails, Maven and wrapper, SBT))
|
||||||
|
|
||||||
The following analyzers reached End of Support status and do not receive updates. They were replaced
|
The following GitLab analyzers have reached [End of Support](../../../update/terminology.md#end-of-support)
|
||||||
by the `semgrep` analyzer with GitLab-managed rules.
|
status and do not receive updates. They were replaced by the Semgrep-based analyzer with
|
||||||
|
GitLab-managed rules.
|
||||||
|
|
||||||
- [`bandit`](https://gitlab.com/gitlab-org/security-products/analyzers/bandit) (Bandit); [End of Support](https://gitlab.com/gitlab-org/gitlab/-/issues/352554) in GitLab 15.4.
|
- [`bandit`](https://gitlab.com/gitlab-org/security-products/analyzers/bandit) (Bandit); [End of Support](https://gitlab.com/gitlab-org/gitlab/-/issues/352554) in GitLab 15.4.
|
||||||
- [`brakeman`](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman) (Brakeman); [End of Support](https://gitlab.com/gitlab-org/gitlab/-/issues/412060) in GitLab 17.0.
|
- [`brakeman`](https://gitlab.com/gitlab-org/security-products/analyzers/brakeman) (Brakeman); [End of Support](https://gitlab.com/gitlab-org/gitlab/-/issues/412060) in GitLab 17.0.
|
||||||
|
|
|
@ -91,7 +91,9 @@ For more information about our plans for language support in SAST, see the [cate
|
||||||
|
|
||||||
## End of supported analyzers
|
## End of supported analyzers
|
||||||
|
|
||||||
GitLab has reached End of Support for the below analyzers. These analyzers have been replaced by the Semgrep-based analyzer.
|
The following GitLab analyzers have reached [End of Support](../../../update/terminology.md#end-of-support)
|
||||||
|
status and do not receive updates. They were replaced by the Semgrep-based analyzer with
|
||||||
|
GitLab-managed rules.
|
||||||
|
|
||||||
| Language / framework | [Analyzer](analyzers.md) used for scanning | Minimum supported GitLab version | End Of Support GitLab version |
|
| Language / framework | [Analyzer](analyzers.md) used for scanning | Minimum supported GitLab version | End Of Support GitLab version |
|
||||||
|------------------------------|--------------------------------------------------------------------------------------------------------------| --------------------------------- | ------------------------------------------------------------- |
|
|------------------------------|--------------------------------------------------------------------------------------------------------------| --------------------------------- | ------------------------------------------------------------- |
|
||||||
|
|
|
@ -285,7 +285,7 @@ Configure FortiToken Cloud in GitLab. On your GitLab server:
|
||||||
|
|
||||||
### Set up a WebAuthn device
|
### Set up a WebAuthn device
|
||||||
|
|
||||||
> - Optional one-time password authentication for WebAuthn devices [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378844) in GitLab 15.10 [with a flag](../../../administration/feature_flags.md) named `webauthn_without_topt`. [Enabled on GitLab.com and self-managed by default](https://gitlab.com/gitlab-org/gitlab/-/issues/232671).
|
> - Optional one-time password authentication for WebAuthn devices [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378844) in GitLab 15.10 [with a flag](../../../administration/feature_flags.md) named `webauthn_without_totp`. [Enabled on GitLab.com and self-managed by default](https://gitlab.com/gitlab-org/gitlab/-/issues/232671).
|
||||||
|
|
||||||
FLAG:
|
FLAG:
|
||||||
On self-managed GitLab, by default, optional one-time password authentication for WebAuthn devices is not available. To enable the feature, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `webauthn_without_totp`.
|
On self-managed GitLab, by default, optional one-time password authentication for WebAuthn devices is not available. To enable the feature, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `webauthn_without_totp`.
|
||||||
|
|
|
@ -82,7 +82,6 @@ module API
|
||||||
optional :squash_option, type: String, values: %w[never always default_on default_off], desc: 'Squash default for project. One of `never`, `always`, `default_on`, or `default_off`.'
|
optional :squash_option, type: String, values: %w[never always default_on default_off], desc: 'Squash default for project. One of `never`, `always`, `default_on`, or `default_off`.'
|
||||||
optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
|
optional :mr_default_target_self, type: Boolean, desc: 'Merge requests of this forked project targets itself by default'
|
||||||
optional :warn_about_potentially_unwanted_characters, type: Boolean, desc: 'Warn about potentially unwanted characters'
|
optional :warn_about_potentially_unwanted_characters, type: Boolean, desc: 'Warn about potentially unwanted characters'
|
||||||
optional :repository_object_format, type: String, values: %w[sha1 sha256], desc: 'The object format of the project repository'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
params :optional_project_params_ee do
|
params :optional_project_params_ee do
|
||||||
|
@ -93,11 +92,16 @@ module API
|
||||||
use :optional_project_params_ee
|
use :optional_project_params_ee
|
||||||
end
|
end
|
||||||
|
|
||||||
|
params :optional_create_project_params_ce do
|
||||||
|
optional :repository_object_format, type: String, values: %w[sha1 sha256], desc: 'The object format of the project repository'
|
||||||
|
end
|
||||||
|
|
||||||
params :optional_create_project_params_ee do
|
params :optional_create_project_params_ee do
|
||||||
end
|
end
|
||||||
|
|
||||||
params :optional_create_project_params do
|
params :optional_create_project_params do
|
||||||
use :optional_project_params
|
use :optional_project_params
|
||||||
|
use :optional_create_project_params_ce
|
||||||
use :optional_create_project_params_ee
|
use :optional_create_project_params_ee
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,12 @@ module API
|
||||||
unauthorized! unless can?(current_user, :admin_remote_mirror, user_project)
|
unauthorized! unless can?(current_user, :admin_remote_mirror, user_project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def find_remote_mirror
|
||||||
|
user_project.remote_mirrors.find(params[:mirror_id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
params do
|
params do
|
||||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
|
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
|
||||||
end
|
end
|
||||||
|
@ -44,7 +50,7 @@ module API
|
||||||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||||
end
|
end
|
||||||
get ':id/remote_mirrors/:mirror_id' do
|
get ':id/remote_mirrors/:mirror_id' do
|
||||||
mirror = user_project.remote_mirrors.find(params[:mirror_id])
|
mirror = find_remote_mirror
|
||||||
|
|
||||||
present mirror, with: Entities::RemoteMirror
|
present mirror, with: Entities::RemoteMirror
|
||||||
end
|
end
|
||||||
|
@ -62,7 +68,7 @@ module API
|
||||||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||||
end
|
end
|
||||||
post ':id/remote_mirrors/:mirror_id/sync' do
|
post ':id/remote_mirrors/:mirror_id/sync' do
|
||||||
mirror = user_project.remote_mirrors.find(params[:mirror_id])
|
mirror = find_remote_mirror
|
||||||
|
|
||||||
result = ::RemoteMirrors::SyncService.new(user_project, current_user).execute(mirror)
|
result = ::RemoteMirrors::SyncService.new(user_project, current_user).execute(mirror)
|
||||||
|
|
||||||
|
@ -137,7 +143,7 @@ module API
|
||||||
use :mirror_branches_setting
|
use :mirror_branches_setting
|
||||||
end
|
end
|
||||||
put ':id/remote_mirrors/:mirror_id' do
|
put ':id/remote_mirrors/:mirror_id' do
|
||||||
mirror = user_project.remote_mirrors.find(params[:mirror_id])
|
mirror = find_remote_mirror
|
||||||
|
|
||||||
mirror_params = declared_params(include_missing: false)
|
mirror_params = declared_params(include_missing: false)
|
||||||
mirror_params[:id] = mirror_params.delete(:mirror_id)
|
mirror_params[:id] = mirror_params.delete(:mirror_id)
|
||||||
|
@ -152,7 +158,7 @@ module API
|
||||||
if result[:status] == :success
|
if result[:status] == :success
|
||||||
present mirror.reset, with: Entities::RemoteMirror
|
present mirror.reset, with: Entities::RemoteMirror
|
||||||
else
|
else
|
||||||
render_api_error!(result[:message], result[:http_status])
|
render_api_error!(result[:message], 400)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -170,7 +176,7 @@ module API
|
||||||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||||
end
|
end
|
||||||
delete ':id/remote_mirrors/:mirror_id' do
|
delete ':id/remote_mirrors/:mirror_id' do
|
||||||
mirror = user_project.remote_mirrors.find(params[:mirror_id])
|
mirror = find_remote_mirror
|
||||||
|
|
||||||
destroy_conditionally!(mirror) do
|
destroy_conditionally!(mirror) do
|
||||||
mirror_params = declared_params(include_missing: false).merge(_destroy: 1)
|
mirror_params = declared_params(include_missing: false).merge(_destroy: 1)
|
||||||
|
|
|
@ -51,6 +51,7 @@ module Sidebars
|
||||||
organizations/organizations#groups_and_projects
|
organizations/organizations#groups_and_projects
|
||||||
organizations/groups#new
|
organizations/groups#new
|
||||||
organizations/projects#edit
|
organizations/projects#edit
|
||||||
|
organizations/groups#edit
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
item_id: :organization_groups_and_projects
|
item_id: :organization_groups_and_projects
|
||||||
|
|
|
@ -9488,9 +9488,6 @@ msgstr ""
|
||||||
msgid "Browse files"
|
msgid "Browse files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Browse templates"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Build cannot be erased"
|
msgid "Build cannot be erased"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
|
||||||
import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue';
|
import CiEditorHeader from '~/ci/pipeline_editor/components/editor/ci_editor_header.vue';
|
||||||
import {
|
import {
|
||||||
pipelineEditorTrackingOptions,
|
pipelineEditorTrackingOptions,
|
||||||
TEMPLATE_REPOSITORY_URL,
|
|
||||||
EDITOR_APP_DRAWER_HELP,
|
EDITOR_APP_DRAWER_HELP,
|
||||||
EDITOR_APP_DRAWER_NONE,
|
EDITOR_APP_DRAWER_NONE,
|
||||||
} from '~/ci/pipeline_editor/constants';
|
} from '~/ci/pipeline_editor/constants';
|
||||||
|
@ -37,7 +36,6 @@ describe('CI Editor Header', () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const findLinkBtn = () => wrapper.findByTestId('template-repo-link');
|
|
||||||
const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
|
const findHelpBtn = () => wrapper.findByTestId('drawer-toggle');
|
||||||
const findCatalogRepoLinkButton = () => wrapper.findByTestId('catalog-repo-link');
|
const findCatalogRepoLinkButton = () => wrapper.findByTestId('catalog-repo-link');
|
||||||
|
|
||||||
|
@ -70,10 +68,6 @@ describe('CI Editor Header', () => {
|
||||||
expect(findCatalogRepoLinkButton().exists()).toBe(true);
|
expect(findCatalogRepoLinkButton().exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has the external-link icon', () => {
|
|
||||||
expect(findCatalogRepoLinkButton().props('icon')).toBe('external-link');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tracks the click on the Catalog button', () => {
|
it('tracks the click on the Catalog button', () => {
|
||||||
const { browseCatalog } = pipelineEditorTrackingOptions.actions;
|
const { browseCatalog } = pipelineEditorTrackingOptions.actions;
|
||||||
|
|
||||||
|
@ -81,31 +75,6 @@ describe('CI Editor Header', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('link button', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
createComponent();
|
|
||||||
trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('finds the browse template button', () => {
|
|
||||||
expect(findLinkBtn().exists()).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('contains the link to the template repo', () => {
|
|
||||||
expect(findLinkBtn().attributes('href')).toBe(TEMPLATE_REPOSITORY_URL);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has the external-link icon', () => {
|
|
||||||
expect(findLinkBtn().props('icon')).toBe('external-link');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tracks the click on the browse button', () => {
|
|
||||||
const { browseTemplates } = pipelineEditorTrackingOptions.actions;
|
|
||||||
|
|
||||||
testTracker(findLinkBtn(), browseTemplates);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('help button', () => {
|
describe('help button', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createComponent();
|
createComponent();
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { GlSprintf } from '@gitlab/ui';
|
||||||
|
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||||
|
import App from '~/organizations/groups/edit/components/app.vue';
|
||||||
|
|
||||||
|
describe('OrganizationGroupsEditApp', () => {
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const defaultProvide = {
|
||||||
|
group: {
|
||||||
|
fullName: 'Mock namespace / Foo bar',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const createComponent = () => {
|
||||||
|
wrapper = shallowMountExtended(App, {
|
||||||
|
provide: defaultProvide,
|
||||||
|
stubs: {
|
||||||
|
GlSprintf,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders page title', () => {
|
||||||
|
createComponent();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper.findByRole('heading', { name: 'Edit group: Mock namespace / Foo bar' }).exists(),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -310,6 +310,20 @@ RSpec.describe Organizations::OrganizationHelper, feature_category: :cell do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#organization_groups_edit_app_data' do
|
||||||
|
let_it_be(:group) { build_stubbed(:group, organization: organization) }
|
||||||
|
|
||||||
|
it 'returns expected json' do
|
||||||
|
expect(Gitlab::Json.parse(helper.organization_groups_edit_app_data(group))).to eq(
|
||||||
|
{
|
||||||
|
'group' => {
|
||||||
|
'full_name' => group.full_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe '#admin_organizations_index_app_data' do
|
describe '#admin_organizations_index_app_data' do
|
||||||
it 'returns expected json' do
|
it 'returns expected json' do
|
||||||
expect(Gitlab::Json.parse(helper.admin_organizations_index_app_data)).to eq(
|
expect(Gitlab::Json.parse(helper.admin_organizations_index_app_data)).to eq(
|
||||||
|
|
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe RemoteMirror, :mailer do
|
RSpec.describe RemoteMirror, :mailer, feature_category: :source_code_management do
|
||||||
before do
|
before do
|
||||||
stub_feature_flags(remote_mirror_no_delay: false)
|
stub_feature_flags(remote_mirror_no_delay: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'validations' do
|
||||||
|
it { is_expected.to allow_value(true, false).for(:only_protected_branches) }
|
||||||
|
it { is_expected.not_to allow_value(nil).for(:only_protected_branches) }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'URL validation' do
|
describe 'URL validation' do
|
||||||
context 'with a valid URL' do
|
context 'with a valid URL' do
|
||||||
it 'is valid' do
|
it 'is valid' do
|
||||||
|
|
|
@ -260,16 +260,6 @@ RSpec.describe API::ProjectPackages, feature_category: :package_registry do
|
||||||
|
|
||||||
expect(json_response['pipelines']).to be_empty
|
expect(json_response['pipelines']).to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not result in additional queries', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/439528' do # rubocop:disable Layout/LineLength -- We prefer to keep it on a single line, for simplicity sake
|
|
||||||
control = ActiveRecord::QueryRecorder.new do
|
|
||||||
get api(package_url, user)
|
|
||||||
end
|
|
||||||
|
|
||||||
expect do
|
|
||||||
get api(package_url, user)
|
|
||||||
end.not_to exceed_query_limit(control).with_threshold(4)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'project is public' do
|
context 'project is public' do
|
||||||
|
|
|
@ -4524,6 +4524,15 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with repository_object_format' do
|
||||||
|
it 'ignores repositotory object format field' do
|
||||||
|
put api(path, user), params: { name: 'new', repository_object_format: 'sha256' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response['repository_object_format']).to eq 'sha1'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when authenticated as project developer' do
|
context 'when authenticated as project developer' do
|
||||||
it 'does not update other attributes' do
|
it 'does not update other attributes' do
|
||||||
project_param = { path: 'bar',
|
project_param = { path: 'bar',
|
||||||
|
|
|
@ -201,6 +201,19 @@ RSpec.describe API::RemoteMirrors, feature_category: :source_code_management do
|
||||||
expect(json_response['error']).to eq('auth_method does not have a valid value')
|
expect(json_response['error']).to eq('auth_method does not have a valid value')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when only_protected_branches is not set' do
|
||||||
|
let(:params) { { url: 'https://foo:bar@test.com', enabled: true, only_protected_branches: nil } }
|
||||||
|
|
||||||
|
it 'returns an error' do
|
||||||
|
project.add_maintainer(user)
|
||||||
|
|
||||||
|
post api(route, user), params: params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
expect(json_response['message']['only_protected_branches']).to match_array(["can't be blank"])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT /projects/:id/remote_mirrors/:mirror_id' do
|
describe 'PUT /projects/:id/remote_mirrors/:mirror_id' do
|
||||||
|
@ -227,6 +240,32 @@ RSpec.describe API::RemoteMirrors, feature_category: :source_code_management do
|
||||||
expect(json_response['only_protected_branches']).to eq(true)
|
expect(json_response['only_protected_branches']).to eq(true)
|
||||||
expect(json_response['keep_divergent_refs']).to eq(true)
|
expect(json_response['keep_divergent_refs']).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when auth method is invalid' do
|
||||||
|
let(:params) { { enabled: true, auth_method: 'invalid' } }
|
||||||
|
|
||||||
|
it 'returns an error' do
|
||||||
|
project.add_maintainer(user)
|
||||||
|
|
||||||
|
put api(route, user), params: params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
expect(json_response['message']).to eq('Remote mirrors auth method is not included in the list')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when only_protected_branches is not set' do
|
||||||
|
let(:params) { { enabled: true, only_protected_branches: nil } }
|
||||||
|
|
||||||
|
it 'returns an error' do
|
||||||
|
project.add_maintainer(user)
|
||||||
|
|
||||||
|
put api(route, user), params: params
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
expect(json_response['message']).to eq("Remote mirrors only protected branches can't be blank")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'DELETE /projects/:id/remote_mirrors/:mirror_id' do
|
describe 'DELETE /projects/:id/remote_mirrors/:mirror_id' do
|
||||||
|
|
|
@ -93,4 +93,105 @@ RSpec.describe Organizations::GroupsController, feature_category: :cell do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #edit' do
|
||||||
|
let_it_be(:group) { create(:group, organization: organization) }
|
||||||
|
|
||||||
|
context 'when group exists' do
|
||||||
|
subject(:gitlab_request) do
|
||||||
|
get edit_groups_organization_path(organization, id: group.to_param)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user is not signed in' do
|
||||||
|
it_behaves_like 'organization - redirects to sign in page'
|
||||||
|
|
||||||
|
context 'when `ui_for_organizations` feature flag is disabled' do
|
||||||
|
before do
|
||||||
|
stub_feature_flags(ui_for_organizations: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'organization - redirects to sign in page'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user is signed in' do
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as as admin', :enable_admin_mode do
|
||||||
|
let_it_be(:user) { create(:admin) }
|
||||||
|
|
||||||
|
it_behaves_like 'organization - successful response'
|
||||||
|
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as a group owner' do
|
||||||
|
before_all do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'organization - successful response'
|
||||||
|
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as a user that is not an owner' do
|
||||||
|
it_behaves_like 'organization - not found response'
|
||||||
|
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'as an organization owner' do
|
||||||
|
let_it_be(:user) do
|
||||||
|
organization_user = create(:organization_owner, organization: organization)
|
||||||
|
organization_user.user
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'organization - successful response'
|
||||||
|
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when group is not in organization' do
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:organization_2) { create(:organization) }
|
||||||
|
|
||||||
|
subject(:gitlab_request) do
|
||||||
|
get edit_groups_organization_path(organization_2, id: group.to_param)
|
||||||
|
end
|
||||||
|
|
||||||
|
before_all do
|
||||||
|
group.add_owner(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'organization - not found response'
|
||||||
|
it_behaves_like 'organization - action disabled by `ui_for_organizations` feature flag'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when group does not exist' do
|
||||||
|
subject(:gitlab_request) do
|
||||||
|
get edit_groups_organization_path(organization, id: 'group-that-does-not-exist')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user is not signed in' do
|
||||||
|
it_behaves_like 'organization - redirects to sign in page'
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the user is signed in' do
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'organization - not found response'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,9 +4,19 @@ require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Organizations::GroupsController, :routing, feature_category: :cell do
|
RSpec.describe Organizations::GroupsController, :routing, feature_category: :cell do
|
||||||
let_it_be(:organization) { build(:organization) }
|
let_it_be(:organization) { build(:organization) }
|
||||||
|
let_it_be(:group) { build(:group, organization: organization) }
|
||||||
|
|
||||||
it 'routes to groups#new' do
|
it 'routes to groups#new' do
|
||||||
expect(get("/-/organizations/#{organization.path}/groups/new"))
|
expect(get("/-/organizations/#{organization.path}/groups/new"))
|
||||||
.to route_to('organizations/groups#new', organization_path: organization.path)
|
.to route_to('organizations/groups#new', organization_path: organization.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'routes to groups#edit' do
|
||||||
|
expect(get("/-/organizations/#{organization.path}/groups/#{group.full_path}/edit"))
|
||||||
|
.to route_to(
|
||||||
|
'organizations/groups#edit',
|
||||||
|
organization_path: organization.path,
|
||||||
|
id: group.to_param
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package senddata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testPrefix = "test:"
|
||||||
|
|
||||||
|
func TestPrefixMatch(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expectedMatch bool
|
||||||
|
}{
|
||||||
|
{"Match with correct prefix", "test:sendData", true},
|
||||||
|
{"Match with correct prefix and nested data", "test:otherData:nestedData", true},
|
||||||
|
{"Does not match with wrong prefix", "another:sendData", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
prefix := Prefix(testPrefix)
|
||||||
|
name := prefix.Name()
|
||||||
|
|
||||||
|
r.Contains(testPrefix, name)
|
||||||
|
r.Equal(tt.expectedMatch, prefix.Match(tt.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrefixUnpack(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
inputData string
|
||||||
|
expectedResult string
|
||||||
|
}{
|
||||||
|
{"Valid JSON data encoded with base64", "test data", "test data"},
|
||||||
|
{"Invalid base64 encoded data", "invalid_base64_encoded_data", "invalid_base64_encoded_data"},
|
||||||
|
{"Invalid JSON data encoded with base64", base64.URLEncoding.EncodeToString([]byte("invalid_json")), "aW52YWxpZF9qc29u"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
jsonBytes, err := json.Marshal(tt.inputData)
|
||||||
|
r.NoError(err)
|
||||||
|
sendData := base64.URLEncoding.EncodeToString(jsonBytes)
|
||||||
|
|
||||||
|
prefix := Prefix(testPrefix)
|
||||||
|
|
||||||
|
var result string
|
||||||
|
err = prefix.Unpack(&result, testPrefix+sendData)
|
||||||
|
r.NoError(err)
|
||||||
|
r.Equal(tt.expectedResult, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue