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/replicator.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/provisioning_service_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_NONE,
|
||||
pipelineEditorTrackingOptions,
|
||||
TEMPLATE_REPOSITORY_URL,
|
||||
} from '../../constants';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
browseCatalog: __('Browse CI/CD Catalog'),
|
||||
browseTemplates: __('Browse templates'),
|
||||
help: __('Help'),
|
||||
jobAssistant: s__('JobAssistant|Job assistant'),
|
||||
},
|
||||
TEMPLATE_REPOSITORY_URL,
|
||||
components: {
|
||||
GlButton,
|
||||
},
|
||||
|
@ -58,11 +55,6 @@ export default {
|
|||
const { label, actions } = pipelineEditorTrackingOptions;
|
||||
this.track(actions.openHelpDrawer, { label });
|
||||
},
|
||||
trackTemplateBrowsing() {
|
||||
const { label, actions } = pipelineEditorTrackingOptions;
|
||||
|
||||
this.track(actions.browseTemplates, { label });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -82,16 +74,6 @@ export default {
|
|||
>
|
||||
{{ $options.i18n.browseCatalog }}
|
||||
</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
|
||||
icon="information-o"
|
||||
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 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() {
|
||||
return {
|
||||
text: this.$options.i18n.modalRemove,
|
||||
attributes: { disabled: this.disableModalSubmit, variant: 'danger' },
|
||||
attributes: {
|
||||
disabled: this.disableModalSubmit,
|
||||
variant: 'danger',
|
||||
type: 'submit',
|
||||
form: this.$options.removeFormId,
|
||||
},
|
||||
};
|
||||
},
|
||||
commandModalId() {
|
||||
return `init-command-modal-${this.state.name}`;
|
||||
},
|
||||
modalInputId() {
|
||||
return `terraform-state-remove-input-${this.state.name}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
|
@ -188,6 +196,7 @@ export default {
|
|||
this.showCommandModal = true;
|
||||
},
|
||||
},
|
||||
removeFormId: 'remove-state-form',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -255,30 +264,26 @@ export default {
|
|||
</gl-sprintf>
|
||||
</template>
|
||||
|
||||
<p>
|
||||
<gl-sprintf :message="$options.i18n.modalBody">
|
||||
<template #name>
|
||||
<span>{{ state.name }}</span>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
|
||||
<gl-form-group>
|
||||
<template #label>
|
||||
<gl-sprintf :message="$options.i18n.modalInputLabel">
|
||||
<form :id="$options.removeFormId" @submit.prevent="remove">
|
||||
<p>
|
||||
<gl-sprintf :message="$options.i18n.modalBody">
|
||||
<template #name>
|
||||
<code>{{ state.name }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
<gl-form-input
|
||||
:id="`terraform-state-remove-input-${state.name}`"
|
||||
ref="input"
|
||||
v-model="removeConfirmText"
|
||||
type="text"
|
||||
@keyup.enter="remove"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</p>
|
||||
|
||||
<gl-form-group :label-for="modalInputId">
|
||||
<template #label>
|
||||
<gl-sprintf :message="$options.i18n.modalInputLabel">
|
||||
<template #name>
|
||||
<code>{{ state.name }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
<gl-form-input :id="modalInputId" ref="input" v-model="removeConfirmText" type="text" />
|
||||
</gl-form-group>
|
||||
</form>
|
||||
</gl-modal>
|
||||
|
||||
<init-command-modal
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
width: $chip-size;
|
||||
height: $chip-size;
|
||||
background: $white;
|
||||
background-image: linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%),
|
||||
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-100 25%, transparent 0%, transparent 75%, $gray-100 0%);
|
||||
background-size: $bg-size $bg-size;
|
||||
background-position: 0 0, $bg-pos $bg-pos;
|
||||
|
||||
|
|
|
@ -83,37 +83,17 @@ $size-scale: (
|
|||
);
|
||||
|
||||
// Color schema
|
||||
$darken-normal-factor: 7% !default;
|
||||
$darken-dark-factor: 10% !default;
|
||||
|
||||
$purple: #6d49cb !default;
|
||||
$purple-light: #ede8fb !default;
|
||||
|
||||
$gray-light: $gray-10 !default;
|
||||
$gray-lighter: lighten($gray-50, 4) !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
|
||||
*/
|
||||
$border-color: $gray-100;
|
||||
$shadow-color: $t-gray-a-08;
|
||||
$well-expand-item: #e8f2f7 !default;
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,8 +27,8 @@ $dropdown-item-padding-x: 12px;
|
|||
$popover-max-width: 300px;
|
||||
$popover-border-width: 1px;
|
||||
$popover-border-color: $border-color;
|
||||
$popover-box-shadow: 0 $border-radius-small $gl-border-radius-base 0 $shadow-color;
|
||||
$popover-arrow-outer-color: $shadow-color;
|
||||
$popover-box-shadow: 0 $border-radius-small $gl-border-radius-base 0 $t-gray-a-08;
|
||||
$popover-arrow-outer-color: $t-gray-a-08;
|
||||
$h1-font-size: 14px * 2.5;
|
||||
$h2-font-size: 14px * 2;
|
||||
$h3-font-size: 14px * 1.75;
|
||||
|
|
|
@ -3,11 +3,6 @@
|
|||
$gray-light: lighten($gray-10, 2);
|
||||
$gray-lighter: darken($gray-50, 4);
|
||||
$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;
|
||||
|
||||
|
|
|
@ -8,9 +8,13 @@ module Organizations
|
|||
urgency :low, [:create, :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 edit; end
|
||||
|
||||
def create
|
||||
response = create_group
|
||||
@group = response[:group]
|
||||
|
@ -24,10 +28,20 @@ module Organizations
|
|||
|
||||
private
|
||||
|
||||
def group
|
||||
@group ||= Group.in_organization(organization).find_by_full_path(params[:id])
|
||||
end
|
||||
|
||||
def create_group
|
||||
create_service_params = group_params.merge(organization_id: organization.id)
|
||||
Groups::CreateService.new(current_user, create_service_params).execute
|
||||
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
|
||||
|
||||
|
|
|
@ -65,6 +65,12 @@ module Organizations
|
|||
}.to_json
|
||||
end
|
||||
|
||||
def organization_groups_edit_app_data(group)
|
||||
{
|
||||
group: group.slice(:full_name)
|
||||
}.to_json
|
||||
end
|
||||
|
||||
def admin_organizations_index_app_data
|
||||
shared_organization_index_app_data.to_json
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@ class DraftNote < ApplicationRecord
|
|||
include Gitlab::Utils::StrongMemoize
|
||||
include Sortable
|
||||
include ShaAttribute
|
||||
include BulkInsertSafe
|
||||
|
||||
PUBLISH_ATTRS = %i[noteable type note internal].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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
scope(
|
||||
path: 'groups/*id',
|
||||
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex }
|
||||
) do
|
||||
resource(
|
||||
:groups,
|
||||
path: '/',
|
||||
only: [:edit],
|
||||
as: :groups_organization
|
||||
)
|
||||
end
|
||||
|
||||
scope(
|
||||
path: 'projects/*namespace_id',
|
||||
as: :namespace,
|
||||
|
|
|
@ -23,3 +23,4 @@ tokens:
|
|||
- "GitLab (v)?11."
|
||||
- "GitLab (v)?12."
|
||||
- "GitLab (v)?13."
|
||||
- "GitLab (v)?14."
|
||||
|
|
|
@ -15,7 +15,7 @@ swap:
|
|||
active users: "billable users"
|
||||
air(?:-| )?gapped: "offline environment"
|
||||
bullet: "list item"
|
||||
click: "select"
|
||||
(?<!right-)click(?!-through): "select"
|
||||
code base: "codebase"
|
||||
config: "configuration"
|
||||
confirmation box: "confirmation dialog"
|
||||
|
|
|
@ -133,7 +133,6 @@ Choose the one or three step method according to your registry installation.
|
|||
|
||||
#### One-step migration
|
||||
|
||||
WARNING:
|
||||
WARNING:
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
|
||||
```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
|
||||
|
@ -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>]'
|
||||
```
|
||||
|
||||
[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**
|
||||
|
||||
|
@ -241,7 +241,7 @@ GITLAB_SIMULATE_SAAS=1 RAILS_ENV=development bundle exec rake 'gitlab:duo:setup[
|
|||
1. Enable **Experiment & Beta features**.
|
||||
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. Setup [AI Gateway](#set-up).
|
||||
1. Setup [AI Gateway](#test-ai-features-with-ai-gateway-locally).
|
||||
|
||||
### Help
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ For each scanner, an analyzer:
|
|||
- Handles its execution.
|
||||
- Converts its output to a [standard format](../terminology/index.md#secure-report-format).
|
||||
|
||||
## SAST analyzers
|
||||
## 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))
|
||||
- [`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
|
||||
by the `semgrep` analyzer with GitLab-managed rules.
|
||||
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.
|
||||
|
||||
- [`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.
|
||||
|
|
|
@ -91,7 +91,9 @@ For more information about our plans for language support in SAST, see the [cate
|
|||
|
||||
## 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 |
|
||||
|------------------------------|--------------------------------------------------------------------------------------------------------------| --------------------------------- | ------------------------------------------------------------- |
|
||||
|
|
|
@ -285,7 +285,7 @@ Configure FortiToken Cloud in GitLab. On your GitLab server:
|
|||
|
||||
### 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:
|
||||
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 :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 :repository_object_format, type: String, values: %w[sha1 sha256], desc: 'The object format of the project repository'
|
||||
end
|
||||
|
||||
params :optional_project_params_ee do
|
||||
|
@ -93,11 +92,16 @@ module API
|
|||
use :optional_project_params_ee
|
||||
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
|
||||
end
|
||||
|
||||
params :optional_create_project_params do
|
||||
use :optional_project_params
|
||||
use :optional_create_project_params_ce
|
||||
use :optional_create_project_params_ee
|
||||
end
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@ module API
|
|||
unauthorized! unless can?(current_user, :admin_remote_mirror, user_project)
|
||||
end
|
||||
|
||||
helpers do
|
||||
def find_remote_mirror
|
||||
user_project.remote_mirrors.find(params[:mirror_id])
|
||||
end
|
||||
end
|
||||
|
||||
params do
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
|
||||
end
|
||||
|
@ -44,7 +50,7 @@ module API
|
|||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||
end
|
||||
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
|
||||
end
|
||||
|
@ -62,7 +68,7 @@ module API
|
|||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||
end
|
||||
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)
|
||||
|
||||
|
@ -137,7 +143,7 @@ module API
|
|||
use :mirror_branches_setting
|
||||
end
|
||||
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[:id] = mirror_params.delete(:mirror_id)
|
||||
|
@ -152,7 +158,7 @@ module API
|
|||
if result[:status] == :success
|
||||
present mirror.reset, with: Entities::RemoteMirror
|
||||
else
|
||||
render_api_error!(result[:message], result[:http_status])
|
||||
render_api_error!(result[:message], 400)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -170,7 +176,7 @@ module API
|
|||
requires :mirror_id, type: String, desc: 'The ID of a remote mirror'
|
||||
end
|
||||
delete ':id/remote_mirrors/:mirror_id' do
|
||||
mirror = user_project.remote_mirrors.find(params[:mirror_id])
|
||||
mirror = find_remote_mirror
|
||||
|
||||
destroy_conditionally!(mirror) do
|
||||
mirror_params = declared_params(include_missing: false).merge(_destroy: 1)
|
||||
|
|
|
@ -51,6 +51,7 @@ module Sidebars
|
|||
organizations/organizations#groups_and_projects
|
||||
organizations/groups#new
|
||||
organizations/projects#edit
|
||||
organizations/groups#edit
|
||||
]
|
||||
},
|
||||
item_id: :organization_groups_and_projects
|
||||
|
|
|
@ -9488,9 +9488,6 @@ msgstr ""
|
|||
msgid "Browse files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Browse templates"
|
||||
msgstr ""
|
||||
|
||||
msgid "Build cannot be erased"
|
||||
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 {
|
||||
pipelineEditorTrackingOptions,
|
||||
TEMPLATE_REPOSITORY_URL,
|
||||
EDITOR_APP_DRAWER_HELP,
|
||||
EDITOR_APP_DRAWER_NONE,
|
||||
} 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 findCatalogRepoLinkButton = () => wrapper.findByTestId('catalog-repo-link');
|
||||
|
||||
|
@ -70,10 +68,6 @@ describe('CI Editor Header', () => {
|
|||
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', () => {
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
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
|
||||
|
||||
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
|
||||
it 'returns expected json' do
|
||||
expect(Gitlab::Json.parse(helper.admin_organizations_index_app_data)).to eq(
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe RemoteMirror, :mailer do
|
||||
RSpec.describe RemoteMirror, :mailer, feature_category: :source_code_management do
|
||||
before do
|
||||
stub_feature_flags(remote_mirror_no_delay: false)
|
||||
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
|
||||
context 'with a valid URL' 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
|
||||
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
|
||||
|
||||
context 'project is public' do
|
||||
|
|
|
@ -4524,6 +4524,15 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and
|
|||
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
|
||||
it 'does not update other attributes' do
|
||||
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')
|
||||
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
|
||||
|
||||
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['keep_divergent_refs']).to eq(true)
|
||||
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
|
||||
|
||||
describe 'DELETE /projects/:id/remote_mirrors/:mirror_id' do
|
||||
|
|
|
@ -93,4 +93,105 @@ RSpec.describe Organizations::GroupsController, feature_category: :cell do
|
|||
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
|
||||
|
|
|
@ -4,9 +4,19 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Organizations::GroupsController, :routing, feature_category: :cell do
|
||||
let_it_be(:organization) { build(:organization) }
|
||||
let_it_be(:group) { build(:group, organization: organization) }
|
||||
|
||||
it 'routes to groups#new' do
|
||||
expect(get("/-/organizations/#{organization.path}/groups/new"))
|
||||
.to route_to('organizations/groups#new', organization_path: organization.path)
|
||||
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
|
||||
|
|
|
@ -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