Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
58c37fa91f
commit
cddb9394be
|
|
@ -0,0 +1,138 @@
|
|||
<!--The purpose of this issue template is to ensure that Product Designers have all the information they need BEFORE starting design (workflow::ready for design). Product Designers should collaborate with the team members requesting design work to ensure the request is fully thought through and that all necessary details to meet users' needs are included.
|
||||
|
||||
For example:
|
||||
|
||||
- Who's the user?
|
||||
- What are they trying to accomplish?
|
||||
- Why do they need this?, etc.-->
|
||||
|
||||
## Problem and scope
|
||||
|
||||
<!--To be filled out by designer in collaboration with requestor (if different from team counterparts), PM and Engineering counterparts-->
|
||||
|
||||
<details>
|
||||
<summary>See details</summary>
|
||||
|
||||
### What is the problem to solve?
|
||||
|
||||
`{ Add a brief description about the problem to solve for }`
|
||||
|
||||
### Who is the design solution for?
|
||||
|
||||
`{ Add persona and/or job performer }`
|
||||
|
||||
### What is the [Job](https://handbook.gitlab.com/handbook/product/ux/jobs-to-be-done/#main-job-what-is-the-job-performer-trying-to-get-done) this user is trying to achieve?
|
||||
|
||||
- ...
|
||||
|
||||
### What [outcomes](https://handbook.gitlab.com/handbook/product/ux/jobs-to-be-done/outcome-driven-innovation-pilot/topics-and-definitions/#outcomes) is this design solution helping them achieve?
|
||||
|
||||
If you have measured Outcome data, put that in the table. If not, delete the table and add the Outcomes to be designed for in a bulleted list.
|
||||
|
||||
| Outcome Statement | Importance | Satisfaction | Overall Score |
|
||||
|-------------------|------------|--------------|---------------|
|
||||
| `{ e.g. Minimize the time it takes for users to navigate to the desired section of the application. }` | `5` | `1` | `9` |
|
||||
|
||||
|
||||
### What are the requirements necessary to solve for this problem and Outcomes?
|
||||
|
||||
`{ Create a bulleted list of everything that will need to be addressed in the holistic Design Vision in order to fully solve for the problem and Outcomes. Work with your counterparts to define this list. Keep it solution agnostic and try to understand any technical constraints that each requirement may imply. Only remove a requirement due to a technical constraint if it's not technically feasible to build within a reasonable amount of time (1-3 milestones is reasonable, anything longer than that you'll have to decide as a team how important it is to keep it in scope or address it in parallel in later iterations). }`
|
||||
|
||||
* ...
|
||||
* ...
|
||||
* ...
|
||||
|
||||
### What supporting research or customer validation do we have?
|
||||
|
||||
- `{ Add links to any supporting research and related problem validation issues }`
|
||||
|
||||
### What is the timeline?
|
||||
|
||||
`{ Add milestone or link to planning issue that clarifies when the design must be ready for the Build phase by }`
|
||||
|
||||
### What are the technical constraints?
|
||||
|
||||
`{ `:warning:` This is to understand initial constraints in which the design solution needs to work within, NOT whether the solution can be implemented in a given milestone.`
|
||||
|
||||
``Once the Product Designer has come up with a holistic Design Vision, or an ideal state for solving the problem, they should collaborate with their team members and engineers to continue the technical feasibility discussion during ``~"workflow::planning breakdown"``. }``
|
||||
|
||||
- `{ e.g. All visualization must use [eCharts](https://echarts.apache.org/examples/en/index.html#chart-type-line) }`
|
||||
- `{ e.g. Data prior to 2024-10-10 will not be available }`
|
||||
- `{ e.g. Solution will only be visible to Maintainer roles }`
|
||||
|
||||
### In what parts of GitLab will this solution be available?
|
||||
|
||||
Plans:
|
||||
|
||||
* [ ] Free
|
||||
* [ ] Premium
|
||||
* [ ] Ultimate
|
||||
|
||||
Instances:
|
||||
|
||||
* [ ] Self-managed
|
||||
* [ ] Dedicated
|
||||
* [ ] GitLab.com
|
||||
|
||||
Levels:
|
||||
|
||||
* [ ] Instance
|
||||
* [ ] Group
|
||||
* [ ] Project
|
||||
|
||||
### How will we know if the solution is successful?
|
||||
|
||||
`{ If you don't have measured Outcome data to measure against, what other success metrics can we use? Otherwise you can reference the Outcomes table above (your goal is to beat the previous Outcome measurements Satisfaction numbers with this new design). }`
|
||||
|
||||
</details>
|
||||
|
||||
## Ready for design
|
||||
|
||||
<details>
|
||||
<summary>See checklist</summary>
|
||||
|
||||
<!--*** Note for Product Designer:
|
||||
- Do not begin designing until you are confident you have everything you need to begin designing.
|
||||
- Do not default to designing a small MVC first. Instead, think about the ideal solution / holistic Design Vision first.-->
|
||||
|
||||
* [ ] The problem has been defined and is well understood
|
||||
* [ ] Who the design solution is for has been defined
|
||||
* [ ] User goals and outcomes have been defined
|
||||
* [ ] Supporting research has been reviewed and linked
|
||||
* [ ] The product requirements have been defined, and the scope has been agreed upon
|
||||
* [ ] Success metrics have been defined and agreed upon
|
||||
* [ ] I, as the Product Designer, believe I have all the information I need to begin creating a design solution
|
||||
* [ ] Move this issue to \~"workflow::ready for design" or ~"workflow::design" :tada:
|
||||
* [ ] (Optional) Help improve this issue template, [view feedback issue](https://gitlab.com/gitlab-org/gitlab/-/issues/519682)
|
||||
|
||||
</details>
|
||||
|
||||
## Proposal
|
||||
|
||||
<details>
|
||||
<summary>See checklist reminder</summary>
|
||||
|
||||
* [ ] Follow the [Product design process](https://handbook.gitlab.com/handbook/product/ux/product-designer/#ideate-and-iterate)
|
||||
* [ ] [Start with a long-term vision](https://handbook.gitlab.com/handbook/values/#start-with-a-long-term-vision)
|
||||
* [ ] Remember to link your video walkthrough, prototypes, Figma project
|
||||
|
||||
</details>
|
||||
|
||||
- :tv: Walkthrough
|
||||
- :frame_photo: Design Solution Proposal
|
||||
- ❖ Figma project →
|
||||
|
||||
## Design breakdown
|
||||
|
||||
Once the proposal is agreed upon, work with your team to break it down into buildable parts (MVC, Iteration 1, Iteration 2, etc... until fully built).
|
||||
|
||||
<details>
|
||||
<summary>See checklist</summary>
|
||||
|
||||
* [ ] Design Vision broken down into MVC and follow-up iterations based on their ability to stand alone and provide value to the user
|
||||
* [ ] Create MVC and other necessary Iteration 1, Iteration 2... issues and add them as Linked items to this issue
|
||||
* [ ] Include all necessary requirements, and specs needed to create the designs for each broken down issue
|
||||
|
||||
</details>
|
||||
|
||||
/label ~"UX" ~"UX Template Applied" ~"workflow::start"
|
||||
|
|
@ -674,7 +674,6 @@ RSpec/FeatureCategory:
|
|||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_deployment_approvals_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/epics_deepest_relationship_level_metric_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/license_management_jobs_metric_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environment_approval_rules_required_approvals_average_metric_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environments_required_approvals_average_metric_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/user_cap_setting_enabled_metric_spec.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
efced52dc4e4f9f202e32dc6239573d4aceb4d4e
|
||||
e947561ab2240a27ca13c4339e2cdd70115fc3ff
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
<script>
|
||||
import { GlButton, GlFormGroup, GlFormInput, GlAlert, GlSprintf, GlLink, GlIcon } from '@gitlab/ui';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlFormGroup,
|
||||
GlFormInput,
|
||||
GlAlert,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlIcon,
|
||||
MultiStepFormTemplate,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
inject: {
|
||||
backButtonPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
namespaceId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
messageAdmin: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
isCiCdOnly: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
isConfigured: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
buttonAuthHref: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
formPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showAuthButton() {
|
||||
return !this.isCiCdOnly && this.isConfigured;
|
||||
},
|
||||
showAdminMessage() {
|
||||
return !this.isCiCdOnly && !this.isConfigured;
|
||||
},
|
||||
},
|
||||
csrf,
|
||||
placeholders: {
|
||||
token: '8d3f016698e...',
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form method="post" :action="formPath">
|
||||
<input :value="$options.csrf.token" type="hidden" name="authenticity_token" />
|
||||
<input
|
||||
id="namespace_id"
|
||||
type="hidden"
|
||||
name="namespace_id"
|
||||
autocomplete="off"
|
||||
:value="namespaceId"
|
||||
/>
|
||||
<multi-step-form-template
|
||||
:title="s__('ProjectsNew|Authenticate with GitHub')"
|
||||
:current-step="3"
|
||||
:steps-total="4"
|
||||
>
|
||||
<template #form>
|
||||
<template v-if="showAuthButton">
|
||||
<gl-button
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
icon="github"
|
||||
:href="buttonAuthHref"
|
||||
data-testid="github-auth-button"
|
||||
>
|
||||
{{ s__('ProjectsNew|Authenticate through GitHub') }}
|
||||
</gl-button>
|
||||
<div class="gl-my-6 gl-flex gl-items-center gl-gap-4">
|
||||
<div class="gl-border-t gl-w-full"></div>
|
||||
<div class="gl-text-primary">{{ __('or') }}</div>
|
||||
<div class="gl-border-t gl-w-full"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<gl-alert v-if="showAdminMessage" class="gl-mb-5" :dismissible="false">
|
||||
<span v-safe-html="messageAdmin"></span>
|
||||
</gl-alert>
|
||||
|
||||
<gl-form-group :label="__('Use personal access token')" label-for="personal_access_token">
|
||||
<gl-form-input
|
||||
id="personal_access_token"
|
||||
name="personal_access_token"
|
||||
type="password"
|
||||
required
|
||||
:placeholder="$options.placeholders.token"
|
||||
/>
|
||||
<template #description>
|
||||
<p class="gl-mb-3">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'GithubImport|Create and provide your GitHub %{linkStart}personal access token%{linkEnd}.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #link="{ content }">
|
||||
<gl-link href="https://github.com/settings/tokens" target="_blank">{{
|
||||
content
|
||||
}}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p class="gl-mb-3">
|
||||
{{
|
||||
s__(
|
||||
'GithubImport|Use a classic GitHub personal access token with the following scopes:',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<ul>
|
||||
<li v-if="isCiCdOnly" class="gl-mb-3">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'GithubImporter|%{codeStart}repo%{codeEnd}: Used to display a list of your public and private repositories that are available to connect to.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #code="{ content }">
|
||||
<code>{{ content }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</li>
|
||||
<li v-if="!isCiCdOnly" class="gl-mb-3">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'GithubImporter|%{codeStart}repo%{codeEnd}: Used to display a list of your public and private repositories that are available to import from.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #code="{ content }">
|
||||
<code>{{ content }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</li>
|
||||
<li v-if="!isCiCdOnly" class="gl-mb-3">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'GithubImporter|%{codeStart}read:org%{codeEnd} (optional): Used to import collaborators from GitHub repositories, or if your project has Git LFS files.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #code="{ content }">
|
||||
<code>{{ content }}</code>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</li>
|
||||
</ul>
|
||||
<gl-sprintf :message="s__('GithubImport|%{linkStart}Learn more%{linkEnd}.')">
|
||||
<template #link="{ content }">
|
||||
<gl-link
|
||||
href="https://github.com/settings/tokens#use-a-github-personal-access-token"
|
||||
target="_blank"
|
||||
>
|
||||
{{ content }}
|
||||
<gl-icon name="external-link" :aria-label="__('(external link)')" />
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
</gl-form-group>
|
||||
</template>
|
||||
<template #back>
|
||||
<gl-button category="primary" variant="default" :href="backButtonPath">
|
||||
{{ __('Go back') }}
|
||||
</gl-button>
|
||||
</template>
|
||||
<template #next>
|
||||
<gl-button type="submit" category="primary" variant="confirm">
|
||||
{{ __('Next step') }}
|
||||
</gl-button>
|
||||
</template>
|
||||
</multi-step-form-template>
|
||||
</form>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import ImportFromGithubApp from './import_from_github_app.vue';
|
||||
|
||||
export function initGitHubImportProjectForm() {
|
||||
const el = document.getElementById('js-vue-import-github-project-app');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { viewModel } = el.dataset;
|
||||
const provide = JSON.parse(viewModel);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
name: 'ImportFromGitHubRoot',
|
||||
provide: convertObjectPropsToCamelCase(provide),
|
||||
render(createElement) {
|
||||
return createElement(ImportFromGithubApp);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
import { initGitHubImportProjectForm } from '~/import/github';
|
||||
import { initPersonalAccessTokenFormValidation } from './init_personal_access_token_form_validation';
|
||||
|
||||
initPersonalAccessTokenFormValidation();
|
||||
if (gon.features.newProjectCreationForm) {
|
||||
initGitHubImportProjectForm();
|
||||
} else {
|
||||
initPersonalAccessTokenFormValidation();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,6 @@ module Ci
|
|||
scope :with_live_trace, -> { where_exists(Ci::BuildTraceChunk.scoped_build) }
|
||||
scope :with_stale_live_trace, -> { with_live_trace.finished_before(12.hours.ago) }
|
||||
scope :finished_before, ->(date) { finished.where('finished_at < ?', date) }
|
||||
scope :license_management_jobs, -> { where(name: %i[license_management license_scanning]) } # handle license rename https://gitlab.com/gitlab-org/gitlab/issues/8911
|
||||
# WARNING: This scope could lead to performance implications for large size of tables `ci_builds` and ci_runners`.
|
||||
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/123131
|
||||
scope :with_runner_type, ->(runner_type) { joins(:runner).where(runner: { runner_type: runner_type }) }
|
||||
|
|
@ -1343,7 +1342,14 @@ module Ci
|
|||
end
|
||||
|
||||
def track_ci_build_created_event
|
||||
Gitlab::InternalEvents.track_event('create_ci_build', project: project, user: user)
|
||||
Gitlab::InternalEvents.track_event(
|
||||
'create_ci_build',
|
||||
project: project,
|
||||
user: user,
|
||||
additional_properties: {
|
||||
property: name
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def partition_id_prefix_in_16_bit_encode
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ module HasUserType
|
|||
llm_bot: 14,
|
||||
placeholder: 15,
|
||||
duo_code_review_bot: 16,
|
||||
import_user: 17,
|
||||
ci_pipeline_bot: 18
|
||||
import_user: 17
|
||||
}.with_indifferent_access.freeze
|
||||
|
||||
BOT_USER_TYPES = %w[
|
||||
|
|
@ -39,7 +38,6 @@ module HasUserType
|
|||
service_account
|
||||
llm_bot
|
||||
duo_code_review_bot
|
||||
ci_pipeline_bot
|
||||
].freeze
|
||||
|
||||
# `service_account` allows instance/namespaces to configure a user for external integrations/automations
|
||||
|
|
|
|||
|
|
@ -1260,7 +1260,7 @@ class Repository
|
|||
|
||||
def get_patch_id(old_revision, new_revision)
|
||||
raw_repository.get_patch_id(old_revision, new_revision)
|
||||
rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository => e
|
||||
rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, Gitlab::Git::CommandTimedOut => e
|
||||
# This is expected when there are no differences between the old_revision and the new_revision.
|
||||
# It's not ideal, but is simpler to handle this here than making breaking changes to gitaly.
|
||||
return if e.message.match?(/no difference between old and new revision./)
|
||||
|
|
|
|||
|
|
@ -3,50 +3,61 @@
|
|||
- header_title _("New project"), new_project_path
|
||||
- add_to_breadcrumbs s_('ProjectsNew|Import project'), new_project_path(anchor: 'import_project')
|
||||
|
||||
= render ::Layouts::PageHeadingComponent.new('') do |c|
|
||||
- c.with_heading do
|
||||
.gl-flex.gl-gap-3.gl-items-center
|
||||
= sprite_icon('github', size: 32)
|
||||
= title
|
||||
- c.with_description do
|
||||
= import_github_authorize_message
|
||||
- if !has_ci_cd_only_params?
|
||||
.gl-mt-5
|
||||
- if github_import_configured?
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm,
|
||||
href: status_import_github_path(namespace_id: params[:namespace_id]),
|
||||
icon: 'github') do
|
||||
= title
|
||||
- else
|
||||
= render Pajamas::AlertComponent.new(variant: :info, dismissible: false) do |c|
|
||||
- c.with_body do
|
||||
= import_configure_github_admin_message
|
||||
- if Feature.enabled?(:new_project_creation_form, @user)
|
||||
#js-vue-import-github-project-app{ data: { view_model: Gitlab::Json.generate({
|
||||
back_button_path: new_project_path(anchor: 'import_project'),
|
||||
namespace_id: namespace_id_from(params),
|
||||
message_admin: import_configure_github_admin_message,
|
||||
is_ci_cd_only: has_ci_cd_only_params?,
|
||||
is_configured: github_import_configured?,
|
||||
button_auth_href: status_import_github_path(namespace_id: params[:namespace_id]),
|
||||
form_path: personal_access_token_import_github_path
|
||||
}) } }
|
||||
- else
|
||||
= render ::Layouts::PageHeadingComponent.new('') do |c|
|
||||
- c.with_heading do
|
||||
.gl-flex.gl-gap-3.gl-items-center
|
||||
= sprite_icon('github', size: 32)
|
||||
= title
|
||||
- c.with_description do
|
||||
= import_github_authorize_message
|
||||
- if !has_ci_cd_only_params?
|
||||
.gl-mt-5
|
||||
- if github_import_configured?
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm,
|
||||
href: status_import_github_path(namespace_id: params[:namespace_id]),
|
||||
icon: 'github') do
|
||||
= title
|
||||
- else
|
||||
= render Pajamas::AlertComponent.new(variant: :info, dismissible: false) do |c|
|
||||
- c.with_body do
|
||||
= import_configure_github_admin_message
|
||||
|
||||
= form_tag personal_access_token_import_github_path, method: :post, class: 'gl-mt-3' do
|
||||
.form-group.gl-form-group
|
||||
%label.col-form-label{ for: 'personal_access_token' }= _('Personal access token')
|
||||
= hidden_field_tag(:namespace_id, params[:namespace_id])
|
||||
= password_field_tag :personal_access_token, '', class: 'form-control gl-form-input js-import-github-pat-field', placeholder: _('e.g. %{token}') % { token: '8d3f016698e...' }, data: { testid: 'personal-access-token-field' }
|
||||
%p.invalid-feedback.js-import-github-pat-validation
|
||||
= _('Personal access token is required.')
|
||||
%span.form-text.gl-text-subtle
|
||||
- code_pair = tag_pair(tag.code, :code_start, :code_end)
|
||||
- github_link_tag_pair = tag_pair(link_to('', 'https://github.com/settings/tokens', target: '_blank', rel: 'noopener noreferrer'), :link_start, :link_end)
|
||||
= safe_format(s_('GithubImport|Create and provide your GitHub %{link_start}personal access token%{link_end}.'), github_link_tag_pair)
|
||||
%br
|
||||
= safe_format(s_('GithubImport|Use a classic GitHub personal access token with the following scopes:'))
|
||||
%ul
|
||||
- if has_ci_cd_only_params?
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}repo%{code_end}: Used to display a list of your public and private repositories that are available to connect to.'), code_pair)
|
||||
- else
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}repo%{code_end}: Used to display a list of your public and private repositories that are available to import from.'), code_pair)
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}read:org%{code_end} (optional): Used to import collaborators from GitHub repositories, or if your project has Git LFS files.'), code_pair)
|
||||
- docs_link = link_to('', help_page_path('user/project/import/github.md', anchor: 'use-a-github-personal-access-token'), target: '_blank', rel: 'noopener noreferrer')
|
||||
- docs_link_tag_pair = tag_pair(docs_link, :link_start, :link_end)
|
||||
= safe_format(s_('GithubImport|%{link_start}Learn more%{link_end}.'), docs_link_tag_pair)
|
||||
= form_tag personal_access_token_import_github_path, method: :post, class: 'gl-mt-3' do
|
||||
.form-group.gl-form-group
|
||||
%label.col-form-label{ for: 'personal_access_token' }= _('Personal access token')
|
||||
= hidden_field_tag(:namespace_id, params[:namespace_id])
|
||||
= password_field_tag :personal_access_token, '', class: 'form-control gl-form-input js-import-github-pat-field', placeholder: _('e.g. %{token}') % { token: '8d3f016698e...' }, data: { testid: 'personal-access-token-field' }
|
||||
%p.invalid-feedback.js-import-github-pat-validation
|
||||
= _('Personal access token is required.')
|
||||
%span.form-text.gl-text-subtle
|
||||
- code_pair = tag_pair(tag.code, :code_start, :code_end)
|
||||
- github_link_tag_pair = tag_pair(link_to('', 'https://github.com/settings/tokens', target: '_blank', rel: 'noopener noreferrer'), :link_start, :link_end)
|
||||
= safe_format(s_('GithubImport|Create and provide your GitHub %{link_start}personal access token%{link_end}.'), github_link_tag_pair)
|
||||
%br
|
||||
= safe_format(s_('GithubImport|Use a classic GitHub personal access token with the following scopes:'))
|
||||
%ul
|
||||
- if has_ci_cd_only_params?
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}repo%{code_end}: Used to display a list of your public and private repositories that are available to connect to.'), code_pair)
|
||||
- else
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}repo%{code_end}: Used to display a list of your public and private repositories that are available to import from.'), code_pair)
|
||||
%li= safe_format(s_('GithubImporter|%{code_start}read:org%{code_end} (optional): Used to import collaborators from GitHub repositories, or if your project has Git LFS files.'), code_pair)
|
||||
- docs_link = link_to('', help_page_path('user/project/import/github.md', anchor: 'use-a-github-personal-access-token'), target: '_blank', rel: 'noopener noreferrer')
|
||||
- docs_link_tag_pair = tag_pair(docs_link, :link_start, :link_end)
|
||||
= safe_format(s_('GithubImport|%{link_start}Learn more%{link_end}.'), docs_link_tag_pair)
|
||||
|
||||
.gl-mt-5.gl-flex.gl-gap-3
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, type: :submit, button_options: { class: 'js-import-github-pat-authenticate', data: { testid: 'authenticate-button' } }) do
|
||||
= _('Authenticate')
|
||||
= render Pajamas::ButtonComponent.new(href: new_project_path) do
|
||||
= _('Cancel')
|
||||
.gl-mt-5.gl-flex.gl-gap-3
|
||||
= render Pajamas::ButtonComponent.new(variant: :confirm, type: :submit, button_options: { class: 'js-import-github-pat-authenticate', data: { testid: 'authenticate-button' } }) do
|
||||
= _('Authenticate')
|
||||
= render Pajamas::ButtonComponent.new(href: new_project_path) do
|
||||
= _('Cancel')
|
||||
|
|
|
|||
|
|
@ -13,3 +13,6 @@ tiers:
|
|||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
additional_properties:
|
||||
property:
|
||||
description: the name of the build
|
||||
|
|
|
|||
|
|
@ -43626,7 +43626,6 @@ Possible types of user.
|
|||
| <a id="usertypeadmin_bot"></a>`ADMIN_BOT` | Admin bot. |
|
||||
| <a id="usertypealert_bot"></a>`ALERT_BOT` | Alert bot. |
|
||||
| <a id="usertypeautomation_bot"></a>`AUTOMATION_BOT` | Automation bot. |
|
||||
| <a id="usertypeci_pipeline_bot"></a>`CI_PIPELINE_BOT` | Ci pipeline bot. |
|
||||
| <a id="usertypeduo_code_review_bot"></a>`DUO_CODE_REVIEW_BOT` | Duo code review bot. |
|
||||
| <a id="usertypeghost"></a>`GHOST` | Ghost. |
|
||||
| <a id="usertypehuman"></a>`HUMAN` | Human. |
|
||||
|
|
|
|||
|
|
@ -70,11 +70,13 @@ to be available in the RubyGems index. We want to minimize external build
|
|||
dependencies and build times. It's enforced by the RuboCop rule
|
||||
[`Cop/GemFetcher`](https://gitlab.com/gitlab-org/ruby/gems/gitlab-styles/-/blob/master/lib/rubocop/cop/gem_fetcher.rb).
|
||||
|
||||
## No gems for importers or integrations
|
||||
## No gems that make HTTP calls for importers or integrations
|
||||
|
||||
Do not add gems for use in importers or integrations. For more information, see:
|
||||
Do not add gems that make HTTP calls in importers or integrations.
|
||||
In general, other gems are also strongly discouraged from these domains.
|
||||
For more information, see:
|
||||
|
||||
- [Integration developer guide](integrations/_index.md#do-not-add-ruby-gems)
|
||||
- [Integration development guidelines](integrations/_index.md#no-ruby-gems-that-make-http-calls)
|
||||
- [Principles of importer design](import/principles_of_importer_design.md#security)
|
||||
|
||||
## Review the new dependency for quality
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ title: Principles of Importer Design
|
|||
- Uploaded files must be validated. Examples:
|
||||
- [`BulkImports::FileDownloadService`](https://gitlab.com/gitlab-org/gitlab/-/blob/cd4a880cbb2bc56b3a55f14c1d8370f4385319db/app/services/bulk_imports/file_download_service.rb#L38-46)
|
||||
- [`ImportExport::CommandLineUtil`](https://gitlab.com/gitlab-org/gitlab/blob/139690b3aeac69675119ce70f17f70bc1753de48/lib/gitlab/import_export/command_line_util.rb#L134)
|
||||
- Importers must not add third-party Ruby gems to our `Gemfile`.
|
||||
For more information, see the
|
||||
[reasons](../integrations/_index.md#do-not-add-ruby-gems) given for integrations.
|
||||
- Importers must not add third-party Ruby gems that make HTTP calls.
|
||||
Importers use the same
|
||||
[Ruby gem policy as for integrations](../integrations/_index.md#no-ruby-gems-that-make-http-calls), for more information about Ruby gem use for importers see that page.
|
||||
- All HTTP calls must use `Gitlab::HTTP`.
|
||||
`Gitlab::HTTP` ensures that [network settings](../../security/webhooks.md) of the instance
|
||||
are enforced and has other [security hardening](../../security/webhooks.md#enforce-dns-rebinding-attack-protection) measures.
|
||||
|
|
|
|||
|
|
@ -185,21 +185,6 @@ attribute :wiki_page_events, default: false
|
|||
If an event attribute for an existing integration changes to `true`,
|
||||
this requires a data migration to back-fill the attribute value for old records.
|
||||
|
||||
#### Do not add Ruby gems
|
||||
|
||||
GitLab integrations must not add Ruby gems to our `Gemfile`.
|
||||
Instead, any required HTTP clients or small abstractions must be implemented in GitLab.
|
||||
|
||||
Gems that wrap interactions with third-party services may look convenient at first glance,
|
||||
but they offer minimal benefit compared to the costs involved:
|
||||
|
||||
- They increase the potential surface area of security problems and the effort required to fix them.
|
||||
- Often these gems make HTTP calls on your behalf. As integrations can make HTTP calls to remote
|
||||
servers configured by users, it is critical that we
|
||||
[fully control the network calls](#all-http-calls-must-use-gitlabhttp).
|
||||
- There is a maintenance cost of managing gem upgrades.
|
||||
- They can block us from using newer features.
|
||||
|
||||
### Security requirements
|
||||
|
||||
#### All HTTP calls must use `Gitlab::HTTP`
|
||||
|
|
@ -225,6 +210,23 @@ def mask_configurable_channels?
|
|||
end
|
||||
```
|
||||
|
||||
## No Ruby gems that make HTTP calls
|
||||
|
||||
GitLab integrations must not add Ruby gems that make HTTP calls.
|
||||
Other gems that add small abstractions should also not be added.
|
||||
|
||||
Certain utility-like gems from official sources, like `atlassian-jwt` gem can be used if required.
|
||||
|
||||
Gems that wrap interactions with third-party services may look convenient at first glance,
|
||||
but they offer minimal benefit compared to the costs involved:
|
||||
|
||||
- They increase the potential surface area of security problems and the effort required to fix them.
|
||||
- Often these gems make HTTP calls on your behalf. As integrations can make HTTP calls to remote
|
||||
servers configured by users, it is critical that we
|
||||
[fully control the network calls](#all-http-calls-must-use-gitlabhttp).
|
||||
- There is a maintenance cost of managing gem upgrades.
|
||||
- They can block us from using newer features.
|
||||
|
||||
## Define configuration test
|
||||
|
||||
Optionally, you can define a configuration test of an integration's settings. The test is executed from the integration form's **Test** button, and results are returned to the user.
|
||||
|
|
|
|||
|
|
@ -85,14 +85,15 @@ module API
|
|||
agent = ::Clusters::AgentsFinder.new(user_project, current_user).execute.find_by_id(params[:cluster_agent_id])
|
||||
|
||||
bad_request!("cluster agent doesn't exist or cannot be associated with this environment") unless agent
|
||||
params[:cluster_agent] = agent
|
||||
end
|
||||
|
||||
environment = user_project.environments.create(params)
|
||||
response = ::Environments::CreateService.new(user_project, current_user, params).execute
|
||||
|
||||
if environment.persisted?
|
||||
present environment, with: Entities::Environment, current_user: current_user
|
||||
if response.success?
|
||||
present response.payload[:environment], with: Entities::Environment, current_user: current_user
|
||||
else
|
||||
render_validation_error!(environment)
|
||||
render_api_error!(response.message, 400)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def bot_user_can_read_project?(user, project)
|
||||
(user.project_bot? || user.ci_pipeline_bot? || user.service_account? || user.security_policy_bot?) && can_read_project?(user, project)
|
||||
(user.project_bot? || user.service_account? || user.security_policy_bot?) && can_read_project?(user, project)
|
||||
end
|
||||
|
||||
def valid_oauth_token?(token)
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ module Gitlab
|
|||
push_frontend_feature_flag(:work_items_view_preference, current_user)
|
||||
push_frontend_feature_flag(:search_button_top_right, current_user)
|
||||
push_frontend_feature_flag(:merge_request_dashboard, current_user, type: :wip)
|
||||
push_frontend_feature_flag(:new_project_creation_form, current_user, type: :wip)
|
||||
end
|
||||
|
||||
# Exposes the state of a feature flag to the frontend code.
|
||||
|
|
|
|||
|
|
@ -26668,6 +26668,15 @@ msgstr ""
|
|||
msgid "Gitea import"
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImporter|%{codeStart}read:org%{codeEnd} (optional): Used to import collaborators from GitHub repositories, or if your project has Git LFS files."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImporter|%{codeStart}repo%{codeEnd}: Used to display a list of your public and private repositories that are available to connect to."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImporter|%{codeStart}repo%{codeEnd}: Used to display a list of your public and private repositories that are available to import from."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImporter|%{code_start}read:org%{code_end} (optional): Used to import collaborators from GitHub repositories, or if your project has Git LFS files."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -26755,9 +26764,15 @@ msgstr ""
|
|||
msgid "GithubImport|\"%{repository_name}\" size (%{repository_size}) is larger than the limit of %{limit}."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImport|%{linkStart}Learn more%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImport|%{link_start}Learn more%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImport|Create and provide your GitHub %{linkStart}personal access token%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "GithubImport|Create and provide your GitHub %{link_start}personal access token%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -46236,6 +46251,12 @@ msgstr ""
|
|||
msgid "ProjectsNew|Analyze your source code for known security vulnerabilities."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNew|Authenticate through GitHub"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNew|Authenticate with GitHub"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectsNew|Available only for projects within groups"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -52853,7 +52874,7 @@ msgstr ""
|
|||
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
|
||||
msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project. Either link it to an existing project or create a new policy, which will create a new project that you can use as a security policy project. For help, see %{linkStart}policies%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityOrchestration|This applies to following compliance frameworks:"
|
||||
|
|
@ -62650,6 +62671,9 @@ msgstr ""
|
|||
msgid "Use one line per URI"
|
||||
msgstr ""
|
||||
|
||||
msgid "Use personal access token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Use primary email (%{email})"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -136,10 +136,6 @@ FactoryBot.define do
|
|||
user_type { :llm_bot }
|
||||
end
|
||||
|
||||
trait :ci_pipeline_bot do
|
||||
user_type { :ci_pipeline_bot }
|
||||
end
|
||||
|
||||
trait :duo_code_review_bot do
|
||||
user_type { :duo_code_review_bot }
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
import { GlAlert } from '@gitlab/ui';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import ImportFromGithubApp from '~/import/github/import_from_github_app.vue';
|
||||
import MultiStepFormTemplate from '~/vue_shared/components/multi_step_form_template.vue';
|
||||
|
||||
describe('Import from GitHub app', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (provide = {}) => {
|
||||
wrapper = shallowMountExtended(ImportFromGithubApp, {
|
||||
provide: {
|
||||
backButtonPath: 'https://gitlab.com',
|
||||
namespaceId: '1',
|
||||
messageAdmin: 'This is an admin alert.',
|
||||
isCiCdOnly: false,
|
||||
isConfigured: true,
|
||||
buttonAuthHref: 'https://gitlab.com/submit',
|
||||
formPath: 'https://gitlab.com/submit',
|
||||
...provide,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findMultiStepForm = () => wrapper.findComponent(MultiStepFormTemplate);
|
||||
const findGithubAuthButton = () => wrapper.findByTestId('github-auth-button');
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
|
||||
it('renders a form', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findMultiStepForm().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('not a ci/cd project', () => {
|
||||
it('renders a button if github is configured', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findGithubAuthButton().exists()).toBe(true);
|
||||
expect(findAlert().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders an alert if github is not configured', () => {
|
||||
createComponent({ isConfigured: false });
|
||||
|
||||
expect(findGithubAuthButton().exists()).toBe(false);
|
||||
expect(findAlert().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('is a ci/cd project', () => {
|
||||
it('does not render a github auth button or alert', () => {
|
||||
createComponent({ isCiCdOnly: true });
|
||||
|
||||
expect(findGithubAuthButton().exists()).toBe(false);
|
||||
expect(findAlert().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -158,14 +158,19 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
end
|
||||
|
||||
context 'when running after_commit callbacks' do
|
||||
it 'tracks creation event' do
|
||||
expect(Gitlab::InternalEvents).to receive(:track_event).with(
|
||||
'create_ci_build',
|
||||
project: project,
|
||||
user: user
|
||||
)
|
||||
let(:name) { 'test123' }
|
||||
|
||||
create(:ci_build, user: user, project: project)
|
||||
subject(:create_ci_build) { create(:ci_build, user: user, project: project, name: name) }
|
||||
|
||||
it 'tracks creation event' do
|
||||
expect { create_ci_build }
|
||||
.to trigger_internal_events('create_ci_build')
|
||||
.with(
|
||||
category: 'InternalEventTracking',
|
||||
user: user,
|
||||
project: project,
|
||||
additional_properties: { property: name }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -360,26 +365,6 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
|
|||
end
|
||||
end
|
||||
|
||||
describe '.license_management_jobs' do
|
||||
subject { described_class.license_management_jobs }
|
||||
|
||||
let!(:management_build) { create(:ci_build, :success, name: :license_management, pipeline: pipeline) }
|
||||
let!(:scanning_build) { create(:ci_build, :success, name: :license_scanning, pipeline: pipeline) }
|
||||
let!(:another_build) { create(:ci_build, :success, name: :another_type, pipeline: pipeline) }
|
||||
|
||||
it 'returns license_scanning jobs' do
|
||||
is_expected.to include(scanning_build)
|
||||
end
|
||||
|
||||
it 'returns license_management jobs' do
|
||||
is_expected.to include(management_build)
|
||||
end
|
||||
|
||||
it 'doesnt return filtered out jobs' do
|
||||
is_expected.not_to include(another_build)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.finished_before' do
|
||||
subject { described_class.finished_before(date) }
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ RSpec.describe User, feature_category: :system_access do
|
|||
expect(described_class::USER_TYPES.keys)
|
||||
.to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot
|
||||
visual_review_bot migration_bot automation_bot security_policy_bot admin_bot suggested_reviewers_bot
|
||||
service_account llm_bot placeholder duo_code_review_bot import_user ci_pipeline_bot])
|
||||
service_account llm_bot placeholder duo_code_review_bot import_user])
|
||||
expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES)
|
||||
expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES)
|
||||
expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES)
|
||||
|
|
|
|||
|
|
@ -4075,6 +4075,30 @@ RSpec.describe Repository, feature_category: :source_code_management do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when a Gitlab::Git::CommandTimedOut is raised' do
|
||||
before do
|
||||
expect(repository.raw_repository)
|
||||
.to receive(:get_patch_id).and_raise(Gitlab::Git::CommandTimedOut)
|
||||
end
|
||||
|
||||
it 'returns nil' do
|
||||
expect(repository.get_patch_id('HEAD', 'f' * 40)).to be_nil
|
||||
end
|
||||
|
||||
it 'reports the exception' do
|
||||
expect(Gitlab::ErrorTracking)
|
||||
.to receive(:track_exception)
|
||||
.with(
|
||||
instance_of(Gitlab::Git::CommandTimedOut),
|
||||
project_id: repository.project.id,
|
||||
old_revision: 'HEAD',
|
||||
new_revision: 'HEAD'
|
||||
)
|
||||
|
||||
repository.get_patch_id('HEAD', 'HEAD')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a Gitlab::Git::Repository::NoRepository is raised' do
|
||||
before do
|
||||
expect(repository.raw_repository)
|
||||
|
|
|
|||
|
|
@ -158,73 +158,86 @@ RSpec.describe API::Environments, feature_category: :continuous_delivery do
|
|||
let_it_be(:cluster_agent) { create(:cluster_agent, project: project) }
|
||||
let_it_be(:foreign_cluster_agent) { create(:cluster_agent) }
|
||||
|
||||
it 'creates an environment with associated cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id }
|
||||
context 'when user access authorization exists' do
|
||||
let_it_be(:user_access_auth) { create(:agent_user_access_project_authorization, project: project, agent: cluster_agent) }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
it 'creates an environment with associated cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
end
|
||||
|
||||
it 'creates an environment with associated kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id, kubernetes_namespace: 'flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
expect(json_response['kubernetes_namespace']).to eq('flux-system')
|
||||
end
|
||||
|
||||
it 'creates an environment with associated flux resource path' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id, kubernetes_namespace: 'flux-system', flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
expect(json_response['kubernetes_namespace']).to eq('flux-system')
|
||||
expect(json_response['flux_resource_path']).to eq('HelmRelease/flux-system')
|
||||
end
|
||||
|
||||
it 'fails to create environment with kubernetes namespace but no cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", kubernetes_namespace: 'flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(['Kubernetes namespace cannot be set without a cluster agent'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with flux resource path but no cluster agent and kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(['Flux resource path cannot be set without a kubernetes namespace'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with flux resource path but no cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", kubernetes_namespace: 'flux-system', flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(['Kubernetes namespace cannot be set without a cluster agent'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with cluster agent and flux resource path but no kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent: cluster_agent.id, flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq(['Flux resource path cannot be set without a kubernetes namespace'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with a non existing cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: non_existing_record_id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
|
||||
end
|
||||
|
||||
it 'fails to create environment with a foreign cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: foreign_cluster_agent.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
|
||||
end
|
||||
end
|
||||
|
||||
it 'creates an environment with associated kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id, kubernetes_namespace: 'flux-system' }
|
||||
context 'when user access authorization does not exist' do
|
||||
it 'fails to create environment with associated cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
expect(json_response['kubernetes_namespace']).to eq('flux-system')
|
||||
end
|
||||
|
||||
it 'creates an environment with associated flux resource path' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: cluster_agent.id, kubernetes_namespace: 'flux-system', flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
expect(response).to match_response_schema('public_api/v4/environment')
|
||||
expect(json_response['cluster_agent']).to be_present
|
||||
expect(json_response['kubernetes_namespace']).to eq('flux-system')
|
||||
expect(json_response['flux_resource_path']).to eq('HelmRelease/flux-system')
|
||||
end
|
||||
|
||||
it 'fails to create environment with kubernetes namespace but no cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", kubernetes_namespace: 'flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']['kubernetes_namespace']).to eq(['cannot be set without a cluster agent'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with flux resource path but no cluster agent and kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']['flux_resource_path']).to eq(['cannot be set without a kubernetes namespace'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with flux resource path but no cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", kubernetes_namespace: 'flux-system', flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']['kubernetes_namespace']).to eq(['cannot be set without a cluster agent'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with cluster agent and flux resource path but no kubernetes namespace' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent: cluster_agent.id, flux_resource_path: 'HelmRelease/flux-system' }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']['flux_resource_path']).to eq(['cannot be set without a kubernetes namespace'])
|
||||
end
|
||||
|
||||
it 'fails to create environment with a non existing cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: non_existing_record_id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
|
||||
end
|
||||
|
||||
it 'fails to create environment with a foreign cluster agent' do
|
||||
post api("/projects/#{project.id}/environments", user), params: { name: "mepmep", cluster_agent_id: foreign_cluster_agent.id }
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq("400 Bad request - cluster agent doesn't exist or cannot be associated with this environment")
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(json_response['message']).to eq('Unauthorized to access the cluster agent in this project')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1097,7 +1097,6 @@
|
|||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_deployment_approvals_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/historical_max_users_metrics_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/licensee_metrics_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/license_management_jobs_metric_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/license_metric_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environment_approval_rules_required_approvals_average_metric_spec.rb'
|
||||
- './ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environments_required_approvals_average_metric_spec.rb'
|
||||
|
|
|
|||
Loading…
Reference in New Issue